pi-mega-compact 0.15.4 → 0.16.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/extensions/mega-events/context-handler.js +70 -3
- package/extensions/dashboard-client/dist/assets/OverviewTab-iKiEcOLL.js.map +1 -1
- package/extensions/dashboard-client/src/components/SessionContextGauges.tsx +6 -1
- package/extensions/dashboard-server/resolve-snapshot.ts +1 -1
- package/extensions/mega-events/context-handler.ts +81 -9
- package/package.json +1 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { openStore, } from "../../src/store/sqlite.js";
|
|
2
2
|
import { autoCompactCheck } from "../../src/compact.js";
|
|
3
|
-
import { estimateSessionTokens } from "../../src/tokens.js";
|
|
3
|
+
import { estimateSessionTokens, estimateBlockTokens, estimateMessageTokens } from "../../src/tokens.js";
|
|
4
4
|
import { runCompact, piCompactWouldNoop } from "../mega-pipeline.js";
|
|
5
5
|
import { pressureFromPct, pressureRatio, } from "../mega-config.js";
|
|
6
6
|
import { appendMirrorMessages } from "./mirror-append.js";
|
|
@@ -281,7 +281,16 @@ export function registerContextHandler(pi, runtime, config) {
|
|
|
281
281
|
// model is fed a raw overflow that errors every turn — the
|
|
282
282
|
// "Already compacted" + overflow death-spiral (2026-08-01 incident).
|
|
283
283
|
// A thin anchor is recoverable; an overflowed session is not.
|
|
284
|
-
|
|
284
|
+
//
|
|
285
|
+
// CRITICAL: pct is null for OpenAI-compatible providers that don't
|
|
286
|
+
// report usage.percent (e.g. neuralwatt). Without the token-pressure
|
|
287
|
+
// fallback the hatch never armed → cut=null → raw overflow → 400
|
|
288
|
+
// "conversation too long even after compaction" (2026-08-03 incident
|
|
289
|
+
// on glm-5.2-short, 200K window). Now also fires on pressure >= 0.9
|
|
290
|
+
// (token-basis) so the hatch arms regardless of whether the provider
|
|
291
|
+
// reports pct.
|
|
292
|
+
criticalOver: (pct ?? 0) >= 90 ||
|
|
293
|
+
pressure >= 0.9,
|
|
285
294
|
});
|
|
286
295
|
if (cut === null) {
|
|
287
296
|
runtime.diagCtxCutNull++;
|
|
@@ -308,7 +317,65 @@ export function registerContextHandler(pi, runtime, config) {
|
|
|
308
317
|
// on every replay within the same compaction epoch.
|
|
309
318
|
timestamp: runtime.rt.lastCompactAt ?? Date.now(),
|
|
310
319
|
};
|
|
311
|
-
const
|
|
320
|
+
const recentRaw = messages.slice(cut); // guardrails-allow PREVENT-PI-002: `cut` is the pre-sanitized `compactedFrom` produced by src/boundary.ts computeDropRange, so the preserved run begins on a toolPair-safe index.
|
|
321
|
+
// FIX 2 (2026-08-03 incident): TOKEN-BUDGET CAP on the live-trim view.
|
|
322
|
+
// Compaction fires at tier% of the window (140K for a 200K window),
|
|
323
|
+
// but a SINGLE turn can inject a huge tool output (file read, bash) that
|
|
324
|
+
// jumps context from 139K → 199K+ before the next gate fires. When that
|
|
325
|
+
// happens [summary + preserved tail] can STILL exceed the model window,
|
|
326
|
+
// and the provider rejects with 400 "conversation too long even after
|
|
327
|
+
// compaction". The anchor floor (PREVENT-PI-001) keeps ≥N user messages
|
|
328
|
+
// but has NO token cap, so a 2-message tail of two 80K bash outputs sails
|
|
329
|
+
// right past the window.
|
|
330
|
+
//
|
|
331
|
+
// Cap: when the model context window is known, reserve room for the
|
|
332
|
+
// summary + the model's max output tokens + a 10% safety margin, then
|
|
333
|
+
// drop oldest preserved messages from the front of `recentRaw` until the
|
|
334
|
+
// tail fits. Never drops below the FINAL message (always keep the latest
|
|
335
|
+
// turn so the agent can respond). This is a last-resort HARD cap — it
|
|
336
|
+
// only fires when the preserved tail alone is oversized, which is rare.
|
|
337
|
+
const ctxWindow = runtime.lastCtxWindow;
|
|
338
|
+
// Reserve room for output tokens. Use the model's reported max output
|
|
339
|
+
// when known; fall back to 10% of the window (scales with any model —
|
|
340
|
+
// 20K for a 200K window, 100K for a 1M window) so we never let the
|
|
341
|
+
// preserved tail eat the model's output budget when maxTokens is unknown.
|
|
342
|
+
const maxOutput = runtime.currentModel?.maxTokens && runtime.currentModel.maxTokens > 0
|
|
343
|
+
? runtime.currentModel.maxTokens
|
|
344
|
+
: Math.ceil(ctxWindow * 0.1);
|
|
345
|
+
let recent = recentRaw;
|
|
346
|
+
if (ctxWindow > 0 && recentRaw.length > 1) {
|
|
347
|
+
const summaryTokens = estimateBlockTokens(summaryMsg.text);
|
|
348
|
+
// Reserve: summary + max output + 10% safety margin.
|
|
349
|
+
const safetyMargin = Math.ceil(ctxWindow * 0.1);
|
|
350
|
+
const budget = ctxWindow - maxOutput - safetyMargin - summaryTokens;
|
|
351
|
+
if (budget > 0) {
|
|
352
|
+
// Walk recent from the front, dropping oldest first until the
|
|
353
|
+
// remaining tail fits. Use the AgentMessage→engine-text estimate via
|
|
354
|
+
// messageContentText (already imported) + estimateMessageTokens.
|
|
355
|
+
let tailTokens = 0;
|
|
356
|
+
for (let i = recentRaw.length - 1; i >= 0; i--) {
|
|
357
|
+
const m = recentRaw[i];
|
|
358
|
+
tailTokens += estimateMessageTokens({
|
|
359
|
+
text: messageContentText(m),
|
|
360
|
+
});
|
|
361
|
+
if (tailTokens > budget) {
|
|
362
|
+
// Keep from i+1 onward; but never fewer than the final message.
|
|
363
|
+
const startIdx = Math.min(i + 1, recentRaw.length - 1);
|
|
364
|
+
if (startIdx > 0) {
|
|
365
|
+
recent = recentRaw.slice(startIdx);
|
|
366
|
+
runtime.logger.warn("live-trim-tail-cap", {
|
|
367
|
+
sessionId: runtime.rt.sessionId,
|
|
368
|
+
dropped: startIdx,
|
|
369
|
+
tailTokens,
|
|
370
|
+
budget,
|
|
371
|
+
ctxWindow,
|
|
372
|
+
});
|
|
373
|
+
}
|
|
374
|
+
break;
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
}
|
|
312
379
|
// v0.8.6: cache the trim view so subsequent gated calls in this epoch
|
|
313
380
|
// replay it verbatim (stabilizing the KV-cache prefix) instead of
|
|
314
381
|
// regenerating a fresh summary + sentinel every fire.
|