fix: preserve context window data when turn_completed lacks usage (#2494)
* fix: preserve context window data when turn_completed lacks usage Merge incoming usage into agent.lastUsage instead of replacing it wholesale. Some providers (Claude autonomous turns, OpenCode) emit turn_completed without a usage payload, which previously wiped the contextWindowMaxTokens/contextWindowUsedTokens accumulated during streaming usage_updated events. The result was the context window meter disappearing from the composer once the agent finished work. * test(server): preserve usage after autonomous completion --------- Co-authored-by: paseo-bot[bot] <266920839+paseo-bot[bot]@users.noreply.github.com>
This commit is contained in:
co-authored by
paseo-bot[bot] <266920839+paseo-bot[bot]@users.noreply.github.com>
parent
acb8506d1f
commit
b897c59e9c
@@ -4757,7 +4757,7 @@ test("replaceAgentRun stays running when a stale old terminal arrives before the
|
|||||||
unsubscribe();
|
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 workdir = mkdtempSync(join(tmpdir(), "agent-manager-live-events-"));
|
||||||
const storagePath = join(workdir, "agents");
|
const storagePath = join(workdir, "agents");
|
||||||
const storage = new AgentStorage(storagePath, logger);
|
const storage = new AgentStorage(storagePath, logger);
|
||||||
@@ -4816,6 +4816,16 @@ test("applies live autonomous events while no foreground run is active", async (
|
|||||||
provider: "codex",
|
provider: "codex",
|
||||||
turnId: autonomousTurnId,
|
turnId: autonomousTurnId,
|
||||||
});
|
});
|
||||||
|
capturedSession!.pushEvent({
|
||||||
|
type: "usage_updated",
|
||||||
|
provider: "codex",
|
||||||
|
usage: {
|
||||||
|
inputTokens: 10,
|
||||||
|
contextWindowMaxTokens: 200_000,
|
||||||
|
contextWindowUsedTokens: 175,
|
||||||
|
},
|
||||||
|
turnId: autonomousTurnId,
|
||||||
|
});
|
||||||
capturedSession!.pushEvent({
|
capturedSession!.pushEvent({
|
||||||
type: "timeline",
|
type: "timeline",
|
||||||
provider: "codex",
|
provider: "codex",
|
||||||
@@ -4831,6 +4841,11 @@ test("applies live autonomous events while no foreground run is active", async (
|
|||||||
|
|
||||||
const updated = manager.getAgent(snapshot.id);
|
const updated = manager.getAgent(snapshot.id);
|
||||||
expect(updated?.lifecycle).toBe("idle");
|
expect(updated?.lifecycle).toBe("idle");
|
||||||
|
expect(updated?.lastUsage).toEqual({
|
||||||
|
inputTokens: 10,
|
||||||
|
contextWindowMaxTokens: 200_000,
|
||||||
|
contextWindowUsedTokens: 175,
|
||||||
|
});
|
||||||
expect(manager.getTimeline(snapshot.id)).toContainEqual({
|
expect(manager.getTimeline(snapshot.id)).toContainEqual({
|
||||||
type: "assistant_message",
|
type: "assistant_message",
|
||||||
text: "AUTONOMOUS_PUMP_MESSAGE",
|
text: "AUTONOMOUS_PUMP_MESSAGE",
|
||||||
|
|||||||
@@ -3552,7 +3552,12 @@ export class AgentManager {
|
|||||||
},
|
},
|
||||||
"agent.manager.turn.completed",
|
"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;
|
agent.lastError = undefined;
|
||||||
if (!isForegroundEvent && agent.lifecycle !== "idle" && !agent.pendingReplacement) {
|
if (!isForegroundEvent && agent.lifecycle !== "idle" && !agent.pendingReplacement) {
|
||||||
(agent as ActiveManagedAgent).lifecycle = "idle";
|
(agent as ActiveManagedAgent).lifecycle = "idle";
|
||||||
|
|||||||
Reference in New Issue
Block a user