From 1fdd3592d365720a4997fbc9dbdf91417461283d Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Tue, 13 Jan 2026 05:58:35 +0000 Subject: [PATCH] fix: tune compaction safeguard schema (#700) (thanks @thewilloftheshadow) --- CHANGELOG.md | 1 + .../pi-extensions/compaction-safeguard.ts | 54 ++++++++++++++----- src/config/zod-schema.ts | 4 +- 3 files changed, 44 insertions(+), 15 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index edb9c220c..2dfd801be 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ ### Changes - Cron: accept ISO timestamps for one-shot schedules (UTC) and allow optional delete-after-run; wired into CLI + macOS editor. - Gateway: add Tailscale binary discovery, custom bind mode, and probe auth retry for password changes. (#740 — thanks @jeffersonwarrior) +- Agents: add compaction mode config with optional safeguard summarization for long histories. (#700 — thanks @thewilloftheshadow) ## 2026.1.12-4 diff --git a/src/agents/pi-extensions/compaction-safeguard.ts b/src/agents/pi-extensions/compaction-safeguard.ts index 198fb99ab..4b374c258 100644 --- a/src/agents/pi-extensions/compaction-safeguard.ts +++ b/src/agents/pi-extensions/compaction-safeguard.ts @@ -2,13 +2,9 @@ import type { AgentMessage } from "@mariozechner/pi-agent-core"; import type { ExtensionAPI, ExtensionContext, + FileOperations, } from "@mariozechner/pi-coding-agent"; -import { - computeFileLists, - formatFileOperations, - generateSummary, - estimateTokens, -} from "@mariozechner/pi-coding-agent"; +import { estimateTokens, generateSummary } from "@mariozechner/pi-coding-agent"; import { DEFAULT_CONTEXT_TOKENS } from "../defaults.js"; @@ -16,10 +12,40 @@ const MAX_CHUNK_RATIO = 0.4; const FALLBACK_SUMMARY = "Summary unavailable due to context limits. Older messages were truncated."; const TURN_PREFIX_INSTRUCTIONS = - "This summary covers the prefix of a split turn. Focus on the original request," - + " early progress, and any details needed to understand the retained suffix."; + "This summary covers the prefix of a split turn. Focus on the original request," + + " early progress, and any details needed to understand the retained suffix."; -function chunkMessages(messages: AgentMessage[], maxTokens: number): AgentMessage[][] { +function computeFileLists(fileOps: FileOperations): { + readFiles: string[]; + modifiedFiles: string[]; +} { + const modified = new Set([...fileOps.edited, ...fileOps.written]); + const readFiles = [...fileOps.read].filter((f) => !modified.has(f)).sort(); + const modifiedFiles = [...modified].sort(); + return { readFiles, modifiedFiles }; +} + +function formatFileOperations( + readFiles: string[], + modifiedFiles: string[], +): string { + const sections: string[] = []; + if (readFiles.length > 0) { + sections.push(`\n${readFiles.join("\n")}\n`); + } + if (modifiedFiles.length > 0) { + sections.push( + `\n${modifiedFiles.join("\n")}\n`, + ); + } + if (sections.length === 0) return ""; + return `\n\n${sections.join("\n\n")}`; +} + +function chunkMessages( + messages: AgentMessage[], + maxTokens: number, +): AgentMessage[][] { if (messages.length === 0) return []; const chunks: AgentMessage[][] = []; @@ -28,10 +54,7 @@ function chunkMessages(messages: AgentMessage[], maxTokens: number): AgentMessag for (const message of messages) { const messageTokens = estimateTokens(message); - if ( - currentChunk.length > 0 && - currentTokens + messageTokens > maxTokens - ) { + if (currentChunk.length > 0 && currentTokens + messageTokens > maxTokens) { chunks.push(currentChunk); currentChunk = []; currentTokens = 0; @@ -144,7 +167,10 @@ export default function compactionSafeguardExtension(api: ExtensionAPI): void { }); let summary = historySummary; - if (preparation.isSplitTurn && preparation.turnPrefixMessages.length > 0) { + if ( + preparation.isSplitTurn && + preparation.turnPrefixMessages.length > 0 + ) { const prefixSummary = await summarizeChunks({ messages: preparation.turnPrefixMessages, model, diff --git a/src/config/zod-schema.ts b/src/config/zod-schema.ts index 1fb119124..cacd7fcfb 100644 --- a/src/config/zod-schema.ts +++ b/src/config/zod-schema.ts @@ -1210,7 +1210,9 @@ const AgentDefaultsSchema = z .optional(), compaction: z .object({ - mode: z.union([z.literal("default"), z.literal("safeguard")]).optional(), + mode: z + .union([z.literal("default"), z.literal("safeguard")]) + .optional(), reserveTokensFloor: z.number().int().nonnegative().optional(), memoryFlush: z .object({