61 lines
2.4 KiB
TypeScript
61 lines
2.4 KiB
TypeScript
/**
|
|
* 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);
|
|
});
|
|
}
|