diff --git a/src/core/condense/__tests__/condense.spec.ts b/src/core/condense/__tests__/condense.spec.ts index c209fa9724..45a725d29f 100644 --- a/src/core/condense/__tests__/condense.spec.ts +++ b/src/core/condense/__tests__/condense.spec.ts @@ -186,6 +186,47 @@ Line 2 } }) + it("should not send messages hidden by sliding-window truncation to the summarizer", async () => { + const truncationId = "trunc-1" + const messages: ApiMessage[] = [ + { role: "user", content: "Hidden first", truncationParent: truncationId }, + { role: "assistant", content: "Hidden second", truncationParent: truncationId }, + { role: "assistant", content: "Truncation marker", isTruncationMarker: true, truncationId }, + { role: "user", content: "Visible third" }, + { role: "assistant", content: "Visible fourth" }, + { role: "user", content: "Visible fifth" }, + ] + const original = structuredClone(messages) + const sentMessages: unknown[] = [] + const createMessageSpy = vi + .spyOn(mockApiHandler, "createMessage") + .mockImplementation((...args: unknown[]) => { + sentMessages.push(args[1]) + return (async function* () { + yield { type: "text", text: "Mock summary" } + })() + }) + + const result = await summarizeConversation({ + messages, + apiHandler: mockApiHandler, + systemPrompt: "System prompt", + taskId, + isAutomaticTrigger: false, + }) + + const sentText = JSON.stringify(sentMessages[0]) + expect(sentText).not.toContain("Hidden first") + expect(sentText).not.toContain("Hidden second") + expect(sentText).toContain("Visible third") + + // Complete stored history is preserved (non-destructive) and the input is not mutated + expect(result.messages.filter((msg) => !msg.isSummary)).toHaveLength(messages.length) + expect(messages).toEqual(original) + + createMessageSpy.mockRestore() + }) + it("should preserve blocks in the summary", async () => { const messages: ApiMessage[] = [ { diff --git a/src/core/condense/index.ts b/src/core/condense/index.ts index 0438bf6bcb..920f047d44 100644 --- a/src/core/condense/index.ts +++ b/src/core/condense/index.ts @@ -275,8 +275,10 @@ export async function summarizeConversation(options: SummarizeConversationOption const response: SummarizeResponse = { messages, cost: 0, summary: "" } - // Get messages to summarize (all messages since the last summary, if any) - const messagesToSummarize = getMessagesSinceLastSummary(messages) + // Summarize the effective history (what normal requests send): excludes messages already hidden + // by sliding-window truncation or a previous condense, and starts at the last summary if any. + // The stored history in `messages` is left intact and tagged below. + const messagesToSummarize = getEffectiveApiHistory(messages) if (messagesToSummarize.length <= 1) { const error =