@cesarus777/opencode-cgp 0.4.1 → 0.4.3

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/cli.js CHANGED
@@ -474,15 +474,109 @@ async function segments(directory) {
474
474
  return (await Promise.all(names.filter((n) => filename.test(n)).map(async (name) => ({
475
475
  name,
476
476
  time: Number(filename.exec(name)?.[1]),
477
- bytes: (await stat(join(directory, name))).size
477
+ ...fileIdentity(await stat(join(directory, name)))
478
478
  })))).sort((a, b) => a.time - b.time || a.name.localeCompare(b.name));
479
479
  }
480
+ function fileIdentity(info) {
481
+ return {
482
+ bytes: info.size,
483
+ dev: info.dev,
484
+ ino: info.ino,
485
+ mtimeMs: info.mtimeMs,
486
+ ctimeMs: info.ctimeMs
487
+ };
488
+ }
489
+ function scanScheduler(signal, options = {}) {
490
+ const now = options.now ?? (() => performance.now());
491
+ const pause = options.yield ?? (() => new Promise((done) => setTimeout(done, 0)));
492
+ let started = now();
493
+ return () => {
494
+ signal?.throwIfAborted();
495
+ if (now() - started >= 4)
496
+ return pause().then(() => {
497
+ signal?.throwIfAborted();
498
+ started = now();
499
+ });
500
+ };
501
+ }
502
+ function matchesHistory(value, filter) {
503
+ return !(filter.sessionId !== undefined && value.sessionId !== filter.sessionId && value.sessionId !== "__history__" || value.type !== "gap" && filter.agent !== undefined && (value.performance?.requestAgent ?? value.agent) !== filter.agent || value.type !== "gap" && filter.model !== undefined && (value.performance?.requestModel ?? value.model) !== filter.model || filter.from !== undefined && value.time < filter.from || filter.to !== undefined && value.time > filter.to);
504
+ }
505
+ async function scanHistorySegment(handle, size, filter = {}, options = {}, start = 0) {
506
+ const records = [];
507
+ let malformedRecords = 0, position = start, boundary = start;
508
+ let pending = Buffer.alloc(0);
509
+ const checkpoint = scanScheduler(filter.signal, options);
510
+ function parse(line) {
511
+ if (!line.length)
512
+ return;
513
+ options.onParse?.();
514
+ let value;
515
+ try {
516
+ value = JSON.parse(line.toString("utf8"));
517
+ } catch {
518
+ malformedRecords++;
519
+ return;
520
+ }
521
+ if (!isHistoryRecord(value)) {
522
+ malformedRecords++;
523
+ return;
524
+ }
525
+ if (matchesHistory(value, filter))
526
+ records.push(value);
527
+ }
528
+ while (position < size) {
529
+ {
530
+ const waiting = checkpoint();
531
+ if (waiting)
532
+ await waiting;
533
+ }
534
+ const buffer = Buffer.alloc(Math.min(options.chunkBytes ?? 65536, size - position));
535
+ const { bytesRead } = await handle.read(buffer, 0, buffer.length, position);
536
+ if (!bytesRead)
537
+ throw new Error("Archive changed during read");
538
+ options.onRead?.(bytesRead);
539
+ position += bytesRead;
540
+ const chunk = pending.length ? Buffer.concat([pending, buffer.subarray(0, bytesRead)]) : buffer.subarray(0, bytesRead);
541
+ let offset = 0;
542
+ for (;; ) {
543
+ const end = chunk.indexOf(10, offset);
544
+ if (end < 0)
545
+ break;
546
+ {
547
+ const waiting = checkpoint();
548
+ if (waiting)
549
+ await waiting;
550
+ }
551
+ parse(chunk.subarray(offset, end));
552
+ offset = end + 1;
553
+ }
554
+ pending = Buffer.from(chunk.subarray(offset));
555
+ boundary = position - pending.length;
556
+ }
557
+ const completeRecords = records.length, completeMalformed = malformedRecords;
558
+ {
559
+ const waiting = checkpoint();
560
+ if (waiting)
561
+ await waiting;
562
+ }
563
+ parse(pending);
564
+ filter.signal?.throwIfAborted();
565
+ return {
566
+ records,
567
+ malformedRecords,
568
+ boundary,
569
+ completeRecords,
570
+ completeMalformed
571
+ };
572
+ }
480
573
 
