pi-mega-compact 0.7.1 → 0.7.2

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.
@@ -8,13 +8,13 @@
8
8
  */
9
9
  import { sessionEntryToContextMessages } from "@earendil-works/pi-coding-agent";
10
10
  import { compactSession } from "../src/engine.js";
11
- import { recallAndInline, recallAndInlineAsync, formatRecallBlock, } from "../src/recall.js";
11
+ import { recallAndInline, recallAndInlineAsync, formatRecallBlock } from "../src/recall.js";
12
12
  import { normalizeSessionId } from "../src/store.js";
13
13
  import { estimateBlockTokens } from "../src/tokens.js";
14
14
  import { touchSession, logDaily } from "../src/store/sqlite.js";
15
15
  import { consolidateMemories } from "../src/memory.js";
16
- import { C, MARKER_TYPE } from "./mega-runtime.js";
17
- import { resolveRepoRoot, preserveRecentForPressure, } from "./mega-config.js";
16
+ import { C, MARKER_TYPE, } from "./mega-runtime.js";
17
+ import { resolveRepoRoot, preserveRecentForPressure } from "./mega-config.js";
18
18
  import { runRaptor } from "../src/dedup/raptor/index.js";
19
19
  import { loadDedupConfig } from "../src/config/dedup.js";
20
20
  import { upsertEmbedding as indexUpsertEmbedding } from "../src/store/vectorIndex.js";
