Skip to content

Commit 4b9b8ce

Browse files
committed
fix(codex): keep pending compact starts pending
1 parent a26f2b9 commit 4b9b8ce

4 files changed

Lines changed: 91 additions & 6 deletions

File tree

extensions/codex/src/app-server/compact.test.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ describe("maybeCompactCodexAppServerSession", () => {
111111
expect(fake.request).toHaveBeenCalledWith("thread/compact/start", { threadId: "thread-1" });
112112
expect(fake.client.addNotificationHandler).not.toHaveBeenCalled();
113113
expect(result.ok).toBe(true);
114-
expect(result.compacted).toBe(true);
114+
expect(result.compacted).toBe(false);
115115
expect(result.result?.tokensBefore).toBe(123);
116116
expect(result.result?.tokensAfter).toBeUndefined();
117117
const details = compactDetails(result);
@@ -176,7 +176,7 @@ describe("maybeCompactCodexAppServerSession", () => {
176176
});
177177

178178
expect(result.ok).toBe(true);
179-
expect(result.compacted).toBe(true);
179+
expect(result.compacted).toBe(false);
180180
expect(result.result?.tokensAfter).toBeUndefined();
181181
expect(compactDetails(result).tokenUsageSource).toBeUndefined();
182182
expect(compactDetails(result).signal).toBe("thread/compact/start");
@@ -241,7 +241,7 @@ describe("maybeCompactCodexAppServerSession", () => {
241241
);
242242

243243
expect(result.ok).toBe(true);
244-
expect(result.compacted).toBe(true);
244+
expect(result.compacted).toBe(false);
245245
expect(compactDetails(result)).toMatchObject({
246246
backend: "codex-app-server",
247247
threadId: "thread-1",
@@ -551,7 +551,7 @@ describe("maybeCompactCodexAppServerSession", () => {
551551

552552
expect(fake.request).toHaveBeenCalledWith("thread/compact/start", { threadId: "thread-1" });
553553
expect(result.ok).toBe(true);
554-
expect(result.compacted).toBe(true);
554+
expect(result.compacted).toBe(false);
555555
expect(compactDetails(result)).toMatchObject({
556556
backend: "codex-app-server",
557557
threadId: "thread-1",

extensions/codex/src/app-server/compact.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -197,7 +197,7 @@ async function compactCodexNativeThread(
197197
};
198198
return {
199199
ok: true,
200-
compacted: true,
200+
compacted: false,
201201
result: {
202202
summary: "",
203203
firstKeptEntryId: "",

src/auto-reply/reply/commands-compact.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -350,7 +350,7 @@ describe("handleCompactCommand", () => {
350350
it("reports started Codex native compaction without incrementing completed compaction state", async () => {
351351
vi.mocked(compactEmbeddedPiSession).mockResolvedValueOnce({
352352
ok: true,
353-
compacted: true,
353+
compacted: false,
354354
result: {
355355
summary: "",
356356
firstKeptEntryId: "",

src/gateway/server.sessions.compaction.test.ts

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -391,6 +391,91 @@ test("sessions.compact without maxLines runs embedded manual compaction for chec
391391
ws.close();
392392
});
393393

394+
test("sessions.compact treats Codex native compaction start as pending, not completed", async () => {
395+
const { dir, storePath } = await createSessionStoreDir();
396+
await fs.writeFile(
397+
path.join(dir, "sess-codex.jsonl"),
398+
`${JSON.stringify({ role: "user", content: "hello codex" })}\n`,
399+
"utf-8",
400+
);
401+
await writeSessionStore({
402+
entries: {
403+
main: sessionStoreEntry("sess-codex", {
404+
agentHarnessId: "codex",
405+
compactionCount: 2,
406+
totalTokens: 54_321,
407+
totalTokensFresh: true,
408+
}),
409+
},
410+
});
411+
embeddedRunMock.compactEmbeddedPiSession.mockResolvedValueOnce({
412+
ok: true,
413+
compacted: false,
414+
result: {
415+
summary: "",
416+
firstKeptEntryId: "",
417+
tokensBefore: 54_321,
418+
details: {
419+
backend: "codex-app-server",
420+
threadId: "thread-1",
421+
signal: "thread/compact/start",
422+
pending: true,
423+
},
424+
},
425+
});
426+
427+
const { ws } = await openClient();
428+
await rpcReq(ws, "sessions.subscribe", {});
429+
const endEventPromise = onceMessage(
430+
ws,
431+
(message) =>
432+
message.type === "event" &&
433+
message.event === "session.operation" &&
434+
(message.payload as { operation?: unknown; phase?: unknown })?.operation === "compact" &&
435+
(message.payload as { operation?: unknown; phase?: unknown })?.phase === "end",
436+
);
437+
438+
const compacted = await rpcReq<{
439+
ok: true;
440+
key: string;
441+
compacted: boolean;
442+
result?: { details?: unknown };
443+
}>(ws, "sessions.compact", {
444+
key: "main",
445+
});
446+
447+
expect(compacted.ok).toBe(true);
448+
expect(compacted.payload?.key).toBe("agent:main:main");
449+
expect(compacted.payload?.compacted).toBe(false);
450+
expect(compacted.payload?.result?.details).toMatchObject({
451+
backend: "codex-app-server",
452+
threadId: "thread-1",
453+
signal: "thread/compact/start",
454+
pending: true,
455+
});
456+
const endEvent = await endEventPromise;
457+
expect(endEvent.payload).toMatchObject({
458+
operation: "compact",
459+
phase: "end",
460+
sessionKey: "agent:main:main",
461+
completed: false,
462+
});
463+
464+
const store = JSON.parse(await fs.readFile(storePath, "utf-8")) as Record<
465+
string,
466+
{
467+
compactionCount?: number;
468+
totalTokens?: number;
469+
totalTokensFresh?: boolean;
470+
}
471+
>;
472+
expect(store["agent:main:main"]?.compactionCount).toBe(2);
473+
expect(store["agent:main:main"]?.totalTokens).toBe(54_321);
474+
expect(store["agent:main:main"]?.totalTokensFresh).toBe(true);
475+
476+
ws.close();
477+
});
478+
394479
test("sessions.patch preserves nested model ids under provider overrides", async () => {
395480
const dir = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-gw-sessions-nested-"));
396481
const storePath = path.join(dir, "sessions.json");

0 commit comments

Comments
 (0)