diff --git a/packages/api/src/features/agent/context.test.ts b/packages/api/src/features/agent/context.test.ts index d3a127b67..7237d4ff0 100644 --- a/packages/api/src/features/agent/context.test.ts +++ b/packages/api/src/features/agent/context.test.ts @@ -4,11 +4,11 @@ import { estimateTokens, pruneAgentModelContext } from "./context"; const BIG_RESUME = { basics: { name: "Alice" }, sections: { summary: { content: "x".repeat(2_000) } } }; -function readResumeExchange(callId: string): ModelMessage[] { +function readResumeExchange(callId: string, toolName = "read_resume"): ModelMessage[] { return [ { role: "assistant", - content: [{ type: "tool-call", toolCallId: callId, toolName: "read_resume", input: {} }], + content: [{ type: "tool-call", toolCallId: callId, toolName, input: {} }], }, { role: "tool", @@ -16,7 +16,7 @@ function readResumeExchange(callId: string): ModelMessage[] { { type: "tool-result", toolCallId: callId, - toolName: "read_resume", + toolName, output: { type: "json", value: { id: "resume-1", data: BIG_RESUME } }, }, ], @@ -64,10 +64,23 @@ describe("pruneAgentModelContext — tier 0 (snapshot supersession)", () => { const pruned = pruneAgentModelContext(messages, 1_000_000); expect(snapshotValue(pruned[2])).not.toHaveProperty("data"); - expect(snapshotValue(pruned[2]).note).toContain("Superseded resume snapshot"); + expect(snapshotValue(pruned[2]).note).toContain("Superseded document snapshot"); expect(snapshotValue(pruned[4])).toHaveProperty("resume"); }); + it("keeps only the last cover letter snapshot too", () => { + const messages = [ + user("hi"), + ...readResumeExchange("call-1", "read_letter"), + ...readResumeExchange("call-2", "read_letter"), + ]; + + const pruned = pruneAgentModelContext(messages, 1_000_000); + + expect(snapshotValue(pruned[2])).not.toHaveProperty("data"); + expect(snapshotValue(pruned[4])).toHaveProperty("data"); + }); + it("returns the same array reference when there is at most one snapshot", () => { const messages = [user("hi"), ...readResumeExchange("call-1")]; diff --git a/packages/api/src/features/agent/context.ts b/packages/api/src/features/agent/context.ts index 5eea8e108..145e4f3bb 100644 --- a/packages/api/src/features/agent/context.ts +++ b/packages/api/src/features/agent/context.ts @@ -8,9 +8,9 @@ import { estimateTokenCount as estimateTextTokenCount } from "tokenx"; const AGENT_CONTEXT_TOKEN_BUDGET = 40_000; -const SNAPSHOT_TOOL_NAMES = new Set(["read_resume", "apply_resume_patch"]); +const SNAPSHOT_TOOL_NAMES = new Set(["read_resume", "read_letter", "apply_resume_patch"]); const SUPERSEDED_SNAPSHOT_NOTE = - "Superseded resume snapshot removed. Base further edits on the resume state in the latest read_resume or apply_resume_patch result."; + "Superseded document snapshot removed. Base further edits on the latest read_resume, read_letter or apply_resume_patch result."; const PRUNED_TOOL_RESULT_NOTE = "Older tool result pruned to fit the context budget."; type LoosePart = Record & { type: string }; @@ -69,7 +69,7 @@ function isSnapshotResultPart(part: LoosePart): boolean { const value = unwrapToolOutput(part.output); if (!value || typeof value !== "object") return false; - return part.toolName === "read_resume" ? "data" in value : "resume" in value; + return part.toolName === "apply_resume_patch" ? "resume" in value : "data" in value; } function supersedeSnapshotPart(part: LoosePart): LoosePart {