@@ -110,17 +110,13 @@ function doCompact(view, keepFrom, opts, sid, config, pi, ctx, runtime) {
110
110
  // Grow the rolling "saved" goal so the progress bar always has a fresh
111
111
  // denominator (we don't want it pinned at 100% once we pass an old target).
112
112
  if (runtime.rt.tokensSaved > runtime.savedGoal)
113
- runtime.savedGoal =
114
- Math.ceil((runtime.rt.tokensSaved * 1.25) / 10_000) * 10_000;
113
+ runtime.savedGoal = Math.ceil((runtime.rt.tokensSaved * 1.25) / 10_000) * 10_000;
115
114
  // Live toolbar activity: what file/region just got compacted or deduped.
116
115
  // Rendered via the rotating ticker line (see snapshot); the ring buffer is
117
116
  // cycled one-per-repaint so the single line scrolls through recent files.
118
117
  const files = result.filesModified ?? [];
119
118
  const fileLabel = files.length
120
- ? files
121
- .map((f) => f.split("/").pop() ?? f)
122
- .slice(0, 2)
123
- .join(", ")
119
+ ? files.map((f) => f.split("/").pop() ?? f).slice(0, 2).join(", ")
124
120
  : result.regionHash.slice(0, 8);
125
121
  runtime.lastActivityAt = Date.now();
126
122
  // Explain-why line: surfaced while fresh. Pulls the dedup reason (which for
@@ -172,10 +168,7 @@ function doCompact(view, keepFrom, opts, sid, config, pi, ctx, runtime) {
172
168
  // waiting for the next turn-cadence tick. Uses the shared runMemoryReview
173
169
  // helper (fire-and-forget; doCompact is sync). Best-effort + non-fatal. Only
174
170
  // fires above the `high` band so low-pressure compactions don't pay the cost.
175
- if (!result.deduped &&
176
- config.memoryAutoReview &&
177
- runtime.pressureBand !== "low" &&
178
- runtime.pressureBand !== "medium") {
171
+ if (!result.deduped && config.memoryAutoReview && runtime.pressureBand !== "low" && runtime.pressureBand !== "medium") {
179
172
  void runMemoryReview(runtime, view, "pressure");
180
173
  }
181
174
  // Sentinel marker: a non-LLM bookkeeping entry so subsequent triggers can
@@ -203,9 +196,7 @@ function doCompact(view, keepFrom, opts, sid, config, pi, ctx, runtime) {
203
196
  // S25: stamp the tree with the newest checkpoint epoch so the
204
197
  // freshness guard in raptorSearchHits can reject stale trees after a
205
198
  // later compaction adds newer checkpoints.
206
- const builtAt = all.length > 0
207
- ? Math.max(...all.map((c) => c.timestamp))
208
- : Date.now();
199
+ const builtAt = all.length > 0 ? Math.max(...all.map((c) => c.timestamp)) : Date.now();
209
200
  runRaptor(leaves, {
210
201
  stateDir: runtime.currentStateDir,
211
202
  sessionId: sid,
@@ -323,8 +314,7 @@ export function piCompactWouldNoop(ctx) {
323
314
  if (m.role !== "toolResult")
324
315
  isCut = true;
325
316
  const c = m.content;
326
- const text = typeof c === "string"
327
- ? c
317
+ const text = typeof c === "string" ? c
328
318
  : Array.isArray(c)
329
319
  ? c.map((b) => b?.text ?? "").join(" ")
330
320
  : "";
@@ -377,22 +367,12 @@ export function doRecall(runtime, config, ctx, query, source) {
377
367
  liveWindow,
378
368
  dedupSim: config.dedupSim,
379
369
  }, runtime.store);
380
- runtime.dashboard.event("recall", {
381
- source,
382
- query: query.slice(0, 120),
383
- injected: result.toInject.length,
384
- empty: result.empty,
385
- });
370
+ runtime.dashboard.event("recall", { source, query: query.slice(0, 120), injected: result.toInject.length, empty: result.empty });
386
371
  if (!result.empty && result.toInject.length > 0) {
387
372
  const top = result.toInject[0];
388
373
  const scorePct = Math.round((top.score ?? 0) * 100);
389
374
  const files = top.checkpoint.filesModified ?? [];
390
- const label = files.length
391
- ? files
392
- .map((f) => f.split("/").pop() ?? f)
393
- .slice(0, 2)
394
- .join(", ")
395
- : top.checkpoint.checkpointId;
375
+ const label = files.length ? files.map((f) => f.split("/").pop() ?? f).slice(0, 2).join(", ") : top.checkpoint.checkpointId;
396
376
  runtime.pushTicker(`${C.amber}↩${C.reset} recalled ${top.checkpoint.checkpointId} · ${scorePct}% · ${label}`);
397
377
  runtime.lastWhy = `why: recalled@${scorePct}% (${result.toInject.length} chkpt)`;
398
378
  }
@@ -415,15 +395,9 @@ export async function doRecallAsync(runtime, config, ctx, query, source, opts =
415
395
  const liveWindow = config.windowDedupe ? extractLiveWindow(ctx) : undefined;
416
396
  // Sync same-repo first (fast, never blocks).
417
397
  const sameRepo = recallAndInline({
418
- sessionId: sid,
419
- query,
420
- limit: config.autoInlineK,
421
- source,
422
- skipInjected: true,
423
- recallMaxTokens: config.recallMaxTokens,
424
- windowDedupe: config.windowDedupe,
425
- liveWindow,
426
- dedupSim: config.dedupSim,
398
+ sessionId: sid, query, limit: config.autoInlineK, source, skipInjected: true,
399
+ recallMaxTokens: config.recallMaxTokens, windowDedupe: config.windowDedupe,
400
+ liveWindow, dedupSim: config.dedupSim,
427
401
  }, runtime.store);
428
402
  if (!config.crossRepoEnabled || !opts.crossRepo)
429
403
  return sameRepo;
@@ -432,22 +406,13 @@ export async function doRecallAsync(runtime, config, ctx, query, source, opts =
432
406
  // Augment: cross-repo HNSW (async) with the stricter floor. Non-fatal.
433
407
  try {
434
408
  const x = await recallAndInlineAsync({
435
- sessionId: sid,
436
- query,
437
- limit: config.autoInlineK,
438
- source,
439
- skipInjected: true,
440
- recallMaxTokens: config.recallMaxTokens,
441
- windowDedupe: config.windowDedupe,
442
- liveWindow,
443
- dedupSim: config.crossRepoCosine,
444
- crossRepo: true,
409
+ sessionId: sid, query, limit: config.autoInlineK, source, skipInjected: true,
410
+ recallMaxTokens: config.recallMaxTokens, windowDedupe: config.windowDedupe,
411
+ liveWindow, dedupSim: config.crossRepoCosine, crossRepo: true,
445
412
  globalIndexDir: process.env.MEGACOMPACT_INDEX_DIR,
446
413
  }, runtime.store);
447
414
  runtime.dashboard.event("recall-crossrepo", {
448
- source,
449
- query: query.slice(0, 120),
450
- injected: x.toInject.length,
415
+ source, query: query.slice(0, 120), injected: x.toInject.length,
451
416
  sourceRepos: x.toInject.map((h) => h.repoId).filter(Boolean),
452
417
  });
453
418
  // Merge, dedup by checkpointId, respect the same token cap by reformatting.
@@ -16,9 +16,9 @@ import { VectorStore } from "../src/vectorStore.js";
16
16
  import { toEngineMessages } from "../src/adapt.js";
17
17
  import { normalizeSessionId } from "../src/store.js";
18
18
  import { Logger } from "../src/log.js";
19
- import { recordModelSnapshot, latestModelSnapshot, upsertRepoRegistry, recordRepoModel, } from "../src/store/sqlite.js";
19
+ import { recordModelSnapshot, latestModelSnapshot, upsertRepoRegistry, recordRepoModel } from "../src/store/sqlite.js";
20
20
  import { detectCrossRepoDrift } from "../src/driftDetection.js";
21
- import { repoStateDir, resolveRepoRoot, pressureRatio, pressureFromPct, pressureBand, effectiveThresholdTokens, } from "./mega-config.js";
21
+ import { repoStateDir, resolveRepoRoot, pressureRatio, pressureFromPct, pressureBand, effectiveThresholdTokens } from "./mega-config.js";
22
22
  import { Dashboard } from "./mega-dashboard.js";
23
23
  export const STATUS_KEY = "mega-compact";
24
24
  export const WIDGET_KEY = "mega-compact-stats";
@@ -74,25 +74,40 @@ function visibleWidth(s) {
74
74
  let w = 0;
75
75
  for (const ch of stripped) {
76
76
  const cp = ch.codePointAt(0) ?? 0;
77
- const wide = cp >= 0x1100 &&
78
- (cp <= 0x115f ||
79
- (cp >= 0x2e80 && cp <= 0x303e) ||
80
- (cp >= 0x3041 && cp <= 0x33ff) ||
81
- (cp >= 0x3400 && cp <= 0x4dbf) ||
82
- (cp >= 0x4e00 && cp <= 0x9fff) ||
83
- (cp >= 0xa000 && cp <= 0xa4cf) ||
84
- (cp >= 0xac00 && cp <= 0xd7a3) ||
85
- (cp >= 0xf900 && cp <= 0xfaff) ||
86
- (cp >= 0xfe30 && cp <= 0xfe4f) ||
87
- (cp >= 0xff00 && cp <= 0xff60) ||
88
- (cp >= 0xffe0 && cp <= 0xffe6) ||
89
- (cp >= 0x1f300 && cp <= 0x1faff) ||
90
- (cp >= 0x20000 && cp <= 0x3fffd));
77
+ const wide = cp >= 0x1100 && ((cp <= 0x115f) || (cp >= 0x2e80 && cp <= 0x303e) ||
78
+ (cp >= 0x3041 && cp <= 0x33ff) || (cp >= 0x3400 && cp <= 0x4dbf) ||
79
+ (cp >= 0x4e00 && cp <= 0x9fff) || (cp >= 0xa000 && cp <= 0xa4cf) ||
80
+ (cp >= 0xac00 && cp <= 0xd7a3) || (cp >= 0xf900 && cp <= 0xfaff) ||
81
+ (cp >= 0xfe30 && cp <= 0xfe4f) || (cp >= 0xff00 && cp <= 0xff60) ||
82
+ (cp >= 0xffe0 && cp <= 0xffe6) || (cp >= 0x1f300 && cp <= 0x1faff) ||
83
+ (cp >= 0x20000 && cp <= 0x3fffd));
91
84
  w += wide ? 2 : 1;
92
85
  }
93
86
  return w;
94
87
  }
95
88
  /** Pad a content string (with ANSI colors) to `width` cells using panel bg. */
89
+ function spreadGroups(width, groups, indent = 0) {
90
+ if (groups.length === 0)
91
+ return panelLine('', width);
92
+ // Calculate visible width of each group
93
+ const groupWidths = groups.map(g => visibleWidth(PANEL_BG + g.replace(/\x1b\[0m/g, PANEL_RST)));
94
+ const totalContent = groupWidths.reduce((a, b) => a + b, 0);
95
+ const separator = ` ${C.dim}│${C.reset} `;
96
+ const sepWidth = visibleWidth(PANEL_BG + separator.replace(/\x1b\[0m/g, PANEL_RST));
97
+ const totalSep = sepWidth * (groups.length - 1);
98
+ const available = width - indent - totalContent - totalSep;
99
+ const gapSize = groups.length > 1 ? Math.max(1, Math.floor(available / (groups.length - 1))) : 0;
100
+ const extra = groups.length > 1 ? available - gapSize * (groups.length - 1) : 0;
101
+ let line = ' '.repeat(indent);
102
+ for (let i = 0; i < groups.length; i++) {
103
+ line += groups[i];
104
+ if (i < groups.length - 1) {
105
+ const gap = gapSize + (i < extra ? 1 : 0);
106
+ line += separator + ' '.repeat(Math.max(0, gap));
107
+ }
108
+ }
109
+ return panelLine(line, width);
110
+ }
96
111
  function panelLine(content, width) {
97
112
  const withBg = PANEL_BG + content.replace(/\x1b\[0m/g, PANEL_RST);
98
113
  const pad = Math.max(0, width - visibleWidth(withBg));
@@ -105,10 +120,8 @@ function panelBar(width, ch = "─") {
105
120
  /** Token-count formatter: M at/above 1e6, k at/above 1e3, raw below.
106
121
  * 5,472,700 → "5.5mil", 24,100 → "24.1k", 142 → "142". */
107
122
  function fmtTokens(x) {
108
- return x >= 1_000_000
109
- ? `${(x / 1_000_000).toFixed(1)}mil`
110
- : x >= 1000
111
- ? `${(x / 1000).toFixed(1)}k`
123
+ return x >= 1_000_000 ? `${(x / 1_000_000).toFixed(1)}mil`
124
+ : x >= 1000 ? `${(x / 1000).toFixed(1)}k`
112
125
  : `${Math.round(x)}`;
113
126
  }
114
127
  /** Retro gradient bar — `w` cells shaded by fill position (green→amber→red).
@@ -123,10 +136,8 @@ function ramp(pct, w = 12) {
123
136
  for (let i = 0; i < full; i++)
124
137
  out += (i / w < 0.6 ? C.green : i / w < 0.85 ? C.amber : C.red) + "█";
125
138
  if (fracCell)
126
- out +=
127
- (full / w < 0.6 ? C.green : full / w < 0.85 ? C.amber : C.red) + fracCell;
128
- out +=
129
- C.dim + "░".repeat(Math.max(0, w - full - (fracCell ? 1 : 0))) + C.reset;
139
+ out += (full / w < 0.6 ? C.green : full / w < 0.85 ? C.amber : C.red) + fracCell;
140
+ out += C.dim + "░".repeat(Math.max(0, w - full - (fracCell ? 1 : 0))) + C.reset;
130
141
  return out;
131
142
  }
132
143
  /** Human "time since" string from a millisecond delta (or null → "never"). */
@@ -257,9 +268,7 @@ export class MegaRuntime {
257
268
  * context event, or a `custom` tier with no tierPct). Always finite + in [0,1].
258
269
  */
259
270
  get pressure() {
260
- if (this.lastCtxWindow > 0 &&
261
- this.config.tierPct != null &&
262
- this.lastCtxPercent != null) {
271
+ if (this.lastCtxWindow > 0 && this.config.tierPct != null && this.lastCtxPercent != null) {
263
272
  // pressureFromPct(x) = x/100, and x = lastCtxPercent/tierPct, so this is
264
273
  // exactly the intended lastCtxPercent/(tierPct*100) 0–1 ratio: at the
265
274
  // fire point (lastCtxPercent == tierPct*100) pressure == 1.0, matching the
@@ -267,9 +276,7 @@ export class MegaRuntime {
267
276
  // the band doesn't jump when a token-count vs percent-only event arrives.
268
277
  return pressureFromPct(this.lastCtxPercent / this.config.tierPct);
269
278
  }
270
- if (this.lastCtxTokens != null &&
271
- this.lastCtxTokens > 0 &&
272
- this.config.thresholdTokens > 0) {
279
+ if (this.lastCtxTokens != null && this.lastCtxTokens > 0 && this.config.thresholdTokens > 0) {
273
280
  return pressureRatio(this.lastCtxTokens, this.config.thresholdTokens);
274
281
  }
275
282
  return pressureFromPct(this.lastCtxPercent);
@@ -295,14 +302,8 @@ export class MegaRuntime {
295
302
  }
296
303
  constructor(config) {
297
304
  this.config = config;
298
- this.store = new VectorStore({
299
- dedupSim: config.dedupSim,
300
- stateDir: config.stateDir,
301
- });
302
- this.logger = new Logger({
303
- enabled: config.debug,
304
- path: join(config.stateDir, "mega-compact.log"),
305
- });
305
+ this.store = new VectorStore({ dedupSim: config.dedupSim, stateDir: config.stateDir });
306
+ this.logger = new Logger({ enabled: config.debug, path: join(config.stateDir, "mega-compact.log") });
306
307
  this.dashboard = new Dashboard(config.stateDir);
307
308
  this.currentStateDir = config.stateDir;
308
309
  }
@@ -313,22 +314,14 @@ export class MegaRuntime {
313
314
  * and events are fully isolated. Falls back to the global default outside git.
314
315
  */
315
316
  bindRepo(cwd) {
316
- const dir = cwd
317
- ? repoStateDir(cwd, this.config.stateDir)
318
- : this.config.stateDir;
319
- const key = cwd ? (resolveRepoRoot(cwd) ?? dir) : dir;
317
+ const dir = cwd ? repoStateDir(cwd, this.config.stateDir) : this.config.stateDir;
318
+ const key = cwd ? resolveRepoRoot(cwd) ?? dir : dir;
320
319
  if (key === this.activeRepoRoot)
321
320
  return dir;
322
321
  this.activeRepoRoot = key;
323
322
  this.currentStateDir = dir;
324
- this.store = new VectorStore({
325
- dedupSim: this.config.dedupSim,
326
- stateDir: dir,
327
- });
328
- this.logger = new Logger({
329
- enabled: this.config.debug,
330
- path: join(dir, "mega-compact.log"),
331
- });
323
+ this.store = new VectorStore({ dedupSim: this.config.dedupSim, stateDir: dir });
324
+ this.logger = new Logger({ enabled: this.config.debug, path: join(dir, "mega-compact.log") });
332
325
  this.dashboard = new Dashboard(dir);
333
326
  // Aggregate this repo into the machine-wide index so the multi-repo
334
327
  // dashboard (Summary / All-repos tabs) can show it alongside every other
@@ -338,7 +331,7 @@ export class MegaRuntime {
338
331
  try {
339
332
  const repo = this.store.repoStats();
340
333
  const di = this.store.dataInvariant();
341
- const root = key !== dir ? key : (resolveRepoRoot(cwd ?? dir) ?? dir);
334
+ const root = key !== dir ? key : resolveRepoRoot(cwd ?? dir) ?? dir;
342
335
  upsertRepoRegistry({
343
336
  repoRoot: root,
344
337
  displayName: root.split(/[\\/]/).filter(Boolean).pop() ?? root,
@@ -378,9 +371,7 @@ export class MegaRuntime {
378
371
  // armed lights at/above the REAL fire point: max(effectiveThresholdPct,
379
372
  // fastGatePct). fastGatePct already equals tierPct*100 by default, but a
380
373
  // MEGACOMPACT_FAST_GATE_PCT override can raise it, so we take the max.
381
- const armed = this.lastCtxPercent != null &&
382
- this.lastCtxPercent >=
383
- Math.max(effectiveThresholdPct ?? 0, this.config.fastGatePct);
374
+ const armed = this.lastCtxPercent != null && this.lastCtxPercent >= Math.max(effectiveThresholdPct ?? 0, this.config.fastGatePct);
384
375
  const ready = armed && (this.lastCtxTokens ?? 0) >= this.effectiveThreshold;
385
376
  this.dashboard.snapshot({
386
377
  version: 1,
@@ -410,32 +401,10 @@ export class MegaRuntime {
410
401
  dedupSkips: this.rt.dedupSkips,
411
402
  dedupAttempts: this.rt.dedupAttempts,
412
403
  },
413
- context: {
414
- tokens: this.lastCtxTokens,
415
- percent: this.lastCtxPercent,
416
- contextWindow: this.lastCtxWindow,
417
- },
418
- trigger: {
419
- armed,
420
- ready,
421
- currentTokens: this.lastCtxTokens,
422
- thresholdTokens: this.effectiveThreshold,
423
- fastGatePct: this.config.fastGatePct,
424
- tierPct: this.config.tierPct,
425
- effectiveThresholdPct,
426
- },
404
+ context: { tokens: this.lastCtxTokens, percent: this.lastCtxPercent, contextWindow: this.lastCtxWindow },
405
+ trigger: { armed, ready, currentTokens: this.lastCtxTokens, thresholdTokens: this.effectiveThreshold, fastGatePct: this.config.fastGatePct, tierPct: this.config.tierPct, effectiveThresholdPct },
427
406
  crew: { activeAgents: this.activeAgents, currentTurn: this.currentTurn },
428
- store: {
429
- checkpointCount: st.checkpointCount,
430
- totalTokenEstimate: st.totalTokenEstimate,
431
- originalTokens: st.originalTokens,
432
- tokensSaved: this.rt.tokensSaved,
433
- injectedCount: st.injectedCount,
434
- dedupHitRate: st.dedupHitRate,
435
- storageDedupRate: st.storageDedupRate,
436
- dedupAttempts: st.dedupAttempts,
437
- dedupCollapsed: st.dedupCollapsed,
438
- },
407
+ store: { checkpointCount: st.checkpointCount, totalTokenEstimate: st.totalTokenEstimate, originalTokens: st.originalTokens, tokensSaved: this.rt.tokensSaved, injectedCount: st.injectedCount, dedupHitRate: st.dedupHitRate, storageDedupRate: st.storageDedupRate, dedupAttempts: st.dedupAttempts, dedupCollapsed: st.dedupCollapsed },
439
408
  // Reconciled token accounting (single canonical formula, session + repo).
440
409
  // Freed = In − Out; In = Freed + Out. session.Freed = rt.tokensSaved (incl.
441
410
  // deduped-away originals); repo.Freed = repo.tokensSaved meta counter.
@@ -444,19 +413,14 @@ export class MegaRuntime {
444
413
  tokensIn: this.rt.tokensSaved + st.totalTokenEstimate,
445
414
  tokensOut: st.totalTokenEstimate,
446
415
  tokensFreed: this.rt.tokensSaved,
447
- compressionPct: this.rt.tokensSaved + st.totalTokenEstimate > 0
448
- ? this.rt.tokensSaved /
449
- (this.rt.tokensSaved + st.totalTokenEstimate)
450
- : 0,
416
+ compressionPct: (this.rt.tokensSaved + st.totalTokenEstimate) > 0 ? this.rt.tokensSaved / (this.rt.tokensSaved + st.totalTokenEstimate) : 0,
451
417
  dedupPct: st.storageDedupRate,
452
418
  },
453
419
  repo: {
454
420
  tokensIn: repo.tokensSaved + repo.totalTokenEstimate,
455
421
  tokensOut: repo.totalTokenEstimate,
456
422
  tokensFreed: repo.tokensSaved,
457
- compressionPct: repo.tokensSaved + repo.totalTokenEstimate > 0
458
- ? repo.tokensSaved / (repo.tokensSaved + repo.totalTokenEstimate)
459
- : 0,
423
+ compressionPct: (repo.tokensSaved + repo.totalTokenEstimate) > 0 ? repo.tokensSaved / (repo.tokensSaved + repo.totalTokenEstimate) : 0,
460
424
  dedupPct: repo.storageDedupRate,
461
425
  },
462
426
  },
@@ -481,24 +445,14 @@ export class MegaRuntime {
481
445
  // Live stats widget above the editor
482
446
  if (ctx) {
483
447
  // ── gather widget data (computed per snapshot, rendered per frame) ────
484
- const tokStr = this.lastCtxTokens != null
485
- ? `${Math.round(this.lastCtxTokens / 1000)}k`
486
- : "?";
487
- const maxStr = this.lastCtxWindow > 0
488
- ? `${Math.round(this.lastCtxWindow / 1000)}k`
489
- : "?";
490
- const pctStr = this.lastCtxPercent != null
491
- ? `${Math.round(this.lastCtxPercent * 10) / 10}%`
492
- : "?%";
448
+ const tokStr = this.lastCtxTokens != null ? `${Math.round(this.lastCtxTokens / 1000)}k` : "?";
449
+ const maxStr = this.lastCtxWindow > 0 ? `${Math.round(this.lastCtxWindow / 1000)}k` : "?";
450
+ const pctStr = this.lastCtxPercent != null ? `${Math.round(this.lastCtxPercent * 10) / 10}%` : "?%";
493
451
  // S24: the tier label is the LIVE pressure band (low/medium/high/ultra/
494
452
  // mega), not the static env preset. It climbs as context fills.
495
453
  const liveBand = this.pressureBand;
496
454
  const tierLabel = `${C.bold}${liveBand}${C.reset}${C.gray}·${this.config.tier}${C.reset}`;
497
- const triggerLabel = ready
498
- ? `${C.green}● ready${C.reset}`
499
- : armed
500
- ? `${C.amber}◐ armed${C.reset}`
501
- : `${C.gray}○ idle${C.reset}`;
455
+ const triggerLabel = ready ? `${C.green}● ready${C.reset}` : armed ? `${C.amber}◐ armed${C.reset}` : `${C.gray}○ idle${C.reset}`;
502
456
  // Storage dedup rate is cumulative (store-wide, per-repo) and survives
503
457
  // session resets. Always show a number (decimal for sub-10%).
504
458
  const storageRate = st.storageDedupRate; // 0..1
@@ -523,55 +477,27 @@ export class MegaRuntime {
523
477
  const ctxPct = this.lastCtxPercent != null ? this.lastCtxPercent / 100 : 0;
524
478
  // Model + provider (S26 capture) for the header.
525
479
  const modelName = modelSnap?.modelName ?? modelSnap?.modelId ?? "?";
526
- const modelStr = modelSnap?.provider
527
- ? `${modelName}·${modelSnap.provider}`
528
- : modelName;
480
+ const modelStr = modelSnap?.provider ? `${modelName}·${modelSnap.provider}` : modelName;
529
481
  // Since-last-compact (ms; null until first compaction this session).
530
- const sinceCompact = this.rt.lastCompactAt != null
531
- ? Date.now() - this.rt.lastCompactAt
532
- : null;
482
+ const sinceCompact = this.rt.lastCompactAt != null ? Date.now() - this.rt.lastCompactAt : null;
533
483
  // Memory store: embedder + compression ratio (original / stored).
534
484
  const embedderName = this.embedderName();
535
485
  const compRatio = st.originalTokens > 0 && st.totalTokenEstimate > 0
536
486
  ? st.originalTokens / st.totalTokenEstimate
537
- : st.originalTokens > 0
538
- ? 1
539
- : 0;
487
+ : (st.originalTokens > 0 ? 1 : 0);
540
488
  const compStr = compRatio >= 1 ? `${compRatio.toFixed(1)}x` : "—";
541
489
  // Cross-repo drift status (cached, read-only).
542
490
  const driftStatus = this.driftStatus();
543
491
  const agentsActive = this.activeAgents > 0;
544
492
  this.widgetData = {
545
493
  version: ownVersion(),
546
- tierLabel,
547
- triggerLabel,
548
- pctStr,
549
- tokStr,
550
- maxStr,
551
- ctxPct,
552
- chk: st.checkpointCount,
553
- agentStr,
554
- turnStr,
555
- dedupStr,
556
- sessIn,
557
- sessKept,
558
- sTxt,
559
- repoIn,
560
- repoKept,
561
- rTxt,
562
- repoChk: repo.checkpointCount,
563
- repoSess: repo.sessionCount,
564
- modelStr,
565
- sinceCompact,
566
- embedderName,
567
- compStr,
568
- driftStatus,
569
- agentsActive,
494
+ tierLabel, triggerLabel, pctStr, tokStr, maxStr, ctxPct,
495
+ chk: st.checkpointCount, agentStr, turnStr, dedupStr,
496
+ sessIn, sessKept, sTxt, repoIn, repoKept, rTxt,
497
+ repoChk: repo.checkpointCount, repoSess: repo.sessionCount,
498
+ modelStr, sinceCompact, embedderName, compStr, driftStatus, agentsActive,
570
499
  fresh: Date.now() - this.lastActivityAt < 4000,
571
- ticker: this.ticker,
572
- lastWhy: this.lastWhy,
573
- tierTrace: this.tierTrace,
574
- pulsing: this.pulsing,
500
+ ticker: this.ticker, lastWhy: this.lastWhy, tierTrace: this.tierTrace, pulsing: this.pulsing,
575
501
  };
576
502
  // Auto-fit: register a factory so pi re-renders the panel at the REAL
577
503
  // terminal width every frame (tui.columns), instead of guessing with
@@ -594,24 +520,31 @@ export class MegaRuntime {
594
520
  buildWidgetLines(width) {
595
521
  const wd = this.widgetData;
596
522
  if (!wd) {
597
- return [
598
- panelBar(width, "─"),
599
- panelLine(" mega-compact: warming up…", width),
600
- panelBar(width, "─"),
601
- ];
523
+ return [panelBar(width, "─"), panelLine(" mega-compact: warming up…", width), panelBar(width, "─")];
602
524
  }
603
- const pulse = wd.pulsing
604
- ? `${C.cyan}${PULSE[Math.floor(Date.now() / 250) % PULSE.length]}${C.reset} `
605
- : "";
525
+ const pulse = wd.pulsing ? `${C.cyan}${PULSE[Math.floor(Date.now() / 250) % PULSE.length]}${C.reset} ` : "";
606
526
  const lines = [
607
527
  // top border
608
528
  panelBar(width, "─"),
609
529
  // L1 — header: tier + ctx bar + pct/tokens + status + model + chk + agents/turn
610
- panelLine(` ${C.amber}⚡ ${wd.tierLabel}${C.reset} v${C.bold}${wd.version}${C.reset} ${ramp(wd.ctxPct, 20)} ${C.bold}${wd.pctStr}${C.reset} ${wd.tokStr}/${wd.maxStr} │ ${wd.triggerLabel} │ ${C.cyan}${wd.modelStr}${C.reset} │ ${wd.chk} chk${wd.agentStr}${wd.turnStr}`, width),
530
+ spreadGroups(width, [
531
+ `${C.amber}⚡ ${wd.tierLabel}${C.reset} v${C.bold}${wd.version}${C.reset} ${ramp(wd.ctxPct, 20)} ${C.bold}${wd.pctStr}${C.reset} ${wd.tokStr}/${wd.maxStr}`,
532
+ wd.triggerLabel,
533
+ `${C.cyan}${wd.modelStr}${C.reset}`,
534
+ `${wd.chk} chk${wd.agentStr}${wd.turnStr}`,
535
+ ]),
611
536
  // L2 — savings reconciled (session + all-time)
612
- panelLine(` ${C.magenta}dup ${wd.dedupStr}${C.reset} │ ${C.gray}sess${C.reset} ${fmtTokens(wd.sessIn)}→${fmtTokens(wd.sessKept)} kept ${C.green}(${wd.sTxt}% freed)${C.reset} · ${C.gray}all-time${C.reset} ${fmtTokens(wd.repoIn)}→${fmtTokens(wd.repoKept)} kept ${C.blue}(${wd.rTxt}% freed)${C.reset} │ ${wd.repoChk} chk/${wd.repoSess} sess`, width),
537
+ spreadGroups(width, [
538
+ `${C.magenta}dup ${wd.dedupStr}${C.reset}`,
539
+ `${C.gray}sess${C.reset} ${fmtTokens(wd.sessIn)}→${fmtTokens(wd.sessKept)} kept ${C.green}(${wd.sTxt}% freed)${C.reset} · ${C.gray}all-time${C.reset} ${fmtTokens(wd.repoIn)}→${fmtTokens(wd.repoKept)} kept ${C.blue}(${wd.rTxt}% freed)${C.reset}`,
540
+ `${wd.repoChk} chk/${wd.repoSess} sess`,
541
+ ]),
613
542
  // L3 — memory store + compression + drift + since-compact (NEW)
614
- panelLine(` ${C.gray}mem${C.reset} ${wd.embedderName} · ${wd.chk} chunks · ${C.blue}comp ${wd.compStr}${C.reset} │ ${C.gray}drift${C.reset} ${wd.driftStatus === "ok" ? C.green : C.amber}${wd.driftStatus}${C.reset} │ ${C.gray}compact${C.reset} ${sinceCompactStr(wd.sinceCompact)}`, width),
543
+ spreadGroups(width, [
544
+ `${C.gray}mem${C.reset} ${wd.embedderName} · ${wd.chk} chunks · ${C.blue}comp ${wd.compStr}${C.reset}`,
545
+ `${C.gray}drift${C.reset} ${wd.driftStatus === "ok" ? C.green : C.amber}${wd.driftStatus}${C.reset}`,
546
+ `${C.gray}compact${C.reset} ${sinceCompactStr(wd.sinceCompact)}`,
547
+ ]),
615
548
  ];
616
549
  // L4 — agents block (S27, count + status; per-agent tokens gated on P0)
617
550
  if (wd.agentsActive) {
@@ -626,9 +559,7 @@ export class MegaRuntime {
626
559
  const idx = wd.ticker.length - 1 - (step % wd.ticker.length);
627
560
  const head = wd.ticker[idx].text;
628
561
  const why = wd.lastWhy ? ` ${C.gray}· ${wd.lastWhy}${C.reset}` : "";
629
- const more = wd.ticker.length > 1
630
- ? ` ${C.dim}(+${wd.ticker.length - 1} more)${C.reset}`
631
- : "";
562
+ const more = wd.ticker.length > 1 ? ` ${C.dim}(+${wd.ticker.length - 1} more)${C.reset}` : "";
632
563
  lines.push(panelLine(` ${wd.fresh ? C.teal : C.dim}${head}${why}${more}${C.reset}`, width));
633
564
  }
634
565
  else if (wd.pulsing) {
@@ -642,8 +573,7 @@ export class MegaRuntime {
642
573
  embedderName() {
643
574
  // MINILM_EMBEDDER flag lives in src/config/dedup.ts; read the same env var
644
575
  // the embedder factory uses so the label matches what's actually running.
645
- return process.env.MEGACOMPACT_MINILM === "true" ||
646
- process.env.MEGACOMPACT_MINILM === "1"
576
+ return process.env.MEGACOMPACT_MINILM === "true" || process.env.MEGACOMPACT_MINILM === "1"
647
577
  ? "MiniLM"
648
578
  : "Trigram";
649
579
  }
@@ -703,18 +633,13 @@ export class MegaRuntime {
703
633
  this.appendEvent("captureModel:no-model", { cwd: ctx.cwd });
704
634
  return;
705
635
  }
706
- if (this.currentModel &&
707
- this.currentModel.modelId === m.id &&
708
- this.currentModel.provider === m.provider)
636
+ if (this.currentModel && this.currentModel.modelId === m.id && this.currentModel.provider === m.provider)
709
637
  return;
710
638
  let providerName = null;
711
639
  try {
712
- providerName =
713
- ctx.modelRegistry?.getProviderDisplayName(m.provider) ?? null;
714
- }
715
- catch {
716
- /* optional */
640
+ providerName = ctx.modelRegistry?.getProviderDisplayName(m.provider) ?? null;
717
641
  }
642
+ catch { /* optional */ }
718
643
  const snap = {
719
644
  provider: m.provider,
720
645
  providerName,
@@ -736,18 +661,14 @@ export class MegaRuntime {
736
661
  try {
737
662
  recordModelSnapshot(repo, snap, this.currentStateDir);
738
663
  this.appendEvent("captureModel:recorded", {
739
- repo,
740
- modelId: snap.modelId,
741
- provider: snap.provider,
742
- inputRate: snap.inputRate,
743
- outputRate: snap.outputRate,
664
+ repo, modelId: snap.modelId, provider: snap.provider,
665
+ inputRate: snap.inputRate, outputRate: snap.outputRate,
744
666
  });
745
667
  }
746
668
  catch (e) {
747
669
  this.diagCaptureModelFails++;
748
670
  this.appendEvent("captureModel:record-failed", {
749
- repo,
750
- modelId: snap.modelId,
671
+ repo, modelId: snap.modelId,
751
672
  error: e instanceof Error ? e.message : String(e),
752
673
  stack: e instanceof Error ? e.stack : undefined,
753
674
  });
@@ -768,8 +689,7 @@ export class MegaRuntime {
768
689
  }
769
690
  catch (e) {
770
691
  this.appendEvent("captureModel:index-record-failed", {
771
- repo,
772
- modelId: snap.modelId,
692
+ repo, modelId: snap.modelId,
773
693
  error: e instanceof Error ? e.message : String(e),
774
694
  });
775
695
  }
@@ -785,9 +705,7 @@ export class MegaRuntime {
785
705
  mkdirSync(this.currentStateDir, { recursive: true });
786
706
  appendFileSync(join(this.currentStateDir, "events.log"), JSON.stringify({ ts: Date.now(), event, ...fields }) + "\n");
787
707
  }
788
- catch {
789
- /* non-fatal */
790
- }
708
+ catch { /* non-fatal */ }
791
709
  }
792
710
  /** S21: state dir of the currently bound repo (where memories live). */
793
711
  getStateDir() {
@@ -797,13 +715,10 @@ export class MegaRuntime {
797
715
  makeTierCallback(ctx) {
798
716
  const order = ["L0", "L1", "L2", "new"];
799
717
  const seen = new Map();
800
- const glyph = (status) => status === "deduped"
801
- ? `${C.green}✓${C.reset}`
802
- : status === "passed"
803
- ? `${C.dim}○${C.reset}`
804
- : status === "scanning"
805
- ? `${C.amber}…${C.reset}`
806
- : `${C.cyan}●${C.reset}`;
718
+ const glyph = (status) => status === "deduped" ? `${C.green}✓${C.reset}` :
719
+ status === "passed" ? `${C.dim}○${C.reset}` :
720
+ status === "scanning" ? `${C.amber}…${C.reset}` :
721
+ `${C.cyan}●${C.reset}`;
807
722
  return (ev) => {
808
723
  const label = ev.tier === "new"
809
724
  ? `${C.cyan}stored${C.reset}`
@@ -820,9 +735,7 @@ export class MegaRuntime {
820
735
  try {
821
736
  this.snapshot(ctx);
822
737
  }
823
- catch {
824
- /* non-fatal */
825
- }
738
+ catch { /* non-fatal */ }
826
739
  };
827
740
  }
828
741
  // Phase 3 — recall/activity ticker ring buffer.