diff --git a/packages/agent/src/adapters/codex/codex-agent.test.ts b/packages/agent/src/adapters/codex/codex-agent.test.ts index 38240e4cb..9f4bf5c2d 100644 --- a/packages/agent/src/adapters/codex/codex-agent.test.ts +++ b/packages/agent/src/adapters/codex/codex-agent.test.ts @@ -107,6 +107,48 @@ describe("CodexAcpAgent", () => { ).toBe("read-only"); }); + it("propagates taskRunId and fires SDK_SESSION when loading a cloud session", async () => { + const { agent, client } = createAgent(); + mockCodexConnection.loadSession.mockResolvedValue({ + modes: { currentModeId: "auto", availableModes: [] }, + configOptions: [], + } satisfies Partial); + + await agent.loadSession({ + sessionId: "session-1", + cwd: process.cwd(), + _meta: { taskRunId: "run-1", taskId: "task-1" }, + } as never); + + expect( + (agent as unknown as { sessionState: { taskRunId?: string } }) + .sessionState.taskRunId, + ).toBe("run-1"); + expect(client.extNotification).toHaveBeenCalledWith( + "_posthog/sdk_session", + { + taskRunId: "run-1", + sessionId: "session-1", + adapter: "codex", + }, + ); + }); + + it("does not emit SDK_SESSION on loadSession when taskRunId is absent", async () => { + const { agent, client } = createAgent(); + mockCodexConnection.loadSession.mockResolvedValue({ + modes: { currentModeId: "auto", availableModes: [] }, + configOptions: [], + } satisfies Partial); + + await agent.loadSession({ + sessionId: "session-1", + cwd: process.cwd(), + } as never); + + expect(client.extNotification).not.toHaveBeenCalled(); + }); + it("preserves the live session mode when loading an existing session", async () => { const { agent } = createAgent(); mockCodexConnection.loadSession.mockResolvedValue({ diff --git a/packages/agent/src/adapters/codex/codex-agent.ts b/packages/agent/src/adapters/codex/codex-agent.ts index 4dda791eb..c975fa7af 100644 --- a/packages/agent/src/adapters/codex/codex-agent.ts +++ b/packages/agent/src/adapters/codex/codex-agent.ts @@ -267,13 +267,27 @@ export class CodexAcpAgent extends BaseAcpAgent { meta?.permissionMode, ); + // Carry taskRunId/taskId across load so prompt() still emits cloud + // notifications (TURN_COMPLETE, USAGE_UPDATE) after a reload. newSession + // and unstable_resumeSession both do this; loadSession historically did + // not, which silently broke task-completion tracking on re-attach. this.sessionState = createSessionState(params.sessionId, params.cwd, { + taskRunId: meta?.taskRunId, + taskId: meta?.taskId ?? meta?.persistence?.taskId, modeId: response.modes?.currentModeId ?? "auto", permissionMode: currentPermissionMode, }); this.sessionId = params.sessionId; this.sessionState.configOptions = response.configOptions ?? []; + if (meta?.taskRunId) { + await this.client.extNotification(POSTHOG_NOTIFICATIONS.SDK_SESSION, { + taskRunId: meta.taskRunId, + sessionId: params.sessionId, + adapter: "codex", + }); + } + return response; }