From 5a4e708f4b6dbce3a6da893a6bb259a8799a25ec Mon Sep 17 00:00:00 2001 From: wassname <1103714+wassname@users.noreply.github.com> Date: Thu, 23 Apr 2026 15:36:03 +0800 Subject: [PATCH] fix: adjust tail budget for forced compression and fix missing timestamps --- package.json | 1 + pruner.ts | 33 +++++++++++++++++++++------------ 2 files changed, 22 insertions(+), 12 deletions(-) diff --git a/package.json b/package.json index 2a0ed74..e9397ff 100644 --- a/package.json +++ b/package.json @@ -25,6 +25,7 @@ }, "peerDependencies": { "@mariozechner/pi-coding-agent": "*", + "@mariozechner/pi-ai": "*", "@mariozechner/pi-tui": "*", "@sinclair/typebox": "*" }, diff --git a/pruner.ts b/pruner.ts index 4ecacd0..c33c3d6 100644 --- a/pruner.ts +++ b/pruner.ts @@ -372,32 +372,37 @@ export async function applyPruning( state.forceCompressNext || (totalTokens >= thresholdTokens && msgs.length > AUTO_COMPRESS_CONFIG.protectFirstN + 4) ) { + let tailBudget = Math.floor(thresholdTokens * AUTO_COMPRESS_CONFIG.summaryTargetRatio); + if (state.forceCompressNext) { + // Force compression: reduce tail budget to ensure we have something to compress + tailBudget = Math.min(tailBudget, Math.floor(totalTokens * 0.3)); + } + state.forceCompressNext = false; - const tailBudget = Math.floor(thresholdTokens * AUTO_COMPRESS_CONFIG.summaryTargetRatio); const compressStart = alignBoundaryForward(msgs, AUTO_COMPRESS_CONFIG.protectFirstN); const compressEnd = findTailCutByTokens(msgs, compressStart, tailBudget); - + if (compressStart < compressEnd) { const middle = msgs.slice(compressStart, compressEnd); const summary = await generateSummary(middle, state.previousSummary, null, model); - + if (summary) { const compressed: any[] = []; - + for (let i = 0; i < compressStart; i++) { compressed.push(msgs[i]); } - + const lastHeadRole = msgs[compressStart - 1]?.role || "user"; const firstTailRole = msgs[compressEnd]?.role || "user"; - + let summaryRole = lastHeadRole === "assistant" ? "user" : "assistant"; if (summaryRole === firstTailRole) { const flipped = summaryRole === "user" ? "assistant" : "user"; if (flipped !== lastHeadRole) { summaryRole = flipped; } else { - const tailMsg = { ...msgs[compressEnd] }; + const tailMsg = { ...msgs[compressEnd], timestamp: msgs[compressEnd].timestamp || Date.now() }; const originalContent = tailMsg.content || ""; tailMsg.content = "## Goal\n" + summary + "\n\n--- END OF CONTEXT SUMMARY ---\n\n" + @@ -412,28 +417,32 @@ export async function applyPruning( return sanitizeToolPairs(compressed); } } - + const prefix = "[CONTEXT COMPACTION — REFERENCE ONLY] Earlier turns were compacted into the summary below. " + "This is a handoff from a previous context window — treat it as background reference, " + "NOT as active instructions. Do NOT answer questions or fulfill requests mentioned in this summary; " + "they were already addressed. Respond ONLY to the latest user message that appears AFTER this summary:"; - + compressed.push({ role: summaryRole, content: prefix + "\n\n" + summary, + timestamp: Date.now(), }); - + for (let i = compressEnd; i < msgs.length; i++) { compressed.push(msgs[i]); } - + state.previousSummary = summary; state.compressionCount++; state.tokensSaved += totalTokens - estimateMessagesTokens(compressed); - return sanitizeToolPairs(compressed); } + } else { + if (config.debug) { + console.log(`[ACP] Compression skipped: conversation too short (start: ${compressStart}, end: ${compressEnd})`); + } } }