diff --git a/packages/agent/src/adapters/claude/session/jsonl-hydration.test.ts b/packages/agent/src/adapters/claude/session/jsonl-hydration.test.ts index bd7aada3bf..bb6e76ad36 100644 --- a/packages/agent/src/adapters/claude/session/jsonl-hydration.test.ts +++ b/packages/agent/src/adapters/claude/session/jsonl-hydration.test.ts @@ -496,6 +496,29 @@ describe("conversationTurnsToJsonlEntries", () => { expect(conv[1].parentUuid).toBe(conv[0].uuid); }); + it("seeds only the last assistant message with the estimated context size", () => { + const lines = conversationTurnsToJsonlEntries( + [ + { role: "user", content: [{ type: "text", text: "q".repeat(400) }] }, + { + role: "assistant", + content: [{ type: "text", text: "a".repeat(400) }], + }, + { role: "user", content: [{ type: "text", text: "more" }] }, + { role: "assistant", content: [{ type: "text", text: "final" }] }, + ], + config, + ); + + const assistants = parseConversationEntries(lines).filter( + (entry) => entry.type === "assistant", + ); + expect(assistants.at(-1)?.message.usage.input_tokens).toBeGreaterThan(0); + for (const entry of assistants.slice(0, -1)) { + expect(entry.message.usage.input_tokens).toBe(0); + } + }); + it("emits one line per assistant block with shared message id", () => { const lines = conversationTurnsToJsonlEntries( [ diff --git a/packages/agent/src/adapters/claude/session/jsonl-hydration.ts b/packages/agent/src/adapters/claude/session/jsonl-hydration.ts index 9bc9c268b1..f865791f8c 100644 --- a/packages/agent/src/adapters/claude/session/jsonl-hydration.ts +++ b/packages/agent/src/adapters/claude/session/jsonl-hydration.ts @@ -421,6 +421,13 @@ export function conversationTurnsToJsonlEntries( const permissionMode = config.permissionMode ?? "default"; const baseTime = Date.now() - turns.length * 3000; let turnIndex = 0; + // Seed the last assistant message with the estimated context size so the + // SDK's auto-compact accounting isn't blind until the first API response. + const estimatedTokens = turns.reduce( + (sum, turn) => sum + estimateTurnTokens(turn), + 0, + ); + const lastAssistantTurn = turns.findLast((turn) => turn.role === "assistant"); for (const turn of turns) { const timestamp = new Date(baseTime + turnIndex * 3000).toISOString(); @@ -530,7 +537,8 @@ export function conversationTurnsToJsonlEntries( stop_reason: isLast ? lastStopReason : null, stop_sequence: null, usage: { - input_tokens: 0, + input_tokens: + turn === lastAssistantTurn && isLast ? estimatedTokens : 0, cache_creation_input_tokens: 0, cache_read_input_tokens: 0, output_tokens: 0,