@bismawy/pi-arnative 0.3.9 → 0.4.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.
@@ -49,7 +49,16 @@ type TCtx = { isError?: boolean; toolCallId?: string; args?: any; state?: Record
49
49
  type Res = (r: TResult, th: Theme, soft: (s: string) => string, isErr: boolean, expanded: boolean) => string;
50
50
 
51
51
  const cwd = process.cwd();
52
- const TIMINGS = new Map<string, number>(); // duration ms per tool call (one run)
52
+ const MAX_TIMINGS = 200;
53
+ const TIMINGS = new Map<string, number>();
54
+
55
+ export function recordTiming(toolCallId: string, durationMs: number): void {
56
+ if (TIMINGS.size >= MAX_TIMINGS) {
57
+ const oldest = TIMINGS.keys().next().value;
58
+ if (oldest !== undefined) TIMINGS.delete(oldest);
59
+ }
60
+ TIMINGS.set(toolCallId, durationMs);
61
+ }
53
62
 
54
63
  // Running icon 󰔟 󱦠 󱦟: 500ms spin frames from a ticker that only runs while
55
64
  // a tool is active (idle = no wasteful re-renders).
@@ -586,7 +595,7 @@ function minimal(
586
595
  try {
587
596
  return await tool.execute(toolCallId, params, signal, onUpdate, ctx);
588
597
  } finally {
589
- TIMINGS.set(toolCallId, Math.round(performance.now() - start));
598
+ recordTiming(toolCallId, Math.round(performance.now() - start));
590
599
  ACTIVE--;
591
600
  syncTicker();
592
601
  }
@@ -659,6 +668,7 @@ function arnativeTools(pi: ExtensionAPI) {
659
668
 
660
669
  // Re-render source for the spinner + ticker cleanup when the session closes
661
670
  pi.on("session_start", async (_event, ctx) => {
671
+ TIMINGS.clear();
662
672
  requestRenderFn = () => {
663
673
  try {
664
674
  (ctx.ui as { requestRender?: () => void }).requestRender?.();
@@ -668,6 +678,7 @@ function arnativeTools(pi: ExtensionAPI) {
668
678
  };
669
679
  });
670
680
  pi.on("session_shutdown", async () => {
681
+ TIMINGS.clear();
671
682
  if (TICK) {
672
683
  clearInterval(TICK);
673
684
  TICK = null;
@@ -1369,5 +1380,11 @@ if (isMain(import.meta.url)) {
1369
1380
  assert(cdpExpanded.some((l) => l.includes('"pageId": "p1"')), "chrome-devtools: expanded shows the full tool output");
1370
1381
  assert(cdpCollapsed.some((l) => l.includes("navigated") && l.includes("to expand")), "chrome-devtools: collapsed shows the first line + hint");
1371
1382
  assert([...cdpCollapsed, ...cdpExpanded].every((l) => visibleWidth(l) === 74), "chrome-devtools: box fills the given width");
1383
+ // Capped timings map self-check
1384
+ for (let i = 0; i < 250; i++) recordTiming(`call_${i}`, i);
1385
+ assert(TIMINGS.size === MAX_TIMINGS, `TIMINGS size is bounded to ${MAX_TIMINGS}`);
1386
+ assert(!TIMINGS.has("call_0") && TIMINGS.has("call_249"), "TIMINGS evicts oldest entries");
1387
+ TIMINGS.clear();
1388
+
1372
1389
  console.log("OK");
1373
1390
  }
@@ -57,8 +57,9 @@ const RELOAD_TEXT = "Reloading keybindings, extensions, skills, prompts, themes,
57
57
  * whole chat the moment a transcript line mentioned the phrase.
58
58
  */
59
59
  export function shouldHideReloadBox(lines: readonly string[]): boolean {
60
+ if (!lines.some((l) => l.includes("Reloading keybindings"))) return false;
60
61
  const rows = lines.map((l) => stripAnsi(l).trim());
61
- return rows.some((r) => r.includes("Reloading keybindings")) && rows.every((r) => r === "" || /^[─-]+$/.test(r) || RELOAD_TEXT.includes(r));
62
+ return rows.every((r) => r === "" || /^[─-]+$/.test(r) || RELOAD_TEXT.includes(r));
62
63
  }
63
64
  const RELOAD_BOX_KEY = Symbol.for("pi-arnative.reloadBoxHidden");
64
65
  if (UserMessageComponent?.prototype && !(globalThis as Record<symbol, boolean>)[RELOAD_BOX_KEY]) {