diff --git a/packages/server/src/server/agent/agent-manager.test.ts b/packages/server/src/server/agent/agent-manager.test.ts index 1ff1b1933..d55f1c598 100644 --- a/packages/server/src/server/agent/agent-manager.test.ts +++ b/packages/server/src/server/agent/agent-manager.test.ts @@ -4757,7 +4757,7 @@ test("replaceAgentRun stays running when a stale old terminal arrives before the unsubscribe(); }); -test("applies live autonomous events while no foreground run is active", async () => { +test("applies live autonomous events and preserves usage omitted from completion", async () => { const workdir = mkdtempSync(join(tmpdir(), "agent-manager-live-events-")); const storagePath = join(workdir, "agents"); const storage = new AgentStorage(storagePath, logger); @@ -4816,6 +4816,16 @@ test("applies live autonomous events while no foreground run is active", async ( provider: "codex", turnId: autonomousTurnId, }); + capturedSession!.pushEvent({ + type: "usage_updated", + provider: "codex", + usage: { + inputTokens: 10, + contextWindowMaxTokens: 200_000, + contextWindowUsedTokens: 175, + }, + turnId: autonomousTurnId, + }); capturedSession!.pushEvent({ type: "timeline", provider: "codex", @@ -4831,6 +4841,11 @@ test("applies live autonomous events while no foreground run is active", async ( const updated = manager.getAgent(snapshot.id); expect(updated?.lifecycle).toBe("idle"); + expect(updated?.lastUsage).toEqual({ + inputTokens: 10, + contextWindowMaxTokens: 200_000, + contextWindowUsedTokens: 175, + }); expect(manager.getTimeline(snapshot.id)).toContainEqual({ type: "assistant_message", text: "AUTONOMOUS_PUMP_MESSAGE", diff --git a/packages/server/src/server/agent/agent-manager.ts b/packages/server/src/server/agent/agent-manager.ts index e6c4e68a8..c18e8c902 100644 --- a/packages/server/src/server/agent/agent-manager.ts +++ b/packages/server/src/server/agent/agent-manager.ts @@ -3552,7 +3552,12 @@ export class AgentManager { }, "agent.manager.turn.completed", ); - agent.lastUsage = event.usage; + if (event.usage) { + agent.lastUsage = { ...agent.lastUsage, ...event.usage }; + } + // If no usage on turn_completed, keep lastUsage as-is so context window + // data accumulated during streaming isn't lost when the provider omits + // it from the completion event. agent.lastError = undefined; if (!isForegroundEvent && agent.lifecycle !== "idle" && !agent.pendingReplacement) { (agent as ActiveManagedAgent).lifecycle = "idle";