pi-mega-compact 0.4.12 β†’ 0.4.13

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.
@@ -255,25 +255,43 @@ export default function (pi) {
255
255
  const usedStr = `${C.cyan}${fmt(st.totalTokenEstimate)} sess${C.reset} / ${C.blue}${fmt(repo.totalTokenEstimate)} repo${C.reset}`;
256
256
  const agentStr = activeAgents > 0 ? ` β”‚ πŸ€– ${activeAgents} agent${activeAgents === 1 ? "" : "s"}` : "";
257
257
  const turnStr = currentTurn > 0 ? ` β”‚ turn ${currentTurn}` : "";
258
+ // Phase 3 β€” pulsing status glyph while a compaction is in flight.
259
+ const pulse = pulsing ? `${C.cyan}${PULSE[Math.floor(Date.now() / 250) % PULSE.length]}${C.reset} ` : "";
258
260
  const lines = [
259
- ` ${C.amber}⚑ ${config.tier}${C.reset} β”‚ ${tokStr}/${maxStr} tokens (${C.bold}${pctStr}${C.reset}) β”‚ ${st.checkpointCount} chkpt${st.checkpointCount === 1 ? "" : "s"}${agentStr}${turnStr}`,
261
+ ` ${C.amber}⚑ ${config.tier}${C.reset} β”‚ ${tokStr}/${maxStr} tokens (${C.bold}${pctStr}${C.reset}) β”‚ ${st.checkpointCount} chkpt${agentStr}${turnStr}`,
260
262
  ` ${triggerLabel} β”‚ ${C.magenta}dedup: ${dedupStr}${C.reset} β”‚ ${C.gray}used:${C.reset} ${usedStr} β”‚ ${C.gray}saved:${C.reset} ${savedStr}`,
261
263
  ];
264
+ // Phase 3 β€” compact progress bar: session tokens saved toward the rolling goal.
265
+ if (rt.tokensSaved > 0) {
266
+ const goal = Math.max(savedGoal, 1);
267
+ const pct = Math.min(100, Math.round((rt.tokensSaved / goal) * 100));
268
+ const filled = Math.round((pct / 100) * 10);
269
+ const bar = "β–“".repeat(filled) + "β–‘".repeat(10 - filled);
270
+ lines.push(` ${C.green}saved ${fmt(rt.tokensSaved)} ${bar}${C.reset} ${pct}% of ${fmt(goal)}`);
271
+ }
262
272
  // Live "now processing" line β€” teal while fresh (≀4s), then the last-seen
263
273
  // action keeps the widget lively. Cleared on session reset.
264
- if (tierTrace) {
265
- // Per-tier dedup progress is more relevant than the last action while a
266
- // compaction is mid-flight, so prefer it when fresh.
267
- const fresh = Date.now() - lastActivityAt < 4000;
268
- if (fresh)
269
- lines.push(` ${tierTrace}`);
270
- else if (currentActivity)
271
- lines.push(` ${C.dim}${currentActivity}${C.reset}`);
274
+ const fresh = Date.now() - lastActivityAt < 4000;
275
+ if (tierTrace && fresh) {
276
+ lines.push(` ${pulse}${tierTrace}`);
272
277
  }
273
278
  else if (currentActivity) {
274
- const fresh = Date.now() - lastActivityAt < 4000;
275
279
  lines.push(` ${fresh ? C.teal : C.dim}${currentActivity}${C.reset}`);
276
280
  }
281
+ else if (pulsing) {
282
+ lines.push(` ${pulse}${C.teal}compacting…${C.reset}`);
283
+ }
284
+ // Phase 3 β€” explain-why line (fresh only).
285
+ if (lastWhy && fresh)
286
+ lines.push(` ${C.gray}${lastWhy}${C.reset}`);
287
+ // Phase 3 β€” recall/activity ticker (most-recent first), fresh only.
288
+ if (fresh) {
289
+ for (let i = ticker.length - 1; i >= 0; i--) {
290
+ if (lines.length >= 10)
291
+ break; // MAX_WIDGET_LINES guard
292
+ lines.push(` ${i === ticker.length - 1 ? "" : C.dim}${ticker[i].text}${C.reset}`);
293
+ }
294
+ }
277
295
  ctx.ui.setWidget(WIDGET_KEY, lines, { placement: "aboveEditor" });
278
296
  }
279
297
  }
