diff --git a/extensions/codex/src/app-server/run-attempt.test.ts b/extensions/codex/src/app-server/run-attempt.test.ts index d7aa4690d8b..de4dc89d135 100644 --- a/extensions/codex/src/app-server/run-attempt.test.ts +++ b/extensions/codex/src/app-server/run-attempt.test.ts @@ -1871,6 +1871,80 @@ describe("runCodexAppServerAttempt", () => { ); }); + it("does not release commentary agent message items", async () => { + let notify: (notification: CodexServerNotification) => Promise = async () => undefined; + const request = vi.fn(async (method: string) => { + if (method === "thread/start") { + return threadStartResult("thread-1"); + } + if (method === "turn/start") { + return turnStartResult("turn-1", "inProgress"); + } + return {}; + }); + __testing.setCodexAppServerClientFactoryForTests( + async () => + ({ + request, + addNotificationHandler: (handler: typeof notify) => { + notify = handler; + return () => undefined; + }, + addRequestHandler: () => () => undefined, + }) as never, + ); + const params = createParams( + path.join(tempDir, "session.jsonl"), + path.join(tempDir, "workspace"), + ); + params.timeoutMs = 200; + + const run = runCodexAppServerAttempt(params, { + turnAssistantCompletionIdleTimeoutMs: 5, + }); + await vi.waitFor( + () => + expect(request).toHaveBeenCalledWith("turn/start", expect.anything(), expect.anything()), + { interval: 1 }, + ); + await notify({ + method: "item/completed", + params: { + threadId: "thread-1", + turnId: "turn-1", + item: { + type: "agentMessage", + id: "msg-commentary-1", + phase: "commentary", + text: "I am checking the workspace.", + }, + }, + }); + await new Promise((resolve) => setTimeout(resolve, 20)); + + expect(request).not.toHaveBeenCalledWith("turn/interrupt", expect.anything()); + await notify({ + method: "item/completed", + params: { + threadId: "thread-1", + turnId: "turn-1", + item: { + type: "agentMessage", + id: "msg-final-1", + phase: "final_answer", + text: "Done.", + }, + }, + }); + + await expect(run).resolves.toMatchObject({ + aborted: false, + timedOut: false, + promptError: null, + assistantTexts: ["Done."], + }); + }); + it("does not release the session after only a raw assistant response item", async () => { let notify: (notification: CodexServerNotification) => Promise = async () => undefined; const request = vi.fn(async (method: string) => { diff --git a/extensions/codex/src/app-server/run-attempt.ts b/extensions/codex/src/app-server/run-attempt.ts index 64de5a185a3..9ad02c5e04e 100644 --- a/extensions/codex/src/app-server/run-attempt.ts +++ b/extensions/codex/src/app-server/run-attempt.ts @@ -2638,7 +2638,11 @@ function isCompletedAssistantNotification(notification: CodexServerNotification) return false; } const item = isJsonObject(notification.params.item) ? notification.params.item : undefined; - return Boolean(item && readString(item, "type") === "agentMessage"); + return Boolean( + item && + readString(item, "type") === "agentMessage" && + readString(item, "phase") !== "commentary", + ); } function shouldDisarmAssistantCompletionIdleWatch(notification: CodexServerNotification): boolean {