481
574
  class PerformanceHistory {
482
575
  path;
483
576
  config;
484
577
  now;
485
578
  report;
579
+ changed;
486
580
  pending = [];
487
581
  working;
488
582
  active;
@@ -492,11 +586,13 @@ class PerformanceHistory {
492
586
  dropped = 0;
493
587
  droppedTotal = 0;
494
588
  constructor(path, config, now = Date.now, report = () => {
589
+ }, changed = () => {
495
590
  }) {
496
591
  this.path = path;
497
592
  this.config = config;
498
593
  this.now = now;
499
594
  this.report = report;
595
+ this.changed = changed;
500
596
  }
501
597
  emit(record2) {
502
598
  if (!this.config.enabled || this.disabled)
@@ -545,6 +641,7 @@ class PerformanceHistory {
545
641
  if (file.time >= this.now() - this.config.retentionDays * 86400000 && total + incoming <= this.config.maxBytes)
546
642
  break;
547
643
  await unlink(join(this.path, file.name));
644
+ this.changed(file.name);
548
645
  if (file.name === this.active) {
549
646
  this.active = undefined;
550
647
  this.bytes = 0;
@@ -595,6 +692,7 @@ class PerformanceHistory {
595
692
  const file = await open(join(this.path, this.active), "a", 384);
596
693
  try {
597
694
  await file.writeFile(line);
695
+ this.changed(this.active);
598
696
  } finally {
599
697
  await file.close();
600
698
  }
@@ -649,7 +747,8 @@ function scalarPerformance(p) {
649
747
  }))
650
748
  };
651
749
  }
652
- async function readPerformanceHistory(directory, filter = {}) {
750
+ async function readPerformanceHistory(directory, filter = {}, options = {}) {
751
+ filter.signal?.throwIfAborted();
653
752
  const records = [];
654
753
  const files = await segments(directory);
655
754
  filter.signal?.throwIfAborted();
@@ -667,36 +766,16 @@ async function readPerformanceHistory(directory, filter = {}) {
667
766
  for (const file of selected) {
668
767
  filter.signal?.throwIfAborted();
669
768
  const handle = await open(join(directory, file.name), "r");
670
- let source;
671
769
  try {
672
- const buffer = Buffer.alloc(file.bytes);
673
- const { bytesRead } = await handle.read(buffer, 0, buffer.length, 0);
674
- source = buffer.subarray(0, bytesRead).toString("utf8");
770
+ const result = await scanHistorySegment(handle, file.bytes, filter, options);
771
+ for (const record2 of result.records)
772
+ records.push(record2);
773
+ malformedRecords += result.malformedRecords;
675
774
  } finally {
676
775
  await handle.close();
677
776
  }
678
- let scannedLines = 0;
679
- for (const line of source.split("\n")) {
680
- if (filter.signal && ++scannedLines % 256 === 0) {
681
- await new Promise((done) => setTimeout(done, 0));
682
- filter.signal.throwIfAborted();
683
- }
684
- if (!line)
685
- continue;
686
- try {
687
- const value = JSON.parse(line);
688
- if (!isHistoryRecord(value)) {
689
- malformedRecords++;
690
- continue;
691
- }
692
- if (filter.sessionId !== undefined && value.sessionId !== filter.sessionId && value.sessionId !== "__history__" || value.type !== "gap" && filter.agent !== undefined && (value.performance?.requestAgent ?? value.agent) !== filter.agent || value.type !== "gap" && filter.model !== undefined && (value.performance?.requestModel ?? value.model) !== filter.model || filter.from !== undefined && value.time < filter.from || filter.to !== undefined && value.time > filter.to)
693
- continue;
694
- records.push(value);
695
- } catch {
696
- malformedRecords++;
697
- }
698
- }
699
777
  }
778
+ filter.signal?.throwIfAborted();
700
779
  const metadata = {
701
780
  schema: 1,
702
781
  ...files[0] ? { retainedFrom: files[0].time } : {},
@@ -714,7 +793,13 @@ function isHistoryRecord(value) {
714
793
  if (!isRecord(value))
715
794
  return false;
716
795
  const r = value;
717
- const allowed = new Set([
796
+ return Object.keys(r).every((k) => historyFields.has(k)) && r.schema === 1 && typeof r.recordId === "string" && typeof r.time === "number" && Number.isSafeInteger(r.time) && r.time >= 0 && typeof r.sessionId === "string" && typeof r.type === "string" && ["sample", "request", "lifecycle", "gap", "terminal"].includes(r.type) && ["parentId", "agent", "model", "event"].every((k) => r[k] === undefined || typeof r[k] === "string") && (r.performance === undefined || isPerformanceSnapshot(r.performance)) && (r.droppedRecords === undefined || typeof r.droppedRecords === "number" && Number.isSafeInteger(r.droppedRecords) && r.droppedRecords >= 0);
797
+ }
798
+ var filename, historyFields;
799
+ var init_history = __esm(() => {
800
+ init_rpc();
801
+ filename = /^([0-9]+)-[a-f0-9-]+\.jsonl$/;
802
+ historyFields = new Set([
718
803
  "schema",
719
804
  "recordId",
720
805
  "time",
@@ -727,12 +812,6 @@ function isHistoryRecord(value) {
727
812
  "performance",
728
813
  "droppedRecords"
729
814
  ]);
730
- return Object.keys(r).every((k) => allowed.has(k)) && r.schema === 1 && typeof r.recordId === "string" && typeof r.time === "number" && Number.isSafeInteger(r.time) && r.time >= 0 && typeof r.sessionId === "string" && typeof r.type === "string" && ["sample", "request", "lifecycle", "gap", "terminal"].includes(r.type) && ["parentId", "agent", "model", "event"].every((k) => r[k] === undefined || typeof r[k] === "string") && (r.performance === undefined || isPerformanceSnapshot(r.performance)) && (r.droppedRecords === undefined || typeof r.droppedRecords === "number" && Number.isSafeInteger(r.droppedRecords) && r.droppedRecords >= 0);
731
- }
732
- var filename;
733
- var init_history = __esm(() => {
734
- init_rpc();
735
- filename = /^([0-9]+)-[a-f0-9-]+\.jsonl$/;
736
815
  });
737
816
 
738
817
  // src/dashboard/command.ts
@@ -1261,7 +1340,7 @@ function validateInventoryLimits(limits) {
1261
1340
  if (!Number.isInteger(limits.maxNodes) || limits.maxNodes < 1 || limits.maxNodes > 2048 || !Number.isInteger(limits.maxPages) || limits.maxPages < 1 || limits.maxPages > 100 || !Number.isInteger(limits.maxDepth) || limits.maxDepth < 0 || limits.maxDepth > 64)
1262
1341
  throw new Error("Invalid dashboard inventory limits");
1263
1342
  }
1264
- async function discoverInventory(port, signal, limits = inventoryLimits) {
1343
+ async function discoverInventory(port, signal, limits = inventoryLimits, progress) {
1265
1344
  validateInventoryLimits(limits);
1266
1345
  const sessions = new Map;
1267
1346
  const notices = new Set;
@@ -1269,59 +1348,75 @@ async function discoverInventory(port, signal, limits = inventoryLimits) {
1269
1348
  let pages = 0;
1270
1349
  while (queue.length && pages < limits.maxPages) {
1271
1350
  signal.throwIfAborted();
1272
- const branch = queue.shift();
1273
- if (!branch)
1274
- break;
1275
- try {
1276
- pages++;
1277
- const page = await port.listSessions({
1278
- parentID: branch.parent,
1279
- limit: 100,
1280
- ...branch.cursor ? { cursor: branch.cursor } : {}
1281
- }, signal);
1282
- signal.throwIfAborted();
1283
- for (const session of page.data) {
1284
- if (session.directory !== port.location.directory) {
1285
- notices.add("foreign-location");
1286
- continue;
1287
- }
1288
- if ((session.parentId ?? null) !== branch.parent || sessions.has(session.sessionId)) {
1289
- notices.add("invalid-hierarchy");
1290
- continue;
1351
+ const batch = queue.splice(0, Math.min(queue[0]?.parent === null ? 1 : 4, limits.maxPages - pages));
1352
+ pages += batch.length;
1353
+ const results = await Promise.allSettled(batch.map(async (branch) => port.listSessions({
1354
+ parentID: branch.parent,
1355
+ limit: 100,
1356
+ ...branch.cursor ? { cursor: branch.cursor } : {}
1357
+ }, signal)));
1358
+ signal.throwIfAborted();
1359
+ for (const [index, branch] of batch.entries()) {
1360
+ try {
1361
+ const result = results[index];
1362
+ if (!result || result.status === "rejected")
1363
+ throw new Error("Inventory unavailable");
1364
+ const page = result.value;
1365
+ signal.throwIfAborted();
1366
+ for (const session of page.data) {
1367
+ if (session.directory !== port.location.directory) {
1368
+ notices.add("foreign-location");
1369
+ continue;
1370
+ }
1371
+ if ((session.parentId ?? null) !== branch.parent || sessions.has(session.sessionId)) {
1372
+ notices.add("invalid-hierarchy");
1373
+ continue;
1374
+ }
1375
+ if (sessions.size >= limits.maxNodes) {
1376
+ notices.add("node-limit");
1377
+ return {
1378
+ sessions: [...sessions.values()],
1379
+ complete: false,
1380
+ pages,
1381
+ notices: [...notices]
1382
+ };
1383
+ }
1384
+ sessions.set(session.sessionId, session);
1385
+ if (branch.depth < limits.maxDepth)
1386
+ queue.push({
1387
+ parent: session.sessionId,
1388
+ depth: branch.depth + 1,
1389
+ cursors: []
1390
+ });
1391
+ else
1392
+ notices.add("depth-limit");
1291
1393
  }
1292
- if (sessions.size >= limits.maxNodes) {
1293
- notices.add("node-limit");
1294
- return {
1295
- sessions: [...sessions.values()],
1296
- complete: false,
1297
- pages,
1298
- notices: [...notices]
1299
- };
1394
+ const cursor = page.cursor?.next;
1395
+ if (cursor) {
1396
+ if (cursor === branch.cursor || branch.cursors.includes(cursor))
1397
+ notices.add("repeated-cursor");
1398
+ else
1399
+ queue.unshift({
1400
+ ...branch,
1401
+ cursor,
1402
+ cursors: [...branch.cursors, cursor]
1403
+ });
1300
1404
  }
1301
- sessions.set(session.sessionId, session);
1302
- if (branch.depth < limits.maxDepth)
1303
- queue.push({
1304
- parent: session.sessionId,
1305
- depth: branch.depth + 1,
1306
- cursors: []
1307
- });
1308
- else
1309
- notices.add("depth-limit");
1405
+ } catch {
1406
+ signal.throwIfAborted();
1407
+ notices.add("inventory-unavailable");
1310
1408
  }
1311
- const cursor = page.cursor?.next;
1312
- if (cursor) {
1313
- if (cursor === branch.cursor || branch.cursors.includes(cursor))
1314
- notices.add("repeated-cursor");
1315
- else
1316
- queue.unshift({
1317
- ...branch,
1318
- cursor,
1319
- cursors: [...branch.cursors, cursor]
1320
- });
1409
+ }
1410
+ if (progress) {
1411
+ try {
1412
+ progress({
1413
+ sessions: [...sessions.values()],
1414
+ complete: false,
1415
+ pages,
1416
+ notices: [...notices]
1417
+ });
1418
+ } catch {
1321
1419
  }
1322
- } catch {
1323
- signal.throwIfAborted();
1324
- notices.add("inventory-unavailable");
1325
1420
  }
1326
1421
  }
1327
1422
  if (queue.length)
@@ -1587,16 +1682,13 @@ var init_models = __esm(() => {
1587
1682
 
1588
1683
  // src/dashboard/view.ts
1589
1684
  function immutableView(value) {
1590
- const copy = structuredClone(value);
1591
- const freeze = (item) => {
1592
- if (item === null || typeof item !== "object")
1593
- return;
1594
- for (const child of Object.values(item))
1595
- freeze(child);
1596
- Object.freeze(item);
1597
- };
1598
- freeze(copy);
1599
- return copy;
1685
+ if (value === null || typeof value !== "object" || owned.has(value))
1686
+ return value;
1687
+ const copy = Object.assign(Array.isArray(value) ? [] : {}, value);
1688
+ for (const key in copy)
1689
+ copy[key] = immutableView(copy[key]);
1690
+ owned.add(copy);
1691
+ return Object.freeze(copy);
1600
1692
  }
1601
1693
  function projectHistory(result, filters) {
1602
1694
  const match = (point) => (!filters.agent || point.agent === filters.agent) && (!filters.model || point.model === filters.model);
@@ -1648,13 +1740,15 @@ function projectHistory(result, filters) {
1648
1740
  notices
1649
1741
  };
1650
1742
  }
1651
- var historyWindows;
1743
+ var historyWindows, owned;
1652
1744
  var init_view = __esm(() => {
1653
1745
  historyWindows = [300000, 3600000, 86400000, 604800000];
1746
+ owned = new WeakSet;
1654
1747
  });
1655
1748
 
1656
1749
  // src/dashboard/controller.ts
1657
1750
  class DashboardController {
1751
+ snapshots = new WeakMap;
1658
1752
  locations = new Map;
1659
1753
  cache = new Map;
1660
1754
  listeners = new Set;
@@ -1860,7 +1954,33 @@ class DashboardController {
1860
1954
  signal,
1861
1955
  AbortSignal.timeout(15000)
1862
1956
  ]);
1863
- const result = await discoverInventory(state.port, scanSignal, this.limits);
1957
+ const result = await discoverInventory(state.port, scanSignal, this.limits, (partial) => {
1958
+ if (!this.current(state, generation, scanSignal))
1959
+ return;
1960
+ for (const session of partial.sessions) {
1961
+ const previous = state.nodes.get(session.sessionId);
1962
+ if (!previous && state.nodes.size >= this.limits.maxNodes)
1963
+ continue;
1964
+ state.nodes.set(session.sessionId, {
1965
+ ...session,
1966
+ status: previous?.status ?? "unknown",
1967
+ inventoryStale: true
1968
+ });
1969
+ }
1970
+ state.complete = false;
1971
+ state.stale = true;
1972
+ state.pages = partial.pages;
1973
+ state.notices = partial.notices;
1974
+ if (!state.selected) {
1975
+ const first = state.nodes.keys().next().value;
1976
+ if (first !== undefined) {
1977
+ state.selected = first;
1978
+ this.queueHistory();
1979
+ }
1980
+ }
1981
+ state.client?.setIds([...state.nodes.keys()]);
1982
+ this.publish();
1983
+ });
1864
1984
  let active;
1865
1985
  try {
1866
1986
  active = new Set(await state.port.activeSessions(result.sessions.map((session) => session.sessionId), scanSignal));
@@ -1935,7 +2055,14 @@ class DashboardController {
1935
2055
  return;
1936
2056
  state.connection = client.connection;
1937
2057
  if (client.health) {
1938
- state.rows = new Map(client.rows);
2058
+ state.rows = new Map([...client.rows].map(([id, row2]) => {
2059
+ let snapshot = this.snapshots.get(row2);
2060
+ if (!snapshot) {
2061
+ snapshot = immutableView(row2);
2062
+ this.snapshots.set(row2, snapshot);
2063
+ }
2064
+ return [id, snapshot];
2065
+ }));
1939
2066
  state.health = client.health;
1940
2067
  state.serverAt = client.now();
1941
2068
  state.receivedAt = this.elapsed();
@@ -2645,7 +2772,7 @@ function performanceLines(row2, width) {
2645
2772
  return [];
2646
2773
  const value = (n) => n === undefined ? "?" : n.toFixed(1);
2647
2774
  return [
2648
- ...wrap(`TPS ${p.status === "waiting" ? "waiting" : `${p.status === "streaming" ? "~" : ""}${value(p.liveTps)}`} \xB7 ${p.status}${p.partial ? " \xB7 partial" : ""}`, width),
2775
+ ...wrap(`Live TPS ${p.status === "waiting" ? "waiting" : `${p.status === "streaming" ? "~" : ""}${value(p.liveTps)}`} \xB7 ${p.status}${p.partial ? " \xB7 partial" : ""}`, width),
2649
2776
  ...wrap(`Avg model ${value(p.averageModelTps)} \xB7 Avg session ${value(p.averageSessionTps)}`, width),
2650
2777
  ...wrap(`TTFT ${p.ttftMs === undefined ? "?" : `~${(p.ttftMs / 1000).toFixed(2)}s`}${p.latestRequestTps === undefined ? "" : ` \xB7 Last ${value(p.latestRequestTps)} t/s`}`, width)
2651
2778
  ];
@@ -2920,6 +3047,7 @@ function panelRowLines(row2, width, selected) {
2920
3047
  `${pad(identity, Math.max(1, width - status.length - 1))} ${status}`,
2921
3048
  clip(`${percent} context \xB7 ${distance}`, width)
2922
3049
  ];
3050
+ lines.push(...performanceLines(row2, width));
2923
3051
  const exception = [
2924
3052
  freshnessLabel(row2),
2925
3053
  compactionLabel(row2),
@@ -2968,12 +3096,30 @@ function overviewLabels(rows) {
2968
3096
  counts.set(suffix, (counts.get(suffix) ?? 0) + 1);
2969
3097
  return suffixes.map((suffix, index) => counts.get(suffix) === 1 ? suffix : `#${index + 1}`);
2970
3098
  }
2971
- function overviewRowLines(row2, width, selected, label, position, maxLines = 3) {
3099
+ function overviewMetrics(row2) {
2972
3100
  const valid2 = row2.freshness === "fresh" || row2.freshness === "stale";
2973
3101
  const percent = `${valid2 && row2.snapshot?.measurement?.confidence === "approximate" ? "~" : ""}${valid2 && row2.utilizationPercent !== undefined ? Math.round(row2.utilizationPercent) : "?"}%`;
2974
3102
  const context = `${row2.freshness === "stale" ? "Last measured " : ""}${percent}`;
2975
3103
  const distance = thresholdDistance(row2, true);
2976
3104
  const threshold = (distance === "At threshold \xB7 requires more" ? "At threshold; requires more" : distance).replace(" \xB7 Threshold reached", " \xB7 reached");
3105
+ const metric2 = `${context} context \xB7 ${threshold}`;
3106
+ const shortThreshold = threshold.replace("pressure unknown", "?").replace("At threshold; requires more", "At; needs more").replace(" \xB7 Near threshold", " near").replace(" \xB7 reached", " reached").replace("Threshold reached", "Reached");
3107
+ const shortMetric = `${context} \xB7 T ${shortThreshold}`;
3108
+ const pressure = row2.freshness !== "fresh" || row2.pressure === "unknown" ? "Threshold ?" : row2.pressure === "crossed" ? "Reached" : row2.triggerDeltaTokens === 0 ? "At; needs more" : row2.pressure === "near" ? "Near threshold" : "Below threshold";
3109
+ const metrics = [metric2, shortMetric, `${context} \xB7 ${pressure}`];
3110
+ return { percent, context, threshold, pressure, metrics };
3111
+ }
3112
+ function overviewRowHeight(row2, width, maxLines) {
3113
+ const base = width >= 72 ? 1 : 2;
3114
+ const exception = row2.freshness !== "fresh" || row2.node.status === "failed" || row2.node.interrupted || !!compactionLabel(row2) || row2.inventoryStale || row2.liveStale || row2.hidden > 0;
3115
+ if (exception)
3116
+ return Math.min(maxLines, base + 1);
3117
+ if (width < 72 && !overviewMetrics(row2).metrics.some((line) => cells(line) <= width))
3118
+ return Math.min(maxLines, base + 1);
3119
+ return Math.min(maxLines, base);
3120
+ }
3121
+ function overviewRowLines(row2, width, selected, label, position, maxLines = 3) {
3122
+ const { percent, context, threshold, pressure, metrics } = overviewMetrics(row2);
2977
3123
  const state = width < 28 ? {
2978
3124
  running: "RUN",
2979
3125
  idle: "IDLE",
@@ -2987,11 +3133,6 @@ function overviewRowLines(row2, width, selected, label, position, maxLines = 3)
2987
3133
  const indent = row2.depth && identityWidth >= 22 ? `${" ".repeat(Math.min(row2.depth, Math.max(0, Math.floor(identityWidth / 8) - 1)))}\u2514 ` : "";
2988
3134
  const name = `${row2.node.agent ?? "unknown agent"}${width >= 40 ? ` / ${row2.node.model ?? "unknown model"}` : ""}`;
2989
3135
  const identity = marker + indent + clip(name, Math.max(1, identityWidth - cells(marker + indent + tag))) + tag;
2990
- const metric2 = `${context} context \xB7 ${threshold}`;
2991
- const shortThreshold = threshold.replace("pressure unknown", "?").replace("At threshold; requires more", "At; needs more").replace(" \xB7 Near threshold", " near").replace(" \xB7 reached", " reached").replace("Threshold reached", "Reached");
2992
- const shortMetric = `${context} \xB7 T ${shortThreshold}`;
2993
- const pressure = row2.freshness !== "fresh" || row2.pressure === "unknown" ? "Threshold ?" : row2.pressure === "crossed" ? "Reached" : row2.triggerDeltaTokens === 0 ? "At; needs more" : row2.pressure === "near" ? "Near threshold" : "Below threshold";
2994
- const metrics = [metric2, shortMetric, `${context} \xB7 ${pressure}`];
2995
3136
  const fittedMetric = metrics.find((line) => cells(line) <= width);
2996
3137
  const lines = width >= 72 ? [
2997
3138
  overviewColumns(identity, state, row2.freshness === "stale" ? "Last" : percent, threshold, width)
@@ -3234,6 +3375,7 @@ import { isAbsolute as isAbsolute3 } from "path";
3234
3375
  class TerminalShell {
3235
3376
  controller;
3236
3377
  quit;
3378
+ work = { formattedOverviewRows: 0 };
3237
3379
  scroll = 0;
3238
3380
  metric = 0;
3239
3381
  sample = 0;
@@ -3249,6 +3391,9 @@ class TerminalShell {
3249
3391
  rowOffset = 0;
3250
3392
  overviewScope = "";
3251
3393
  overviewPage = 1;
3394
+ labelKey = "";
3395
+ labels = [];
3396
+ layouts = new Map;
3252
3397
  historyIdentity = "";
3253
3398
  constructor(controller, quit) {
3254
3399
  this.controller = controller;
@@ -3504,27 +3649,75 @@ class TerminalShell {
3504
3649
  const noticeBudget = Math.min(notices.length, Math.floor((available - heading.length) / 4));
3505
3650
  const status = notices.slice(0, noticeBudget).map((notice, index) => clip(index === noticeBudget - 1 && notices.length > noticeBudget ? `${notices.length} notices \xB7 see Details` : notice, width));
3506
3651
  const budget = Math.max(1, available - heading.length - status.length);
3507
- const labels = overviewLabels(view.rows);
3508
- const rows = view.rows.map((row2, index) => overviewRowLines(row2, width, row2.node.sessionId === view.selectedSessionId, labels[index] ?? `#${index + 1}`, index, budget));
3652
+ const labelKey = view.rows.map((row2) => row2.node.sessionId).join("\0");
3653
+ if (labelKey !== this.labelKey) {
3654
+ this.labels = overviewLabels(view.rows);
3655
+ this.labelKey = labelKey;
3656
+ }
3657
+ const ids = new Set(view.rows.map((row2) => row2.node.sessionId));
3658
+ for (const id of this.layouts.keys())
3659
+ if (!ids.has(id))
3660
+ this.layouts.delete(id);
3661
+ const heights = view.rows.map((row2) => {
3662
+ const key = JSON.stringify([
3663
+ width,
3664
+ budget,
3665
+ row2.node,
3666
+ row2.depth,
3667
+ row2.freshness,
3668
+ row2.pressure,
3669
+ row2.triggerDeltaTokens,
3670
+ row2.utilizationPercent,
3671
+ row2.snapshot?.measurement?.confidence,
3672
+ row2.snapshot?.compaction,
3673
+ row2.inventoryStale,
3674
+ row2.liveStale,
3675
+ row2.hidden
3676
+ ]);
3677
+ let layout = this.layouts.get(row2.node.sessionId);
3678
+ if (!layout || layout.key !== key) {
3679
+ layout = { key, height: overviewRowHeight(row2, width, budget) };
3680
+ this.layouts.set(row2.node.sessionId, layout);
3681
+ }
3682
+ return layout.height;
3683
+ });
3509
3684
  const selected = view.rows.findIndex((row2) => row2.node.sessionId === view.selectedSessionId);
3510
- const start = rows.slice(0, Math.max(0, selected)).reduce((n, row2) => n + row2.length, 0);
3511
- const end = start + (rows[selected]?.length ?? 0);
3685
+ const start = heights.slice(0, Math.max(0, selected)).reduce((n, h) => n + h, 0);
3686
+ const end = start + (heights[selected] ?? 0);
3512
3687
  if (start < this.rowOffset)
3513
3688
  this.rowOffset = start;
3514
3689
  if (end > this.rowOffset + budget)
3515
3690
  this.rowOffset = Math.max(start, end - budget);
3516
- const flat = rows.flat();
3517
- this.rowOffset = Math.max(0, Math.min(this.rowOffset, Math.max(0, flat.length - budget)));
3518
- let cursor = 0;
3519
- this.overviewPage = Math.max(1, rows.filter((row2) => {
3520
- const start2 = cursor;
3521
- cursor += row2.length;
3522
- return start2 >= this.rowOffset && cursor <= this.rowOffset + budget;
3523
- }).length);
3691
+ const total = heights.reduce((n, h) => n + h, 0);
3692
+ this.rowOffset = Math.max(0, Math.min(this.rowOffset, Math.max(0, total - budget)));
3693
+ const visible = [];
3694
+ let cursor = 0, page = 0;
3695
+ for (const [index, row2] of view.rows.entries()) {
3696
+ const rowHeight = heights[index] ?? 1;
3697
+ if (cursor >= this.rowOffset && cursor + rowHeight <= this.rowOffset + budget)
3698
+ page++;
3699
+ if (cursor + rowHeight > this.rowOffset && cursor < this.rowOffset + budget) {
3700
+ const layout = this.layouts.get(row2.node.sessionId);
3701
+ const chosen = row2.node.sessionId === view.selectedSessionId;
3702
+ const label = this.labels[index] ?? `#${index + 1}`;
3703
+ if (layout) {
3704
+ if (!layout.lines || layout.selected !== chosen || layout.label !== label || layout.position !== index) {
3705
+ this.work.formattedOverviewRows++;
3706
+ layout.lines = overviewRowLines(row2, width, chosen, label, index, budget);
3707
+ layout.selected = chosen;
3708
+ layout.label = label;
3709
+ layout.position = index;
3710
+ }
3711
+ visible.push(...layout.lines.slice(Math.max(0, this.rowOffset - cursor), this.rowOffset + budget - cursor));
3712
+ }
3713
+ }
3714
+ cursor += rowHeight;
3715
+ }
3716
+ this.overviewPage = Math.max(1, page);
3524
3717
  body = [
3525
3718
  ...status,
3526
3719
  ...heading,
3527
- ...flat.length ? flat.slice(this.rowOffset, this.rowOffset + budget) : [
3720
+ ...total ? visible : [
3528
3721
  clip("No matching sessions; / search, f filters, r refresh.", width)
3529
3722
  ]
3530
3723
  ];
@@ -3569,7 +3762,7 @@ class TerminalShell {
3569
3762
  }
3570
3763
  }
3571
3764
  function mountTerminal(controller, renderer, text2, finished) {
3572
- let closed = false;
3765
+ let closed = false, pendingDraw = false, drawing = false;
3573
3766
  let unsubscribe = () => {
3574
3767
  };
3575
3768
  const shutdown = (failed = false) => {
@@ -3600,25 +3793,38 @@ function mountTerminal(controller, renderer, text2, finished) {
3600
3793
  const onSignal = () => shutdown();
3601
3794
  const signals = ["SIGINT", "SIGTERM", "SIGHUP"];
3602
3795
  const draw = () => {
3603
- if (closed)
3796
+ if (closed || drawing)
3604
3797
  return;
3798
+ pendingDraw = false;
3799
+ drawing = true;
3605
3800
  try {
3606
3801
  text2.content = shell.frame(renderer.width, renderer.height);
3607
3802
  } catch {
3608
3803
  fail();
3804
+ } finally {
3805
+ drawing = false;
3609
3806
  }
3610
3807
  };
3808
+ const scheduleDraw = () => {
3809
+ if (pendingDraw || closed || drawing)
3810
+ return;
3811
+ pendingDraw = true;
3812
+ queueMicrotask(() => {
3813
+ if (pendingDraw)
3814
+ draw();
3815
+ });
3816
+ };
3611
3817
  const key = (event) => {
3612
3818
  try {
3613
3819
  shell.key(event, Math.max(1, renderer.height - 4));
3614
- draw();
3820
+ scheduleDraw();
3615
3821
  } catch {
3616
3822
  fail();
3617
3823
  }
3618
3824
  };
3619
3825
  const paste = (event) => {
3620
3826
  shell.paste(new TextDecoder().decode(event.bytes));
3621
- draw();
3827
+ scheduleDraw();
3622
3828
  };
3623
3829
  try {
3624
3830
  renderer.root.add(text2);
@@ -3632,7 +3838,8 @@ function mountTerminal(controller, renderer, text2, finished) {
3632
3838
  process.on(signal, onSignal);
3633
3839
  process.on("uncaughtException", fail);
3634
3840
  process.on("unhandledRejection", fail);
3635
- unsubscribe = controller.subscribe(draw);
3841
+ unsubscribe = controller.subscribe(scheduleDraw);
3842
+ draw();
3636
3843
  if (closed) {
3637
3844
  unsubscribe();
3638
3845
  return { shell, shutdown };
@@ -3737,6 +3944,11 @@ function reconcile(a, b) {
3737
3944
  };
3738
3945
  }
3739
3946
  function analyzeModelRequests(records, options) {
3947
+ const accumulator = createModelAccumulator(options);
3948
+ accumulator.append(records);
3949
+ return accumulator.result();
3950
+ }
3951
+ function createModelAccumulator(options) {
3740
3952
  const maxRequests = options.maxRequests ?? 1e5;
3741
3953
  if (!Number.isSafeInteger(maxRequests) || maxRequests < 1 || maxRequests > 1e6)
3742
3954
  throw new Error("maxRequests must be an integer from 1 to 1000000");
@@ -3747,7 +3959,7 @@ function analyzeModelRequests(records, options) {
3747
3959
  throw new Error("Invalid analytics timestamp");
3748
3960
  if (options.from !== undefined && options.to !== undefined && options.from > options.to)
3749
3961
  throw new Error("--from must precede --to");
3750
- const coverage = {
3962
+ const counters = {
3751
3963
  source: "retained-request-records",
3752
3964
  scannedRecords: 0,
3753
3965
  unscannedRecords: 0,
@@ -3761,74 +3973,98 @@ function analyzeModelRequests(records, options) {
3761
3973
  partial: false
3762
3974
  };
3763
3975
  const requests = new Map;
3764
- for (const record2 of records) {
3765
- coverage.scannedRecords++;
3766
- if (record2.type === "gap") {
3767
- coverage.gapRecords++;
3768
- continue;
3769
- }
3770
- if (record2.type !== "request")
3771
- continue;
3772
- const fact = requestFact(record2);
3773
- if (!fact) {
3774
- coverage.missingIdentityRecords++;
3775
- continue;
3776
- }
3777
- fact.location = options.location;
3778
- const key = JSON.stringify([
3779
- fact.sessionId,
3780
- fact.requestId,
3781
- fact.startedAt
3782
- ]);
3783
- const previous = requests.get(key);
3784
- if (previous) {
3785
- coverage.duplicateRecords++;
3786
- const merged = reconcile(previous.fact, fact);
3787
- if (!merged)
3788
- previous.conflict = true;
3789
- else
3790
- previous.fact = merged;
3791
- } else {
3792
- if (requests.size === maxRequests) {
3793
- coverage.scannedRecords--;
3794
- coverage.unscannedRecords = records.length - coverage.scannedRecords;
3976
+ let totalRecords = 0, stopped = false;
3977
+ function append(records) {
3978
+ totalRecords += records.length;
3979
+ for (const record2 of records) {
3980
+ if (stopped)
3795
3981
  break;
3982
+ counters.scannedRecords++;
3983
+ if (record2.type === "gap") {
3984
+ counters.gapRecords++;
3985
+ continue;
3986
+ }
3987
+ if (record2.type !== "request")
3988
+ continue;
3989
+ const fact = requestFact(record2);
3990
+ if (!fact) {
3991
+ counters.missingIdentityRecords++;
3992
+ continue;
3993
+ }
3994
+ fact.location = options.location;
3995
+ const key = JSON.stringify([
3996
+ fact.sessionId,
3997
+ fact.requestId,
3998
+ fact.startedAt
3999
+ ]);
4000
+ const previous = requests.get(key);
4001
+ if (previous) {
4002
+ counters.duplicateRecords++;
4003
+ const merged = reconcile(previous.fact, fact);
4004
+ if (!merged)
4005
+ previous.conflict = true;
4006
+ else
4007
+ previous.fact = merged;
4008
+ } else {
4009
+ if (requests.size === maxRequests) {
4010
+ counters.scannedRecords--;
4011
+ stopped = true;
4012
+ break;
4013
+ }
4014
+ requests.set(key, { fact, conflict: false });
3796
4015
  }
3797
- requests.set(key, { fact, conflict: false });
3798
4016
  }
3799
4017
  }
3800
- const accepted = [];
3801
- for (const { fact: r, conflict } of requests.values()) {
3802
- if (conflict) {
3803
- coverage.conflictingRequests++;
3804
- continue;
3805
- }
3806
- if (r.completedAt === undefined) {
3807
- coverage.incompleteRequests++;
3808
- continue;
3809
- }
3810
- const duration = r.completedAt - r.startedAt;
3811
- if (!nonnegative2(r.completedAt) || !Number.isFinite(duration) || duration <= 0) {
3812
- coverage.invalidTimingRequests++;
3813
- continue;
4018
+ function* steps() {
4019
+ const coverage = {
4020
+ ...counters,
4021
+ unscannedRecords: totalRecords - counters.scannedRecords
4022
+ };
4023
+ const accepted = [];
4024
+ for (const { fact: r, conflict } of requests.values()) {
4025
+ yield;
4026
+ if (conflict) {
4027
+ coverage.conflictingRequests++;
4028
+ continue;
4029
+ }
4030
+ if (r.completedAt === undefined) {
4031
+ coverage.incompleteRequests++;
4032
+ continue;
4033
+ }
4034
+ const duration = r.completedAt - r.startedAt;
4035
+ if (!nonnegative2(r.completedAt) || !Number.isFinite(duration) || duration <= 0) {
4036
+ coverage.invalidTimingRequests++;
4037
+ continue;
4038
+ }
4039
+ if (options.from !== undefined && r.completedAt < options.from || options.to !== undefined && r.completedAt >= options.to || options.model !== undefined && r.model !== options.model || options.agent !== undefined && r.agent !== options.agent)
4040
+ continue;
4041
+ accepted.push({ ...r, completedAt: r.completedAt });
3814
4042
  }
3815
- if (options.from !== undefined && r.completedAt < options.from || options.to !== undefined && r.completedAt >= options.to || options.model !== undefined && r.model !== options.model || options.agent !== undefined && r.agent !== options.agent)
3816
- continue;
3817
- accepted.push({ ...r, completedAt: r.completedAt });
4043
+ const models = yield* summarizeModelRequestSteps(accepted);
4044
+ coverage.partial = coverage.unscannedRecords > 0 || coverage.conflictingRequests > 0 || coverage.missingIdentityRecords > 0 || coverage.incompleteRequests > 0 || coverage.invalidTimingRequests > 0 || coverage.gapRecords > 0 || models.some((s) => s.partialRequests > 0 || s.legacyAttributionRequests > 0 || s.excludedTpsRequests > 0 || s.excludedTtftRequests > 0 || !s.model);
4045
+ return {
4046
+ schemaVersion: 1,
4047
+ location: options.location,
4048
+ models,
4049
+ coverage,
4050
+ requests: accepted
4051
+ };
3818
4052
  }
3819
- const models = summarizeModelRequests(accepted);
3820
- coverage.partial = coverage.unscannedRecords > 0 || coverage.conflictingRequests > 0 || coverage.missingIdentityRecords > 0 || coverage.incompleteRequests > 0 || coverage.invalidTimingRequests > 0 || coverage.gapRecords > 0 || models.some((s) => s.partialRequests > 0 || s.legacyAttributionRequests > 0 || s.excludedTpsRequests > 0 || s.excludedTtftRequests > 0 || !s.model);
3821
- return {
3822
- schemaVersion: 1,
3823
- location: options.location,
3824
- models,
3825
- coverage,
3826
- requests: accepted
3827
- };
4053
+ return { append, steps, result: () => finishSteps(steps()) };
4054
+ }
4055
+ function finishSteps(steps) {
4056
+ let step = steps.next();
4057
+ while (!step.done)
4058
+ step = steps.next();
4059
+ return step.value;
3828
4060
  }
3829
4061
  function summarizeModelRequests(requests) {
4062
+ return finishSteps(summarizeModelRequestSteps(requests));
4063
+ }
4064
+ function* summarizeModelRequestSteps(requests) {
3830
4065
  const groups = new Map;
3831
4066
  for (const r of requests) {
4067
+ yield;
3832
4068
  const duration = r.completedAt - r.startedAt;
3833
4069
  let group = groups.get(r.model);
3834
4070
  if (!group) {