@@ -305,6 +323,24 @@ export default function (pi) {
305
323
  // Built from the store's sync onTier callback during a compaction so the user
306
324
  // watches each tier evaluate in real time. Cleared once the outcome settles.
307
325
  let tierTrace;
326
+ const ticker = [];
327
+ const TICKER_MAX = 5;
328
+ function pushTicker(text) {
329
+ ticker.push({ text, at: Date.now() });
330
+ while (ticker.length > TICKER_MAX)
331
+ ticker.shift();
332
+ lastActivityAt = Date.now();
333
+ }
334
+ // Pulsing status: set true while a compaction is in flight, cleared on result.
335
+ let pulsing = false;
336
+ // Rolling "saved" goal for the progress bar β€” grows as we save more, so the
337
+ // bar always has a meaningful denominator (never sits at 100% forever).
338
+ let savedGoal = 50_000;
339
+ // Last explain-why line (dedup reason / anchor-kept / superseded), surfaced
340
+ // while fresh.
341
+ let lastWhy = undefined;
342
+ // Cycling glyph phases for the pulsing status.
343
+ const PULSE = ["◐", "β—“", "β—‘", "β—’"];
308
344
  // ANSI palette for the toolbar. The pi TUI's Text component preserves ANSI
309
345
  // escape codes (see wrapTextWithAnsi), so raw escapes render as colors. No
310
346
  // chalk dependency needed β€” these are just strings.
@@ -344,6 +380,10 @@ export default function (pi) {
344
380
  currentActivity = undefined;
345
381
  lastActivityAt = 0;
346
382
  tierTrace = undefined;
383
+ ticker.length = 0;
384
+ pulsing = false;
385
+ savedGoal = 50_000;
386
+ lastWhy = undefined;
347
387
  }
348
388
  /** Build the sync onTier callback that paints the live per-tier trace. */
349
389
  function makeTierCallback(ctx) {
@@ -382,6 +422,7 @@ export default function (pi) {
382
422
  const keepFrom = opts.keepFrom ?? Math.max(0, view.length - config.preserveRecent);
383
423
  if (keepFrom <= 0)
384
424
  return { skipped: true };
425
+ pulsing = true; // animate the status line while the (sync) pipeline runs
385
426
  const result = compactSession({
386
427
  sessionId: sid,
387
428
  messages: view,
@@ -390,6 +431,7 @@ export default function (pi) {
390
431
  timestamp: Date.now(),
391
432
  onTier: makeTierCallback(ctx),
392
433
  }, store);
434
+ pulsing = false;
393
435
  if (result.skipped)
394
436
  return { skipped: true };
395
437
  if (!result.deduped) {
@@ -410,6 +452,10 @@ export default function (pi) {
410
452
  rt.tokensSaved += saved;
411
453
  if (result.deduped)
412
454
  rt.dedupSkips++;
455
+ // Grow the rolling "saved" goal so the progress bar always has a fresh
456
+ // denominator (we don't want it pinned at 100% once we pass an old target).
457
+ if (rt.tokensSaved > savedGoal)
458
+ savedGoal = Math.ceil((rt.tokensSaved * 1.25) / 10_000) * 10_000;
413
459
  // Live toolbar "now processing" line: what file/region just got compacted or
414
460
  // deduped. Reset to the last-seen action after a few seconds (see snapshot).
415
461
  const files = result.filesModified ?? [];
@@ -420,6 +466,16 @@ export default function (pi) {
420
466
  ? `β™» deduped ${fileLabel}`
421
467
  : `πŸ—œ compacted ${result.checkpointId} Β· ${fileLabel}`;
422
468
  lastActivityAt = Date.now();
469
+ // Explain-why line: surfaced while fresh. Pulls the dedup reason (which for
470
+ // L2 includes the cosine sim) so the user sees WHY a region was kept/dropped.
471
+ lastWhy = result.deduped
472
+ ? `why: deduped@${result.dedupReason ?? "tier"}`
473
+ : `why: compacted β†’ ${result.checkpointId}`;
474
+ // Recall/activity ticker: record this event in the ring buffer.
475
+ const savedK = (saved / 1000).toFixed(1);
476
+ pushTicker(result.deduped
477
+ ? `${C.green}β™»${C.reset} deduped ${fileLabel} Β· ${savedK}k saved`
478
+ : `${C.cyan}πŸ—œ${C.reset} ${result.checkpointId} Β· +${savedK}k Β· ${fileLabel}`);
423
479
  // The per-tier trace has settled into the final outcome β€” fold it back into
424
480
  // the activity line and stop showing the live trace.
425
481
  tierTrace = undefined;
@@ -472,6 +528,14 @@ export default function (pi) {
472
528
  const sid = normalizeSessionId(ctx.sessionManager.getSessionId());
473
529
  const result = recallAndInline({ sessionId: sid, query, limit: config.autoInlineK, source, skipInjected: true }, store);
474
530
  dashboard.event("recall", { source, query: query.slice(0, 120), injected: result.toInject.length, empty: result.empty });
531
+ if (!result.empty && result.toInject.length > 0) {
532
+ const top = result.toInject[0];
533
+ const scorePct = Math.round((top.score ?? 0) * 100);
534
+ const files = top.checkpoint.filesModified ?? [];
535
+ const label = files.length ? files.map((f) => f.split("/").pop() ?? f).slice(0, 2).join(", ") : top.checkpoint.checkpointId;
536
+ pushTicker(`${C.amber}↩${C.reset} recalled ${top.checkpoint.checkpointId} Β· ${scorePct}% Β· ${label}`);
537
+ lastWhy = `why: recalled@${scorePct}% (${result.toInject.length} chkpt)`;
538
+ }
475
539
  return result;
476
540
  }
477
541
  // ---- Session lifecycle (state reset points) -------------------------------
@@ -358,21 +358,38 @@ export default function (pi: ExtensionAPI) {
358
358
  const usedStr = `${C.cyan}${fmt(st.totalTokenEstimate)} sess${C.reset} / ${C.blue}${fmt(repo.totalTokenEstimate)} repo${C.reset}`;
359
359
  const agentStr = activeAgents > 0 ? ` β”‚ πŸ€– ${activeAgents} agent${activeAgents === 1 ? "" : "s"}` : "";
360
360
  const turnStr = currentTurn > 0 ? ` β”‚ turn ${currentTurn}` : "";
361
+ // Phase 3 β€” pulsing status glyph while a compaction is in flight.
362
+ const pulse = pulsing ? `${C.cyan}${PULSE[Math.floor(Date.now() / 250) % PULSE.length]}${C.reset} ` : "";
361
363
  const lines = [
362
- ` ${C.amber}⚑ ${config.tier}${C.reset} β”‚ ${tokStr}/${maxStr} tokens (${C.bold}${pctStr}${C.reset}) β”‚ ${st.checkpointCount} chkpt${st.checkpointCount === 1 ? "" : "s"}${agentStr}${turnStr}`,
364
+ ` ${C.amber}⚑ ${config.tier}${C.reset} β”‚ ${tokStr}/${maxStr} tokens (${C.bold}${pctStr}${C.reset}) β”‚ ${st.checkpointCount} chkpt${agentStr}${turnStr}`,
363
365
  ` ${triggerLabel} β”‚ ${C.magenta}dedup: ${dedupStr}${C.reset} β”‚ ${C.gray}used:${C.reset} ${usedStr} β”‚ ${C.gray}saved:${C.reset} ${savedStr}`,
364
366
  ];
367
+ // Phase 3 β€” compact progress bar: session tokens saved toward the rolling goal.
368
+ if (rt.tokensSaved > 0) {
369
+ const goal = Math.max(savedGoal, 1);
370
+ const pct = Math.min(100, Math.round((rt.tokensSaved / goal) * 100));
371
+ const filled = Math.round((pct / 100) * 10);
372
+ const bar = "β–“".repeat(filled) + "β–‘".repeat(10 - filled);
373
+ lines.push(` ${C.green}saved ${fmt(rt.tokensSaved)} ${bar}${C.reset} ${pct}% of ${fmt(goal)}`);
374
+ }
365
375
  // Live "now processing" line β€” teal while fresh (≀4s), then the last-seen
366
376
  // action keeps the widget lively. Cleared on session reset.
367
- if (tierTrace) {
368
- // Per-tier dedup progress is more relevant than the last action while a
369
- // compaction is mid-flight, so prefer it when fresh.
370
- const fresh = Date.now() - lastActivityAt < 4000;
371
- if (fresh) lines.push(` ${tierTrace}`);
372
- else if (currentActivity) lines.push(` ${C.dim}${currentActivity}${C.reset}`);
377
+ const fresh = Date.now() - lastActivityAt < 4000;
378
+ if (tierTrace && fresh) {
379
+ lines.push(` ${pulse}${tierTrace}`);
373
380
  } else if (currentActivity) {
374
- const fresh = Date.now() - lastActivityAt < 4000;
375
381
  lines.push(` ${fresh ? C.teal : C.dim}${currentActivity}${C.reset}`);
382
+ } else if (pulsing) {
383
+ lines.push(` ${pulse}${C.teal}compacting…${C.reset}`);
384
+ }
385
+ // Phase 3 β€” explain-why line (fresh only).
386
+ if (lastWhy && fresh) lines.push(` ${C.gray}${lastWhy}${C.reset}`);
387
+ // Phase 3 β€” recall/activity ticker (most-recent first), fresh only.
388
+ if (fresh) {
389
+ for (let i = ticker.length - 1; i >= 0; i--) {
390
+ if (lines.length >= 10) break; // MAX_WIDGET_LINES guard
391
+ lines.push(` ${i === ticker.length - 1 ? "" : C.dim}${ticker[i].text}${C.reset}`);
392
+ }
376
393
  }
377
394
  ctx.ui.setWidget(WIDGET_KEY, lines, { placement: "aboveEditor" });
378
395
  }
@@ -406,6 +423,27 @@ export default function (pi: ExtensionAPI) {
406
423
  // Built from the store's sync onTier callback during a compaction so the user
407
424
  // watches each tier evaluate in real time. Cleared once the outcome settles.
408
425
  let tierTrace: string | undefined;
426
+ // Phase 3 β€” standout toolbar state.
427
+ // Recall/activity ticker: a small ring buffer (≀5) of recent compact/recall
428
+ // events so the widget shows a live history instead of a single last action.
429
+ interface TickerEntry { text: string; at: number; }
430
+ const ticker: TickerEntry[] = [];
431
+ const TICKER_MAX = 5;
432
+ function pushTicker(text: string): void {
433
+ ticker.push({ text, at: Date.now() });
434
+ while (ticker.length > TICKER_MAX) ticker.shift();
435
+ lastActivityAt = Date.now();
436
+ }
437
+ // Pulsing status: set true while a compaction is in flight, cleared on result.
438
+ let pulsing = false;
439
+ // Rolling "saved" goal for the progress bar β€” grows as we save more, so the
440
+ // bar always has a meaningful denominator (never sits at 100% forever).
441
+ let savedGoal = 50_000;
442
+ // Last explain-why line (dedup reason / anchor-kept / superseded), surfaced
443
+ // while fresh.
444
+ let lastWhy: string | undefined = undefined;
445
+ // Cycling glyph phases for the pulsing status.
446
+ const PULSE = ["◐", "β—“", "β—‘", "β—’"];
409
447
  // ANSI palette for the toolbar. The pi TUI's Text component preserves ANSI
410
448
  // escape codes (see wrapTextWithAnsi), so raw escapes render as colors. No
411
449
  // chalk dependency needed β€” these are just strings.
@@ -446,6 +484,10 @@ export default function (pi: ExtensionAPI) {
446
484
  currentActivity = undefined;
447
485
  lastActivityAt = 0;
448
486
  tierTrace = undefined;
487
+ ticker.length = 0;
488
+ pulsing = false;
489
+ savedGoal = 50_000;
490
+ lastWhy = undefined;
449
491
  }
450
492
 
451
493
  /** Build the sync onTier callback that paints the live per-tier trace. */
@@ -488,6 +530,7 @@ export default function (pi: ExtensionAPI) {
488
530
  const keepFrom = opts.keepFrom ?? Math.max(0, view.length - config.preserveRecent);
489
531
  if (keepFrom <= 0) return { skipped: true as const };
490
532
 
533
+ pulsing = true; // animate the status line while the (sync) pipeline runs
491
534
  const result = compactSession(
492
535
  {
493
536
  sessionId: sid,
@@ -499,6 +542,7 @@ export default function (pi: ExtensionAPI) {
499
542
  },
500
543
  store,
501
544
  );
545
+ pulsing = false;
502
546
 
503
547
  if (result.skipped) return { skipped: true as const };
504
548
  if (!result.deduped) {
@@ -518,6 +562,9 @@ export default function (pi: ExtensionAPI) {
518
562
  : Math.max(0, result.originalTokenEstimate - result.tokenEstimate);
519
563
  rt.tokensSaved += saved;
520
564
  if (result.deduped) rt.dedupSkips++;
565
+ // Grow the rolling "saved" goal so the progress bar always has a fresh
566
+ // denominator (we don't want it pinned at 100% once we pass an old target).
567
+ if (rt.tokensSaved > savedGoal) savedGoal = Math.ceil((rt.tokensSaved * 1.25) / 10_000) * 10_000;
521
568
 
522
569
  // Live toolbar "now processing" line: what file/region just got compacted or
523
570
  // deduped. Reset to the last-seen action after a few seconds (see snapshot).
@@ -529,6 +576,18 @@ export default function (pi: ExtensionAPI) {
529
576
  ? `β™» deduped ${fileLabel}`
530
577
  : `πŸ—œ compacted ${result.checkpointId} Β· ${fileLabel}`;
531
578
  lastActivityAt = Date.now();
579
+ // Explain-why line: surfaced while fresh. Pulls the dedup reason (which for
580
+ // L2 includes the cosine sim) so the user sees WHY a region was kept/dropped.
581
+ lastWhy = result.deduped
582
+ ? `why: deduped@${result.dedupReason ?? "tier"}`
583
+ : `why: compacted β†’ ${result.checkpointId}`;
584
+ // Recall/activity ticker: record this event in the ring buffer.
585
+ const savedK = (saved / 1000).toFixed(1);
586
+ pushTicker(
587
+ result.deduped
588
+ ? `${C.green}β™»${C.reset} deduped ${fileLabel} Β· ${savedK}k saved`
589
+ : `${C.cyan}πŸ—œ${C.reset} ${result.checkpointId} Β· +${savedK}k Β· ${fileLabel}`,
590
+ );
532
591
  // The per-tier trace has settled into the final outcome β€” fold it back into
533
592
  // the activity line and stop showing the live trace.
534
593
  tierTrace = undefined;
@@ -590,6 +649,14 @@ export default function (pi: ExtensionAPI) {
590
649
  store,
591
650
  );
592
651
  dashboard.event("recall", { source, query: query.slice(0, 120), injected: result.toInject.length, empty: result.empty });
652
+ if (!result.empty && result.toInject.length > 0) {
653
+ const top = result.toInject[0];
654
+ const scorePct = Math.round((top.score ?? 0) * 100);
655
+ const files = top.checkpoint.filesModified ?? [];
656
+ const label = files.length ? files.map((f) => f.split("/").pop() ?? f).slice(0, 2).join(", ") : top.checkpoint.checkpointId;
657
+ pushTicker(`${C.amber}↩${C.reset} recalled ${top.checkpoint.checkpointId} Β· ${scorePct}% Β· ${label}`);
658
+ lastWhy = `why: recalled@${scorePct}% (${result.toInject.length} chkpt)`;
659
+ }
593
660
  return result;
594
661
  }
595
662
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-mega-compact",
3
- "version": "0.4.12",
3
+ "version": "0.4.13",
4
4
  "description": "Layered, local, vector-backed context compressor for pi β€” supersede/collapse/cluster compaction with deduped inline recall.",
5
5
  "type": "module",
6
6
  "license": "BSD-2-Clause",