Compaction/Safeguard: avoid empty-history fallback text

This commit is contained in:
Rodrigo Uroz 2026-02-27 20:04:54 +00:00 committed by Josh Lehman
parent 1566d428a3
commit 9216b885f3
No known key found for this signature in database
GPG Key ID: D141B425AC7F876B
2 changed files with 48 additions and 17 deletions

View File

@ -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);

View File

@ -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,