/** * Compaction Watch * * Compaction is otherwise silent, which is why "compacting fails a lot" took code * reading and arithmetic to diagnose rather than a glance at a log. This makes it * visible, and specifically names the failure mode that was breaking it here before * 2026-08-16. * * pi reports a `reason` on every compaction: * "manual" - you ran /compact * "threshold" - the healthy path: contextTokens passed contextWindow - reserveTokens * "overflow" - the window was ALREADY full; this is recovery, not prevention * * An "overflow" compaction means the turn blew past contextWindow before the threshold * could fire, which happens whenever `compaction.reserveTokens` is smaller than the * model's `maxTokens`. pi then gets one recovery attempt, summarizing from an * already-overflowed context — and when that does not fit either, the session dies with * "Context overflow recovery failed after one compact-and-retry attempt". * * So: seeing "threshold" is fine. Seeing "overflow" means the settings are wrong for the * model in use, and this says so at the moment it happens instead of leaving you to * infer it later. See CLAUDE.md > Compaction tuning. * * Note there is no event for a *failed* auto-compaction — `onError` exists only on the * manual `ctx.compact()` call — so catching the overflow entry is the earliest reliable * warning available. */ import type { ExtensionAPI } from "@earendil-works/pi-coding-agent"; export default function (pi: ExtensionAPI) { let overflowCount = 0; pi.on("session_before_compact", async (event, ctx) => { if (!ctx.hasUI) return undefined; if (event.reason === "overflow") { overflowCount++; ctx.ui.notify( `Compacting in OVERFLOW recovery (#${overflowCount}) — the context window was already ` + `full before compaction could fire. Raise compaction.reserveTokens above this model's ` + `maxTokens, or cap maxTokens in models.json. See CLAUDE.md > Compaction tuning.`, "warning", ); } // Never alter the compaction itself — observe only. return undefined; }); pi.on("session_compact", async (event, ctx) => { if (!ctx.hasUI) return; const reason = event.reason ?? "unknown"; const retrying = event.willRetry ? ", retrying the aborted turn" : ""; const level = reason === "overflow" ? "warning" : "info"; ctx.ui.notify(`Compacted (${reason}${retrying})`, level); }); }