From 9216b885f394fbc7dce6de67331d9e12dd274c66 Mon Sep 17 00:00:00 2001 From: Rodrigo Uroz Date: Fri, 27 Feb 2026 20:04:54 +0000 Subject: [PATCH] Compaction/Safeguard: avoid empty-history fallback text --- .../compaction-safeguard.test.ts | 14 +++++ .../pi-extensions/compaction-safeguard.ts | 51 ++++++++++++------- 2 files changed, 48 insertions(+), 17 deletions(-) diff --git a/src/agents/pi-extensions/compaction-safeguard.test.ts b/src/agents/pi-extensions/compaction-safeguard.test.ts index e5953a9c8c9..4053547c783 100644 --- a/src/agents/pi-extensions/compaction-safeguard.test.ts +++ b/src/agents/pi-extensions/compaction-safeguard.test.ts @@ -17,6 +17,7 @@ const { formatToolFailuresSection, splitPreservedRecentTurns, formatPreservedTurnsSection, + appendSummarySection, resolveRecentTurnsPreserve, computeAdaptiveChunkRatio, isOversizedForSummary, @@ -621,6 +622,19 @@ describe("compaction-safeguard recent-turn preservation", () => { expect(formatPreservedTurnsSection(split.preservedMessages)).not.toContain("assistant-2"); }); + it("trim-starts preserved section when history summary is empty", () => { + const summary = appendSummarySection( + "", + "\n\n## Recent turns preserved verbatim\n- User: hello", + ); + expect(summary.startsWith("## Recent turns preserved verbatim")).toBe(true); + }); + + it("does not append empty summary sections", () => { + expect(appendSummarySection("History", "")).toBe("History"); + expect(appendSummarySection("", "")).toBe(""); + }); + it("clamps preserve count into a safe range", () => { expect(resolveRecentTurnsPreserve(undefined)).toBe(3); expect(resolveRecentTurnsPreserve(-1)).toBe(0); diff --git a/src/agents/pi-extensions/compaction-safeguard.ts b/src/agents/pi-extensions/compaction-safeguard.ts index ba1481c5c3e..917f3830171 100644 --- a/src/agents/pi-extensions/compaction-safeguard.ts +++ b/src/agents/pi-extensions/compaction-safeguard.ts @@ -376,6 +376,16 @@ function formatPreservedTurnsSection(messages: AgentMessage[]): string { return `\n\n## Recent turns preserved verbatim\n${lines.join("\n")}`; } +function appendSummarySection(summary: string, section: string): string { + if (!section) { + return summary; + } + if (!summary.trim()) { + return section.trimStart(); + } + return `${summary}${section}`; +} + /** * Read and format critical workspace context for compaction summary. * Extracts "Session Startup" and "Red Lines" from AGENTS.md. @@ -569,18 +579,21 @@ export default function compactionSafeguardExtension(api: ExtensionAPI): void { // incorporates context from pruned messages instead of losing it entirely. const effectivePreviousSummary = droppedSummary ?? preparation.previousSummary; - const historySummary = await summarizeInStages({ - messages: messagesToSummarize, - model, - apiKey, - signal, - reserveTokens, - maxChunkTokens, - contextWindow: contextWindowTokens, - customInstructions, - summarizationInstructions, - previousSummary: effectivePreviousSummary, - }); + const historySummary = + messagesToSummarize.length > 0 + ? await summarizeInStages({ + messages: messagesToSummarize, + model, + apiKey, + signal, + reserveTokens, + maxChunkTokens, + contextWindow: contextWindowTokens, + customInstructions, + summarizationInstructions, + previousSummary: effectivePreviousSummary, + }) + : (effectivePreviousSummary?.trim() ?? ""); let summary = historySummary; if (preparation.isSplitTurn && turnPrefixMessages.length > 0) { @@ -596,17 +609,20 @@ export default function compactionSafeguardExtension(api: ExtensionAPI): void { summarizationInstructions, previousSummary: undefined, }); - summary = `${historySummary}\n\n---\n\n**Turn Context (split turn):**\n\n${prefixSummary}`; + const splitTurnSection = `**Turn Context (split turn):**\n\n${prefixSummary}`; + summary = historySummary.trim() + ? `${historySummary}\n\n---\n\n${splitTurnSection}` + : splitTurnSection; } - summary += preservedTurnsSection; + summary = appendSummarySection(summary, preservedTurnsSection); - summary += toolFailureSection; - summary += fileOpsSummary; + summary = appendSummarySection(summary, toolFailureSection); + summary = appendSummarySection(summary, fileOpsSummary); // Append workspace critical context (Session Startup + Red Lines from AGENTS.md) const workspaceContext = await readWorkspaceContextForSummary(); if (workspaceContext) { - summary += workspaceContext; + summary = appendSummarySection(summary, workspaceContext); } return { @@ -633,6 +649,7 @@ export const __testing = { formatToolFailuresSection, splitPreservedRecentTurns, formatPreservedTurnsSection, + appendSummarySection, resolveRecentTurnsPreserve, computeAdaptiveChunkRatio, isOversizedForSummary,