From 3e24e21c3d5dae9a9737f6822d8a6b7762c2d326 Mon Sep 17 00:00:00 2001 From: SannidhyaSah Date: Mon, 16 Feb 2026 22:51:52 +0530 Subject: [PATCH] fix: preserve condensation summary during task resume (#11487) (#11488) Co-authored-by: Sannidhya --- .../__tests__/rewind-after-condense.spec.ts | 52 +++++++++++++++++++ src/core/task/Task.ts | 12 ++++- 2 files changed, 63 insertions(+), 1 deletion(-) diff --git a/src/core/condense/__tests__/rewind-after-condense.spec.ts b/src/core/condense/__tests__/rewind-after-condense.spec.ts index 068f49a857..586e7f5f88 100644 --- a/src/core/condense/__tests__/rewind-after-condense.spec.ts +++ b/src/core/condense/__tests__/rewind-after-condense.spec.ts @@ -518,4 +518,56 @@ describe("Rewind After Condense - Issue #8295", () => { }) }) }) + + describe("Resume after condense preserves summary", () => { + it("should keep condensed messages filtered when a new user message is added after summary", () => { + const condenseId = "summary-resume-test" + + // Simulate post-condensation state: all old messages tagged, summary at end, + // then a new user message added after resume (the fix preserves summary) + const historyAfterResume: ApiMessage[] = [ + { role: "user", content: "Original task", ts: 100, condenseParent: condenseId }, + { role: "assistant", content: "Response 1", ts: 200, condenseParent: condenseId }, + { role: "user", content: "Follow-up", ts: 300, condenseParent: condenseId }, + { role: "assistant", content: "Response 2", ts: 400, condenseParent: condenseId }, + { + role: "user", + content: [{ type: "text", text: "## Conversation Summary\nSummary of work done" }], + ts: 401, + isSummary: true, + condenseId, + }, + // New user message added after resume (no isSummary, no condenseId) + { role: "user", content: [{ type: "text", text: "Please continue with the next step" }], ts: 500 }, + ] + + const effective = getEffectiveApiHistory(historyAfterResume) + + // Should only include summary + new message (fresh start model) + expect(effective).toHaveLength(2) + expect(effective[0].isSummary).toBe(true) + expect(effective[1].ts).toBe(500) + }) + + it("should restore ALL messages if summary is missing (the bug scenario before fix)", () => { + const condenseId = "summary-bug-demo" + + // Simulate the bug: summary was stripped during resume, replaced with regular message. + // condenseParent tags still exist but point to a non-existent summary. + const historyWithoutSummary: ApiMessage[] = [ + { role: "user", content: "Original task", ts: 100, condenseParent: condenseId }, + { role: "assistant", content: "Response 1", ts: 200, condenseParent: condenseId }, + { role: "user", content: "Follow-up", ts: 300, condenseParent: condenseId }, + { role: "assistant", content: "Response 2", ts: 400, condenseParent: condenseId }, + // Summary was REMOVED and replaced with a regular user message (no isSummary) + { role: "user", content: [{ type: "text", text: "Summary content merged with new input" }], ts: 500 }, + ] + + const effective = getEffectiveApiHistory(historyWithoutSummary) + + // Without the summary, ALL messages are restored (orphaned condenseParent) + // This demonstrates the bug: condensation is effectively undone + expect(effective).toHaveLength(5) + }) + }) }) diff --git a/src/core/task/Task.ts b/src/core/task/Task.ts index 3feb695e10..6ba57e98ac 100644 --- a/src/core/task/Task.ts +++ b/src/core/task/Task.ts @@ -2099,7 +2099,17 @@ export class Task extends EventEmitter implements TaskLike { if (existingApiConversationHistory.length > 0) { const lastMessage = existingApiConversationHistory[existingApiConversationHistory.length - 1] - if (lastMessage.role === "assistant") { + if (lastMessage.isSummary) { + // IMPORTANT: If the last message is a condensation summary, we must preserve it + // intact. The summary message carries critical metadata (isSummary, condenseId) + // that getEffectiveApiHistory() uses to filter out condensed messages. + // Removing or merging it would destroy this metadata, causing all condensed + // messages to become "orphaned" and restored to active status — effectively + // undoing the condensation and sending the full history to the API. + // See: https://github.com/RooCodeInc/Roo-Code/issues/11487 + modifiedApiConversationHistory = [...existingApiConversationHistory] + modifiedOldUserContent = [] + } else if (lastMessage.role === "assistant") { const content = Array.isArray(lastMessage.content) ? lastMessage.content : [{ type: "text", text: lastMessage.content }]