@cesarus777/opencode-cgp 0.4.0 → 0.4.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.
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
@@ -936,6 +1015,51 @@ function executionStatus(known, nativeRunning, evidence) {
936
1015
  return known ? "idle" : "unknown";
937
1016
  }
938
1017
 
1018
+ // src/historyclient.ts
1019
+ function historyFailure(error, signal) {
1020
+ if (signal?.aborted && signal.reason instanceof DOMException && signal.reason.name === "TimeoutError" || error instanceof DOMException && error.name === "TimeoutError")
1021
+ return new HistoryFailure("timeout");
1022
+ if (error instanceof HistoryFailure)
1023
+ return error;
1024
+ const code = typeof error === "object" && error !== null && "type" in error ? error.type : undefined;
1025
+ return new HistoryFailure(code === "rpc.unavailable" || code === "rpc.method_not_found" ? "unsupported" : code === "rpc.invalid_output" ? "incompatible" : code === "rpc.invalid_input" ? "rejected" : "unavailable");
1026
+ }
1027
+ function checkedHistory(value, sessionId) {
1028
+ if (!isHistoryResult(value) || value.data.series.some((series) => series.sessionId !== sessionId))
1029
+ throw new HistoryFailure("incompatible");
1030
+ return value;
1031
+ }
1032
+ function historyStatusLines(input) {
1033
+ const messages = {
1034
+ unsupported: "History service is unavailable in this server location. Check that the owning server has a plugin version with chart history. Press r to check again.",
1035
+ timeout: "History request timed out. Press r to retry.",
1036
+ incompatible: "History response is incompatible. Check client/server plugin versions. Press r to retry.",
1037
+ rejected: "History request was rejected. Check client/server plugin versions. Press r to retry.",
1038
+ unavailable: "History could not be loaded. Press r to retry."
1039
+ };
1040
+ return [
1041
+ ...input.status === "loading" ? [
1042
+ input.loadedAt === undefined ? "Loading retained history\u2026" : "Updating history \xB7 showing previous results"
1043
+ ] : input.status === "disabled" ? [
1044
+ "History recording is disabled in this server location. Press r to check again after configuration changes."
1045
+ ] : input.status === "unavailable" ? [messages[input.reason ?? "unavailable"]] : [],
1046
+ ...input.stale && input.loadedAt !== undefined ? [
1047
+ `Showing previous results \xB7 last loaded ${new Date(input.loadedAt).toISOString()}`
1048
+ ] : []
1049
+ ];
1050
+ }
1051
+ var HistoryFailure;
1052
+ var init_historyclient = __esm(() => {
1053
+ init_rpc();
1054
+ HistoryFailure = class HistoryFailure extends Error {
1055
+ reason;
1056
+ constructor(reason) {
1057
+ super(`History ${reason}`);
1058
+ this.reason = reason;
1059
+ }
1060
+ };
1061
+ });
1062
+
939
1063
  // src/tui/transport.ts
940
1064
  class SnapshotClient {
941
1065
  transport;
@@ -1216,7 +1340,7 @@ function validateInventoryLimits(limits) {
1216
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)
1217
1341
  throw new Error("Invalid dashboard inventory limits");
1218
1342
  }
1219
- async function discoverInventory(port, signal, limits = inventoryLimits) {
1343
+ async function discoverInventory(port, signal, limits = inventoryLimits, progress) {
1220
1344
  validateInventoryLimits(limits);
1221
1345
  const sessions = new Map;
1222
1346
  const notices = new Set;
@@ -1224,59 +1348,75 @@ async function discoverInventory(port, signal, limits = inventoryLimits) {
1224
1348
  let pages = 0;
1225
1349
  while (queue.length && pages < limits.maxPages) {
1226
1350
  signal.throwIfAborted();
1227
- const branch = queue.shift();
1228
- if (!branch)
1229
- break;
1230
- try {
1231
- pages++;
1232
- const page = await port.listSessions({
1233
- parentID: branch.parent,
1234
- limit: 100,
1235
- ...branch.cursor ? { cursor: branch.cursor } : {}
1236
- }, signal);
1237
- signal.throwIfAborted();
1238
- for (const session of page.data) {
1239
- if (session.directory !== port.location.directory) {
1240
- notices.add("foreign-location");
1241
- continue;
1242
- }
1243
- if ((session.parentId ?? null) !== branch.parent || sessions.has(session.sessionId)) {
1244
- notices.add("invalid-hierarchy");
1245
- 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");
1246
1393
  }
1247
- if (sessions.size >= limits.maxNodes) {
1248
- notices.add("node-limit");
1249
- return {
1250
- sessions: [...sessions.values()],
1251
- complete: false,
1252
- pages,
1253
- notices: [...notices]
1254
- };
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
+ });
1255
1404
  }
1256
- sessions.set(session.sessionId, session);
1257
- if (branch.depth < limits.maxDepth)
1258
- queue.push({
1259
- parent: session.sessionId,
1260
- depth: branch.depth + 1,
1261
- cursors: []
1262
- });
1263
- else
1264
- notices.add("depth-limit");
1405
+ } catch {
1406
+ signal.throwIfAborted();
1407
+ notices.add("inventory-unavailable");
1265
1408
  }
1266
- const cursor = page.cursor?.next;
1267
- if (cursor) {
1268
- if (cursor === branch.cursor || branch.cursors.includes(cursor))
1269
- notices.add("repeated-cursor");
1270
- else
1271
- queue.unshift({
1272
- ...branch,
1273
- cursor,
1274
- cursors: [...branch.cursors, cursor]
1275
- });
1409
+ }
1410
+ if (progress) {
1411
+ try {
1412
+ progress({
1413
+ sessions: [...sessions.values()],
1414
+ complete: false,
1415
+ pages,
1416
+ notices: [...notices]
1417
+ });
1418
+ } catch {
1276
1419
  }
1277
- } catch {
1278
- signal.throwIfAborted();
1279
- notices.add("inventory-unavailable");
1280
1420
  }
1281
1421
  }
1282
1422
  if (queue.length)
@@ -1542,16 +1682,13 @@ var init_models = __esm(() => {
1542
1682
 
1543
1683
  // src/dashboard/view.ts
1544
1684
  function immutableView(value) {
1545
- const copy = structuredClone(value);
1546
- const freeze = (item) => {
1547
- if (item === null || typeof item !== "object")
1548
- return;
1549
- for (const child of Object.values(item))
1550
- freeze(child);
1551
- Object.freeze(item);
1552
- };
1553
- freeze(copy);
1554
- 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);
1555
1692
  }
1556
1693
  function projectHistory(result, filters) {
1557
1694
  const match = (point) => (!filters.agent || point.agent === filters.agent) && (!filters.model || point.model === filters.model);
@@ -1603,13 +1740,15 @@ function projectHistory(result, filters) {
1603
1740
  notices
1604
1741
  };
1605
1742
  }
1606
- var historyWindows;
1743
+ var historyWindows, owned;
1607
1744
  var init_view = __esm(() => {
1608
1745
  historyWindows = [300000, 3600000, 86400000, 604800000];
1746
+ owned = new WeakSet;
1609
1747
  });
1610
1748
 
1611
1749
  // src/dashboard/controller.ts
1612
1750
  class DashboardController {
1751
+ snapshots = new WeakMap;
1613
1752
  locations = new Map;
1614
1753
  cache = new Map;
1615
1754
  listeners = new Set;
@@ -1628,6 +1767,7 @@ class DashboardController {
1628
1767
  pendingHistory;
1629
1768
  historyAbort;
1630
1769
  historyBusy = false;
1770
+ historyReason;
1631
1771
  historyStatus = "idle";
1632
1772
  refreshTimer;
1633
1773
  ageTimer;
@@ -1814,7 +1954,33 @@ class DashboardController {
1814
1954
  signal,
1815
1955
  AbortSignal.timeout(15000)
1816
1956
  ]);
1817
- 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
+ });
1818
1984
  let active;
1819
1985
  try {
1820
1986
  active = new Set(await state.port.activeSessions(result.sessions.map((session) => session.sessionId), scanSignal));
@@ -1889,7 +2055,14 @@ class DashboardController {
1889
2055
  return;
1890
2056
  state.connection = client.connection;
1891
2057
  if (client.health) {
1892
- 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
+ }));
1893
2066
  state.health = client.health;
1894
2067
  state.serverAt = client.now();
1895
2068
  state.receivedAt = this.elapsed();
@@ -1919,6 +2092,7 @@ class DashboardController {
1919
2092
  }
1920
2093
  cancelHistory() {
1921
2094
  this.historyGeneration++;
2095
+ this.historyReason = undefined;
1922
2096
  this.pendingHistory = undefined;
1923
2097
  this.historyAbort?.abort();
1924
2098
  }
@@ -1972,10 +2146,10 @@ class DashboardController {
1972
2146
  }, signal);
1973
2147
  if (signal.aborted || task.generation !== this.historyGeneration)
1974
2148
  return;
1975
- if (!isHistoryResult(result) || result.data.series.some((series) => series.sessionId !== task.sessionId))
1976
- throw new Error("Invalid history response");
2149
+ checkedHistory(result, task.sessionId);
1977
2150
  this.cache.set(task.key, {
1978
2151
  directory: task.state.port.location.directory,
2152
+ loadedAt: this.now(),
1979
2153
  valid: true,
1980
2154
  result: structuredClone(result),
1981
2155
  from,
@@ -1987,12 +2161,16 @@ class DashboardController {
1987
2161
  this.cache.delete(oldest);
1988
2162
  }
1989
2163
  this.historyStatus = result.enabled ? "ready" : "disabled";
1990
- } catch {
1991
- if (task.generation === this.historyGeneration && !task.scope.aborted)
2164
+ } catch (error) {
2165
+ if (task.generation === this.historyGeneration && !task.scope.aborted) {
1992
2166
  this.historyStatus = "unavailable";
2167
+ this.historyReason = historyFailure(error, signal).reason;
2168
+ }
1993
2169
  } finally {
1994
- if (task.generation === this.historyGeneration && signal.aborted && !task.scope.aborted)
2170
+ if (task.generation === this.historyGeneration && signal.aborted && !task.scope.aborted) {
1995
2171
  this.historyStatus = "unavailable";
2172
+ this.historyReason = historyFailure(undefined, signal).reason;
2173
+ }
1996
2174
  this.historyBusy = false;
1997
2175
  if (this.historyAbort === controller)
1998
2176
  this.historyAbort = undefined;
@@ -2077,12 +2255,13 @@ class DashboardController {
2077
2255
  ...selected ? { selected } : {},
2078
2256
  history: {
2079
2257
  status: this.historyStatus,
2258
+ ...this.historyReason ? { reason: this.historyReason } : {},
2080
2259
  windowMs: this.windowMs,
2081
2260
  maxWindowMs: 604800000,
2082
2261
  maxPoints: 500,
2083
2262
  stale: this.historyStatus === "loading" || this.historyStatus === "unavailable" || !this.running,
2084
2263
  ...state.selected ? { sessionId: state.selected } : {},
2085
- ...cached ? { from: cached.from, to: cached.to } : {},
2264
+ ...cached ? { from: cached.from, to: cached.to, loadedAt: cached.loadedAt } : {},
2086
2265
  ...projected ? { result: projected.result, summary: projected.summary } : {},
2087
2266
  notices: projected?.notices ?? [
2088
2267
  "bounded-retention",
@@ -2100,7 +2279,7 @@ class DashboardController {
2100
2279
  }
2101
2280
  }
2102
2281
  var init_controller = __esm(() => {
2103
- init_rpc();
2282
+ init_historyclient();
2104
2283
  init_transport();
2105
2284
  init_inventory();
2106
2285
  init_models();
@@ -2272,13 +2451,12 @@ async function connectDashboard(options, discovery = Service) {
2272
2451
  async history(input, signal) {
2273
2452
  if (!isHistoryRead(input))
2274
2453
  throw new Error("History requires one session, at most seven days and 16\u2013500 points");
2454
+ const requestSignal = deadline(signal);
2275
2455
  try {
2276
- const result = await client.rpc(historyRpc).read(input, { location, signal: deadline(signal) });
2277
- if (!isHistoryResult(result))
2278
- throw new Error;
2279
- return result;
2280
- } catch {
2281
- throw new Error("Context Guard history unavailable or incompatible in this location");
2456
+ const result = await client.rpc(historyRpc).read(input, { location, signal: requestSignal });
2457
+ return checkedHistory(result, input.sessionId);
2458
+ } catch (error) {
2459
+ throw historyFailure(error, requestSignal);
2282
2460
  }
2283
2461
  }
2284
2462
  };
@@ -2295,10 +2473,214 @@ async function connectDashboard(options, discovery = Service) {
2295
2473
  };
2296
2474
  }
2297
2475
  var init_connection = __esm(() => {
2476
+ init_historyclient();
2298
2477
  init_rpc();
2299
2478
  init_port();
2300
2479
  });
2301
2480
 
2481
+ // src/tui/quantity.ts
2482
+ function tokens(value) {
2483
+ if (value === undefined || !Number.isFinite(value))
2484
+ return "?";
2485
+ const magnitude = Math.abs(value);
2486
+ if (magnitude >= 1e6)
2487
+ return `${+(value / 1e6).toFixed(1)}M`;
2488
+ if (magnitude >= 1000)
2489
+ return `${+(value / 1000).toFixed(1)}K`;
2490
+ return String(value);
2491
+ }
2492
+
2493
+ // src/tui/statusformat.ts
2494
+ function freshnessLabel(row2) {
2495
+ switch (row2?.freshness) {
2496
+ case "fresh":
2497
+ return;
2498
+ case "stale":
2499
+ return "Measurement out of date";
2500
+ case "invalid":
2501
+ return "Usage unavailable";
2502
+ default:
2503
+ return "Usage unknown";
2504
+ }
2505
+ }
2506
+ function measurementLabel(row2, ageMs) {
2507
+ const m = row2?.snapshot?.measurement;
2508
+ if (!m || row2?.freshness === "missing")
2509
+ return "Usage unknown";
2510
+ if (row2?.freshness === "invalid")
2511
+ return "Usage unavailable";
2512
+ const age = ageMs === undefined ? "" : ` \xB7 measured ${Math.floor(Math.max(0, ageMs) / 1000)}s ago`;
2513
+ return `${m.confidence === "reported" ? "Provider reported" : "Estimated"}${age}${row2?.freshness === "stale" ? " \xB7 out of date" : ""}`;
2514
+ }
2515
+ function measurementExplanation(row2) {
2516
+ const m = row2.snapshot?.measurement;
2517
+ if (!m || row2.freshness === "missing")
2518
+ return ["No measurement available."];
2519
+ if (row2.freshness === "invalid")
2520
+ return ["Measurement cannot be used for this session."];
2521
+ return [
2522
+ m.scope === "completed-request" ? "Usage from the last completed request; not the next request's size." : "Usage for the prepared request; not a completed request.",
2523
+ m.provenance === "serialized-estimate" ? "Estimate from serialized request content." : "Provider usage includes input and cache tokens."
2524
+ ];
2525
+ }
2526
+ function compactionLabel(row2, compact = false) {
2527
+ const phase = row2?.snapshot?.compaction.phase;
2528
+ if (!phase || phase === "idle")
2529
+ return;
2530
+ if (compact)
2531
+ return {
2532
+ requesting: "Requesting",
2533
+ accepted: "Waiting",
2534
+ running: "Compacting",
2535
+ "awaiting-measurement": "Checking",
2536
+ blocked: "Retry held",
2537
+ uncertain: "Unconfirmed"
2538
+ }[phase];
2539
+ return {
2540
+ requesting: "Requesting compaction",
2541
+ accepted: "Compaction request accepted \xB7 waiting to start",
2542
+ running: "Compaction running",
2543
+ "awaiting-measurement": "Compaction ended \xB7 checking context reduction",
2544
+ blocked: "Automatic compaction retry suppressed",
2545
+ uncertain: "Completion unconfirmed \xB7 automatic retry suppressed"
2546
+ }[phase];
2547
+ }
2548
+ function reasonExplanation(reason) {
2549
+ switch (reason) {
2550
+ case "below-threshold":
2551
+ return "Context is below the policy threshold.";
2552
+ case "threshold-crossed":
2553
+ return "Context reached the policy threshold; this does not guarantee compaction.";
2554
+ case "missing-usage":
2555
+ return "No measurement available for the policy decision.";
2556
+ case "model-changed":
2557
+ return "The measurement belongs to a different model.";
2558
+ case "invalid-usage":
2559
+ return "The measurement cannot be used for the policy decision.";
2560
+ case "stale-usage":
2561
+ return "The policy measurement is out of date.";
2562
+ case "missing-model-budget":
2563
+ return "Model capacity is unavailable for this threshold.";
2564
+ case "incompatible-model-budget":
2565
+ return "The threshold is incompatible with model capacity.";
2566
+ case "compaction-failed":
2567
+ return "Compaction failed; automatic retries are limited after repeated failures.";
2568
+ case "reclaim-unverifiable":
2569
+ return "Context reduction could not be verified with comparable provider readings.";
2570
+ case "insufficient-reclaim":
2571
+ return "Measured context reduction was below the required minimum.";
2572
+ case "admission-outcome-unknown":
2573
+ return "Whether the compaction request was accepted is unconfirmed.";
2574
+ case "completion-event-timeout":
2575
+ return "No confirming completion event arrived within the waiting period.";
2576
+ case "restart-needs-lifecycle-evidence":
2577
+ return "The previous request needs confirming lifecycle evidence after restart.";
2578
+ case "event-stream-gap":
2579
+ return "Automatic requests are suspended after an event gap; reload required.";
2580
+ case "persistence-unavailable":
2581
+ return "Safety storage is unavailable; repair storage and reload.";
2582
+ case "capacity-exceeded":
2583
+ return "Monitoring limit reached; some sessions are unavailable.";
2584
+ case "model-metadata-unavailable":
2585
+ return "Some model limits are unavailable.";
2586
+ default:
2587
+ return "See diagnostic codes.";
2588
+ }
2589
+ }
2590
+ function pressureLabel(row2) {
2591
+ if (row2?.freshness !== "fresh")
2592
+ return "Threshold pressure unknown";
2593
+ switch (row2.pressure) {
2594
+ case "crossed":
2595
+ return "Threshold reached";
2596
+ case "near":
2597
+ return "Near threshold";
2598
+ case "normal":
2599
+ return "Below threshold";
2600
+ default:
2601
+ return "Threshold pressure unknown";
2602
+ }
2603
+ }
2604
+ function thresholdDistance(row2, compact = false) {
2605
+ const delta = row2?.triggerDeltaTokens;
2606
+ if (row2?.freshness !== "fresh" || row2.pressure === "unknown" || delta === undefined || !Number.isFinite(delta))
2607
+ return "pressure unknown";
2608
+ if (delta === 0)
2609
+ return row2.pressure === "crossed" ? "Threshold reached" : "At threshold \xB7 requires more";
2610
+ if (compact)
2611
+ return `${tokens(Math.abs(delta))} ${delta < 0 ? "over" : "below"}${row2.pressure === "near" ? " \xB7 Near threshold" : delta < 0 ? " \xB7 Threshold reached" : ""}`;
2612
+ return `${tokens(Math.abs(delta))} ${Math.abs(delta) === 1 ? "token" : "tokens"} ${delta < 0 ? "over" : "below"} threshold`;
2613
+ }
2614
+ function guardStatusLines(health, compact = false) {
2615
+ switch (health?.enforcement) {
2616
+ case "observing":
2617
+ return compact ? ["Observing", "No Guard compact"] : ["Observing \xB7 Guard won't request compaction"];
2618
+ case "available":
2619
+ return compact ? ["Guard requests", "enabled"] : ["Guard automatic requests enabled"];
2620
+ case "disabled":
2621
+ return ["Guard disabled"];
2622
+ case "suspended": {
2623
+ const gap = health.reasons.includes("event-stream-gap");
2624
+ const storage = health.reasons.includes("persistence-unavailable");
2625
+ return compact ? [
2626
+ "Guard suspended",
2627
+ ...storage ? ["Storage: repair"] : [],
2628
+ ...gap ? ["Reload required"] : storage ? ["Then reload"] : ["See Details"]
2629
+ ] : [
2630
+ "Guard suspended" + (storage ? " \xB7 safety storage unavailable; repair storage and reload" : "") + (gap ? " \xB7 reload required" : storage ? "" : " \xB7 see Details")
2631
+ ];
2632
+ }
2633
+ default:
2634
+ return ["Guard status unknown"];
2635
+ }
2636
+ }
2637
+ function panelStatus(health) {
2638
+ return guardStatusLines(health).join(" \xB7 ");
2639
+ }
2640
+ function connectionLabel(connection, compact = false) {
2641
+ switch (connection) {
2642
+ case "loading":
2643
+ return "Connecting";
2644
+ case "live":
2645
+ return "Live";
2646
+ case "reconnecting":
2647
+ return "Reconnecting";
2648
+ case "unavailable":
2649
+ return compact ? "Unavailable" : "Connection unavailable";
2650
+ case "incompatible":
2651
+ return compact ? "Unsupported" : "Monitoring unsupported";
2652
+ }
2653
+ }
2654
+ function connectionNotice(connection, retainedMeasurements, compact = false) {
2655
+ if (connection === "live")
2656
+ return;
2657
+ return retainedMeasurements ? compact ? "Last data shown" : "Showing last received measurements" : compact ? "No measurements" : "No measurements available";
2658
+ }
2659
+ function monitorStatusLines(connection, health, retainedMeasurements, compact = false) {
2660
+ const notice = connectionNotice(connection, retainedMeasurements, compact);
2661
+ const reasons = health?.reasons ?? [];
2662
+ const limited = reasons.includes("capacity-exceeded");
2663
+ const model = reasons.includes("model-metadata-unavailable");
2664
+ const attention = health?.enforcement !== "suspended" && reasons.length > 0;
2665
+ return [
2666
+ ...health && connection !== "live" ? [compact ? "Last known:" : "Last known Guard:"] : [],
2667
+ ...guardStatusLines(health, compact),
2668
+ ...attention ? compact ? [limited ? "Monitor limit" : model ? "Limits unknown" : "See Details"] : [
2669
+ ...limited ? ["Monitoring limit reached \xB7 some sessions unavailable"] : [],
2670
+ ...model ? ["Some model limits unavailable \xB7 context may be unknown"] : [],
2671
+ ...!limited && !model ? ["Guard needs attention \xB7 see Details"] : []
2672
+ ] : [],
2673
+ ...notice ? [notice] : []
2674
+ ];
2675
+ }
2676
+ var contextHistoryLegend;
2677
+ var init_statusformat = __esm(() => {
2678
+ contextHistoryLegend = [
2679
+ "Recent samples \u2192 now \xB7 \u2504 threshold",
2680
+ "Dim = last known context"
2681
+ ];
2682
+ });
2683
+
2302
2684
  // src/tui/format.ts
2303
2685
  function quantity(n) {
2304
2686
  return n === undefined || !Number.isFinite(n) ? "?" : Math.abs(n) >= 1e6 ? `${(n / 1e6).toFixed(1)}m` : Math.abs(n) >= 1000 ? `${(n / 1000).toFixed(1)}k` : String(n);
@@ -2326,22 +2708,6 @@ function clip(value, width) {
2326
2708
  }
2327
2709
  return width < 3 ? ".".repeat(Math.max(0, width)) : result + "...";
2328
2710
  }
2329
- function rowLines(row2, width) {
2330
- const s = row2.snapshot, m = s?.measurement;
2331
- const prefix = row2.current ? "* " : row2.depth > 5 ? ".../" : " ".repeat(Math.min(row2.depth, 5)) + "|-";
2332
- const percent = row2.utilizationPercent === undefined ? "?" : `${row2.utilizationPercent.toFixed(1)}%`;
2333
- const warning = row2.freshness === "stale" ? " STALE" : row2.pressure === "crossed" ? " !! crossed" : row2.pressure === "near" ? " ! near" : "";
2334
- const suffix = ` ${statuses[row2.node.status]} ${m?.confidence === "approximate" ? "~" : ""}${percent}${warning}`;
2335
- const label = clip(`${prefix}${row2.node.agent ?? row2.node.sessionId}`, Math.max(1, width - cells(suffix)));
2336
- return [
2337
- clip(`${label}${suffix}`, width),
2338
- ...performanceLines(row2, width),
2339
- ...wrap(`${quantity(m?.tokens)}/${quantity(s?.capacityTokens)} input; to ${quantity(row2.triggerDeltaTokens)}`, width),
2340
- ...s && s.compaction.phase !== "idle" ? wrap(`compact: ${s.compaction.phase}`, width) : [],
2341
- ...row2.node.interrupted ? [clip("interrupted", width)] : [],
2342
- ...row2.hidden ? wrap(`+ ${row2.hidden} completed descendants`, width) : []
2343
- ];
2344
- }
2345
2711
  function detailLines(row2, width, expanded = false, ageMs = 0) {
2346
2712
  const s = row2?.snapshot, m = s?.measurement;
2347
2713
  if (!row2)
@@ -2352,22 +2718,29 @@ function detailLines(row2, width, expanded = false, ageMs = 0) {
2352
2718
  ...wrap("Guard: not monitored / unavailable", width)
2353
2719
  ];
2354
2720
  const threshold = s.policy.configured;
2355
- const headroom = row2.triggerDeltaTokens;
2721
+ const valid2 = row2.freshness === "fresh" || row2.freshness === "stale";
2356
2722
  const lines = [
2357
2723
  `${s.agent ?? "?"} / ${s.model ?? "?"}`,
2724
+ `${row2.freshness === "stale" ? "Last measured" : "Context"} ${valid2 && m?.confidence === "approximate" ? "~" : ""}${quantity(valid2 ? m?.tokens : undefined)} / ${quantity(s.capacityTokens)} input`,
2725
+ `${valid2 ? row2.utilizationPercent?.toFixed(1) ?? "?" : "?"}% capacity \xB7 ${pressureLabel(row2)}`,
2726
+ `Threshold ${quantity(s.policy.triggerTokens)} \xB7 ${thresholdDistance(row2)}`,
2727
+ measurementLabel(row2, ageMs),
2728
+ ...measurementExplanation(row2),
2729
+ compactionLabel(row2) ?? "No compaction request in progress.",
2730
+ ...s.compaction.blockedReason ? [reasonExplanation(s.compaction.blockedReason)] : [],
2731
+ `Recorded policy decision: ${reasonExplanation(s.policy.reason)}`,
2358
2732
  ...performanceLines(row2, width),
2359
- `${m?.confidence === "approximate" ? "~" : ""}${quantity(m?.tokens)} / ${quantity(s.capacityTokens)} input`,
2360
- `${row2.utilizationPercent?.toFixed(1) ?? "?"}% ${row2.pressure === "crossed" ? "!! crossed" : row2.pressure === "near" ? "! near" : row2.freshness.toUpperCase()}`,
2361
- `Trigger ${threshold.type === "ratio" ? `${threshold.value * 100}%` : `${threshold.value} ${threshold.type}`} (${s.policy.comparison})`,
2362
- `Resolved trigger ${s.policy.triggerTokens ?? "?"}`,
2363
- `Until trigger ${quantity(headroom)}`,
2364
- `Compact: ${s.compaction.phase}`,
2365
- `${s.compaction.armed ? "armed" : "disarmed"}${s.compaction.blockedReason ? ` / ${s.compaction.blockedReason}` : ""}`,
2366
- m ? `${m.confidence} ${m.scope}` : "measurement unknown",
2367
- m ? `${Math.floor(Math.max(0, ageMs) / 1000)}s ago; ${row2.freshness}` : ""
2733
+ ...s.performance ? [
2734
+ "TPS: tokens per second. TTFT: time to first output (text or reasoning); approximate.",
2735
+ "Live TPS is estimated; completed rates use provider output including reasoning and matched timing.",
2736
+ "Model average uses matched request durations; session average includes idle and tool time.",
2737
+ ...s.performance.partial ? [
2738
+ "Incomplete timing coverage for model averages; only matched observed durations are included."
2739
+ ] : []
2740
+ ] : []
2368
2741
  ];
2369
2742
  if (expanded)
2370
- lines.push(`Policy ${s.policy.id}: ${s.policy.reason}`, `Limits in/context/out ${quantity(s.limits?.input)}/${quantity(s.limits?.context)}/${quantity(s.limits?.output)}`, `Output reserve ${quantity(s.outputReserveTokens)}`, `Provenance ${m?.provenance ?? "?"}`, `Cooldown until ${s.compaction.cooldownUntil ?? "?"}`, `Cumulative (${s.cumulativeScope})`, `Input ${quantity(s.cumulative?.input)} output ${quantity(s.cumulative?.output)}`, `Reasoning ${quantity(s.cumulative?.reasoning)}`, `Cache read ${quantity(s.cumulative?.cache.read)} write ${quantity(s.cumulative?.cache.write)}`);
2743
+ lines.push("Diagnostic codes and accounting:", `Compaction phase: ${s.compaction.phase}`, `Armed: ${s.compaction.armed} (one eligibility condition, not permission to compact)`, `Blocked reason: ${s.compaction.blockedReason ?? "?"}`, `Measurement confidence: ${m?.confidence ?? "?"}`, `Measurement scope: ${m?.scope ?? "?"}`, `Measurement freshness: ${row2.freshness}`, `Trigger ${threshold.type === "ratio" ? `${threshold.value * 100}%` : `${threshold.value} ${threshold.type}`} (${s.policy.comparison})`, `Resolved trigger ${s.policy.triggerTokens ?? "?"}`, `Policy ${s.policy.id}: ${s.policy.reason}`, `Pressure code: ${row2.pressure}`, `Recorded tokens: ${quantity(m?.tokens)}`, `Limits in/context/out ${quantity(s.limits?.input)}/${quantity(s.limits?.context)}/${quantity(s.limits?.output)}`, `Output reserve ${quantity(s.outputReserveTokens)}`, `Provenance ${m?.provenance ?? "?"}`, `Cooldown until ${s.compaction.cooldownUntil ?? "?"}`, `Cumulative (${s.cumulativeScope})`, `Input ${quantity(s.cumulative?.input)} output ${quantity(s.cumulative?.output)}`, `Reasoning ${quantity(s.cumulative?.reasoning)}`, `Cache read ${quantity(s.cumulative?.cache.read)} write ${quantity(s.cumulative?.cache.write)}`);
2371
2744
  return lines.filter(Boolean).flatMap((line) => wrap(line, width));
2372
2745
  }
2373
2746
  function sanitize(value) {
@@ -2404,16 +2777,10 @@ function performanceLines(row2, width) {
2404
2777
  ...wrap(`TTFT ${p.ttftMs === undefined ? "?" : `~${(p.ttftMs / 1000).toFixed(2)}s`}${p.latestRequestTps === undefined ? "" : ` \xB7 Last ${value(p.latestRequestTps)} t/s`}`, width)
2405
2778
  ];
2406
2779
  }
2407
- var segmenter, statuses;
2780
+ var segmenter;
2408
2781
  var init_format = __esm(() => {
2782
+ init_statusformat();
2409
2783
  segmenter = new Intl.Segmenter(undefined, { granularity: "grapheme" });
2410
- statuses = {
2411
- running: "RUN",
2412
- idle: "IDLE",
2413
- completed: "DONE",
2414
- failed: "FAIL",
2415
- unknown: "?"
2416
- };
2417
2784
  });
2418
2785
 
2419
2786
  // src/tui/chartformat.ts
@@ -2627,11 +2994,170 @@ var init_modelview = __esm(() => {
2627
2994
  ];
2628
2995
  });
2629
2996
 
2997
+ // src/tui/panelformat.ts
2998
+ function panelSummary(row2, width, age, compact = false) {
2999
+ if (!row2)
3000
+ return [clip("No sessions available \xB7 r refresh", width)];
3001
+ const snapshot = row2.snapshot;
3002
+ const valid2 = row2.freshness === "fresh" || row2.freshness === "stale";
3003
+ const prefix = row2.freshness === "stale" ? "Last measured" : "Context";
3004
+ const percent = `${row2.utilizationPercent === undefined ? "?" : Math.round(row2.utilizationPercent)}%`;
3005
+ const estimate = valid2 && snapshot?.measurement?.confidence === "approximate" ? "~" : "";
3006
+ const identity = `${width < 28 ? ">" : "Selected:"} ${row2.node.agent ?? row2.node.sessionId}${row2.current ? width < 28 ? " *" : " (current)" : ""}`;
3007
+ const threshold = `Threshold ${tokens(snapshot?.policy.triggerTokens)} \xB7 ${thresholdDistance(row2)}`;
3008
+ const occupancy = compact ? `${prefix} ${estimate}${percent}` : `${prefix} ${estimate}${tokens(valid2 ? snapshot?.measurement?.tokens : undefined)}/${tokens(snapshot?.capacityTokens)} \xB7 ${percent}`;
3009
+ return [
3010
+ clip(identity, width),
3011
+ clip(occupancy, width),
3012
+ ...!compact ? [
3013
+ clip(cells(threshold) <= width ? threshold : thresholdDistance(row2), width),
3014
+ clip(`${row2.node.model ?? snapshot?.model ?? "Model unknown"} \xB7 ${panelStatuses[row2.node.status]}`, width),
3015
+ ...wrap(measurementLabel(row2, age), width)
3016
+ ] : []
3017
+ ];
3018
+ }
3019
+ function tableLine(identity, status, context, threshold, width) {
3020
+ const pad = (value, size) => {
3021
+ const text2 = clip(value, size);
3022
+ return text2 + " ".repeat(Math.max(0, size - cells(text2)));
3023
+ };
3024
+ return `${pad(identity, width - 47)} ${pad(status, 8)} ${pad(context, 7)} ${pad(threshold, 29)}`;
3025
+ }
3026
+ function panelRowHeading(label, width) {
3027
+ return width >= 72 ? tableLine(label, "State", "Context", "Threshold", width) : clip(label, width);
3028
+ }
3029
+ function panelRowLines(row2, width, selected) {
3030
+ const marker = `${selected ? ">" : " "}${row2.current ? "*" : " "}`;
3031
+ const indent = " ".repeat(Math.min(row2.depth, Math.floor(width / 24))) + (row2.depth ? "\u2514 " : "");
3032
+ const identity = `${marker} ${indent}${row2.node.agent ?? row2.node.sessionId}`;
3033
+ const percent = `${(row2.freshness === "fresh" || row2.freshness === "stale") && row2.snapshot?.measurement?.confidence === "approximate" ? "~" : ""}${row2.utilizationPercent === undefined ? "?" : Math.round(row2.utilizationPercent)}%`;
3034
+ const status = width < 28 ? {
3035
+ running: "RUN",
3036
+ idle: "IDLE",
3037
+ completed: "DONE",
3038
+ failed: "FAIL",
3039
+ unknown: "?"
3040
+ }[row2.node.status] : panelStatuses[row2.node.status];
3041
+ const distance = thresholdDistance(row2, true);
3042
+ const pad = (value, size) => {
3043
+ const text2 = clip(value, size);
3044
+ return text2 + " ".repeat(Math.max(0, size - cells(text2)));
3045
+ };
3046
+ const lines = width >= 72 ? [tableLine(identity, status, percent, distance, width)] : [
3047
+ `${pad(identity, Math.max(1, width - status.length - 1))} ${status}`,
3048
+ clip(`${percent} context \xB7 ${distance}`, width)
3049
+ ];
3050
+ const exception = [
3051
+ freshnessLabel(row2),
3052
+ compactionLabel(row2),
3053
+ row2.node.interrupted ? "interrupted" : "",
3054
+ row2.hidden ? `+${row2.hidden} hidden \xB7 Space expands` : ""
3055
+ ].filter(Boolean).join(" \xB7 ");
3056
+ if (exception)
3057
+ lines.push(...wrap(exception, width));
3058
+ return lines.map((line) => clip(line, width));
3059
+ }
3060
+ var panelStatuses;
3061
+ var init_panelformat = __esm(() => {
3062
+ init_format();
3063
+ init_statusformat();
3064
+ init_statusformat();
3065
+ panelStatuses = {
3066
+ running: "RUNNING",
3067
+ idle: "IDLE",
3068
+ completed: "DONE",
3069
+ failed: "FAILED",
3070
+ unknown: "UNKNOWN"
3071
+ };
3072
+ });
3073
+
2630
3074
  // src/dashboard/terminalview.ts
2631
- function overviewRow(row2) {
3075
+ function diagnosticRow(row2) {
2632
3076
  const s = row2.snapshot;
2633
3077
  const valid2 = row2.freshness === "fresh" || row2.freshness === "stale";
2634
- return `${" ".repeat(Math.min(row2.depth, 12))}${row2.depth ? "\u2514 " : ""}${row2.node.agent ?? "unknown agent"} / ${row2.node.model ?? "unknown model"} [${row2.node.sessionId}] ${row2.node.status} | prompt ${quantity(valid2 ? s?.measurement?.tokens : undefined)}/${quantity(s?.capacityTokens)} | occupancy ${row2.utilizationPercent?.toFixed(1) ?? "?"}% | trigger ${quantity(s?.policy.triggerTokens)} headroom ${quantity(row2.triggerDeltaTokens)} | pressure ${row2.pressure} | compact ${s?.compaction.phase ?? "unavailable"} | performance ${s?.performance?.status ?? "unavailable"} | live TPS ${quantity(row2.performanceAvailable && row2.performanceFreshness !== "invalid" ? s?.performance?.liveTps : undefined)} | age ${row2.measurementAgeMs === undefined ? "unknown" : `${Math.floor(row2.measurementAgeMs / 1000)}s`} | inventory ${row2.inventoryStale ? "stale" : "fresh"}, live ${row2.liveStale ? "stale" : "fresh"}, usage ${row2.freshness}, performance ${row2.performanceFreshness}${s?.measurement?.confidence === "approximate" ? " estimated" : ""}${s?.performance?.tokenSource === "estimated" ? " estimated TPS" : ""}${s?.performance?.partial ? " partial" : ""}${row2.hidden ? ` | ${row2.hidden} hidden descendants` : ""}`;
3078
+ return `${" ".repeat(Math.min(row2.depth, 12))}${row2.depth ? "\u2514 " : ""}${row2.node.agent ?? "unknown agent"} / ${row2.node.model ?? "unknown model"} [${row2.node.sessionId}] ${row2.node.status} | ${row2.freshness === "stale" ? "Last measured" : "Context"} ${quantity(valid2 ? s?.measurement?.tokens : undefined)}/${quantity(s?.capacityTokens)} | occupancy ${row2.utilizationPercent?.toFixed(1) ?? "?"}% | threshold ${quantity(s?.policy.triggerTokens)} \xB7 ${thresholdDistance(row2)} | ${pressureLabel(row2)} | compact ${s?.compaction.phase ?? "unavailable"} | performance ${s?.performance?.status ?? "unavailable"} | live TPS ${quantity(row2.performanceAvailable && row2.performanceFreshness !== "invalid" ? s?.performance?.liveTps : undefined)} | age ${row2.measurementAgeMs === undefined ? "unknown" : `${Math.floor(row2.measurementAgeMs / 1000)}s`} | inventory ${row2.inventoryStale ? "stale" : "fresh"}, live ${row2.liveStale ? "stale" : "fresh"}, usage ${row2.freshness}, performance ${row2.performanceFreshness}${s?.measurement?.confidence === "approximate" ? " estimated" : ""}${s?.performance?.tokenSource === "estimated" ? " estimated TPS" : ""}${s?.performance?.partial ? " partial" : ""}${row2.hidden ? ` | ${row2.hidden} hidden descendants` : ""}`;
3079
+ }
3080
+ function pad(value, width) {
3081
+ const text2 = clip(value, width);
3082
+ return text2 + " ".repeat(Math.max(0, width - cells(text2)));
3083
+ }
3084
+ function overviewColumns(identity, state, context, threshold, width) {
3085
+ return `${pad(identity, width - 44)} ${pad(state, 7)} ${pad(context, 7)} ${pad(threshold, 27)}`;
3086
+ }
3087
+ function overviewHeading(width) {
3088
+ return width >= 72 ? [overviewColumns("Agent / model", "State", "Context", "Threshold", width)] : [];
3089
+ }
3090
+ function overviewLabels(rows) {
3091
+ const segmenter2 = new Intl.Segmenter(undefined, { granularity: "grapheme" });
3092
+ const suffixes = rows.map((row2) => [...segmenter2.segment(clip(row2.node.sessionId, 4096))].slice(-8).map((part) => part.segment).join(""));
3093
+ const counts = new Map;
3094
+ for (const suffix of suffixes)
3095
+ counts.set(suffix, (counts.get(suffix) ?? 0) + 1);
3096
+ return suffixes.map((suffix, index) => counts.get(suffix) === 1 ? suffix : `#${index + 1}`);
3097
+ }
3098
+ function overviewMetrics(row2) {
3099
+ const valid2 = row2.freshness === "fresh" || row2.freshness === "stale";
3100
+ const percent = `${valid2 && row2.snapshot?.measurement?.confidence === "approximate" ? "~" : ""}${valid2 && row2.utilizationPercent !== undefined ? Math.round(row2.utilizationPercent) : "?"}%`;
3101
+ const context = `${row2.freshness === "stale" ? "Last measured " : ""}${percent}`;
3102
+ const distance = thresholdDistance(row2, true);
3103
+ const threshold = (distance === "At threshold \xB7 requires more" ? "At threshold; requires more" : distance).replace(" \xB7 Threshold reached", " \xB7 reached");
3104
+ const metric2 = `${context} context \xB7 ${threshold}`;
3105
+ 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");
3106
+ const shortMetric = `${context} \xB7 T ${shortThreshold}`;
3107
+ 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";
3108
+ const metrics = [metric2, shortMetric, `${context} \xB7 ${pressure}`];
3109
+ return { percent, context, threshold, pressure, metrics };
3110
+ }
3111
+ function overviewRowHeight(row2, width, maxLines) {
3112
+ const base = width >= 72 ? 1 : 2;
3113
+ const exception = row2.freshness !== "fresh" || row2.node.status === "failed" || row2.node.interrupted || !!compactionLabel(row2) || row2.inventoryStale || row2.liveStale || row2.hidden > 0;
3114
+ if (exception)
3115
+ return Math.min(maxLines, base + 1);
3116
+ if (width < 72 && !overviewMetrics(row2).metrics.some((line) => cells(line) <= width))
3117
+ return Math.min(maxLines, base + 1);
3118
+ return Math.min(maxLines, base);
3119
+ }
3120
+ function overviewRowLines(row2, width, selected, label, position, maxLines = 3) {
3121
+ const { percent, context, threshold, pressure, metrics } = overviewMetrics(row2);
3122
+ const state = width < 28 ? {
3123
+ running: "RUN",
3124
+ idle: "IDLE",
3125
+ completed: "DONE",
3126
+ failed: "FAIL",
3127
+ unknown: "?"
3128
+ }[row2.node.status] : panelStatuses[row2.node.status];
3129
+ const identityWidth = width >= 72 ? width - 44 : Math.max(1, width - cells(state) - 1);
3130
+ const marker = selected ? "> " : " ";
3131
+ const tag = width >= 100 ? ` [${label}]` : ` #${position + 1}`;
3132
+ const indent = row2.depth && identityWidth >= 22 ? `${" ".repeat(Math.min(row2.depth, Math.max(0, Math.floor(identityWidth / 8) - 1)))}\u2514 ` : "";
3133
+ const name = `${row2.node.agent ?? "unknown agent"}${width >= 40 ? ` / ${row2.node.model ?? "unknown model"}` : ""}`;
3134
+ const identity = marker + indent + clip(name, Math.max(1, identityWidth - cells(marker + indent + tag))) + tag;
3135
+ const fittedMetric = metrics.find((line) => cells(line) <= width);
3136
+ const lines = width >= 72 ? [
3137
+ overviewColumns(identity, state, row2.freshness === "stale" ? "Last" : percent, threshold, width)
3138
+ ] : [`${pad(identity, identityWidth)} ${state}`, fittedMetric ?? context];
3139
+ const lifecycle = compactionLabel(row2);
3140
+ const exceptions = [
3141
+ ...width >= 72 && row2.freshness === "stale" ? [context] : [],
3142
+ ...width < 72 && !fittedMetric ? [pressure] : [],
3143
+ ...row2.node.status === "failed" ? ["Agent failed"] : [],
3144
+ ...row2.node.interrupted ? ["Interrupted"] : [],
3145
+ ...lifecycle ? [cells(lifecycle) <= width - 6 ? lifecycle : compactionLabel(row2, true)] : [],
3146
+ ...row2.freshness !== "fresh" ? [freshnessLabel(row2)] : [],
3147
+ ...row2.inventoryStale ? ["Inventory stale"] : [],
3148
+ ...row2.liveStale ? ["Live data stale"] : [],
3149
+ ...row2.hidden ? [`${row2.hidden} hidden descendants`] : []
3150
+ ].filter((value) => value !== undefined);
3151
+ if (exceptions.length) {
3152
+ let shown = 1;
3153
+ let text2 = exceptions[0] ?? "";
3154
+ while (shown < exceptions.length && cells(text2 + " \xB7 " + exceptions[shown]) <= width - 6) {
3155
+ text2 += " \xB7 " + exceptions[shown++];
3156
+ }
3157
+ const more = shown < exceptions.length ? ` +${exceptions.length - shown}` : "";
3158
+ lines.push(clip(text2, Math.max(0, width - cells(more))) + more);
3159
+ }
3160
+ return lines.slice(0, Math.max(1, maxLines)).map((line) => clip(line, width));
2635
3161
  }
2636
3162
  function pathValue(object2, path) {
2637
3163
  let value = object2;
@@ -2646,8 +3172,11 @@ function terminalDetail(row2) {
2646
3172
  if (!row2)
2647
3173
  return ["No selected session; choose one in overview."];
2648
3174
  return [
2649
- overviewRow(row2),
2650
3175
  `Session ID: ${row2.node.sessionId}`,
3176
+ ...detailLines(row2, 4096, false, row2.measurementAgeMs),
3177
+ ...row2.node.interrupted ? ["Agent execution interrupted"] : [],
3178
+ "Diagnostic codes and accounting:",
3179
+ diagnosticRow(row2),
2651
3180
  `Parent ID: ${row2.node.parentId ?? "unknown/unavailable"}`,
2652
3181
  "All snapshot fields (timestamps are Unix milliseconds):",
2653
3182
  ...snapshotFields.map((path) => `${path}: ${scalar(pathValue(row2.snapshot, path))}`),
@@ -2656,6 +3185,8 @@ function terminalDetail(row2) {
2656
3185
  }
2657
3186
  function historyObservations(view, metricIndex) {
2658
3187
  const metric2 = chartMetrics[metricIndex] ?? chartMetrics[0];
3188
+ if (!view.history.result?.enabled)
3189
+ return [];
2659
3190
  return view.history.result?.data.series.flatMap((series) => metric2.source === "points" ? series.points.map((point) => ({
2660
3191
  time: point.time,
2661
3192
  value: point[metric2.key],
@@ -2684,14 +3215,15 @@ function terminalHistory(view, width, metricIndex, sample) {
2684
3215
  ];
2685
3216
  const selected = observations[Math.min(sample, Math.max(0, observations.length - 1))];
2686
3217
  return [
2687
- `History: ${h.status}${h.stale ? " \xB7 stale cached results" : ""} \xB7 ${h.windowMs / 60000} minutes \xB7 ${metric2.label} (${metric2.unit})`,
3218
+ `History: ${h.status} \xB7 ${h.windowMs / 60000} minutes \xB7 ${metric2.label} (${metric2.unit})`,
3219
+ ...historyStatusLines(h),
2688
3220
  "Left/Right metric \xB7 w window \xB7 [/] exact sample/request \xB7 Up/Down scroll",
2689
3221
  "Retention bounded to seven days; maximum 500 points and 500 requests per session.",
2690
3222
  ...h.notices,
2691
- ...h.summary ? [
3223
+ ...h.result?.enabled && h.summary ? [
2692
3224
  `Matched requests ${h.summary.matchedRequests}/${h.summary.observedRequests}; output ${scalar(h.summary.outputTokens)}; duration ms ${scalar(h.summary.durationMs)}; average model TPS ${scalar(h.summary.averageModelTps)}${h.summary.partial ? " \xB7 partial" : ""}`
2693
3225
  ] : [],
2694
- ...h.from === undefined || h.to === undefined ? [] : [
3226
+ ...!h.result?.enabled || h.from === undefined || h.to === undefined ? [] : [
2695
3227
  `${new Date(h.from).toISOString()} \u2192 ${new Date(h.to).toISOString()}`,
2696
3228
  ...metricPlot(observations.map((p) => ({
2697
3229
  time: p.time,
@@ -2703,9 +3235,9 @@ function terminalHistory(view, width, metricIndex, sample) {
2703
3235
  `Exact ${metric2.source === "points" ? "sample" : "request"} ${Math.min(sample + 1, observations.length)}/${observations.length}:`,
2704
3236
  ...Object.entries(selected.details).map(([key, value]) => `${key}: ${scalar(value)}`),
2705
3237
  `Selected ${metric2.label}: ${scalar(selected.value)} ${metric2.unit}`
2706
- ] : [
3238
+ ] : h.status === "ready" && h.result?.enabled ? [
2707
3239
  "No retained measurements for this metric; values unknown/unavailable."
2708
- ]
3240
+ ] : []
2709
3241
  ];
2710
3242
  }
2711
3243
  function wrapped(lines, width) {
@@ -2715,17 +3247,33 @@ function wrapped(lines, width) {
2715
3247
  return cells(clean) <= available ? [clean] : wrap(clean, available);
2716
3248
  });
2717
3249
  }
2718
- function headerLines(view, width) {
3250
+ function headerLines(view, width, height = 24) {
3251
+ const compact = width < 28;
3252
+ const small = height < 18;
3253
+ if (width < 16 || height < 8)
3254
+ return [
3255
+ clip(connectionLabel(view.connection, true), width),
3256
+ clip(panelStatus(view.health), width),
3257
+ clip(`Location: ${view.location}`, width)
3258
+ ];
2719
3259
  return [
2720
- clip(`Context Guard \xB7 ${view.screen} \xB7 ${view.connection} \xB7 enforcement ${view.health?.enforcement ?? "unknown"}`, width),
2721
- clip(`Known ${view.inventory.total} \xB7 monitored ${view.inventory.monitored} \xB7 active ${view.inventory.active} \xB7 inventory ${view.inventory.stale ? "stale" : "fresh"}${view.inventory.complete ? "" : " / partial"}`, width),
2722
- clip(`Location: ${view.location}`, width)
3260
+ clip(`${connectionLabel(view.connection, compact)} \xB7 ${compact ? width >= 20 ? "Context Guard" : "Guard" : `Context Guard \xB7 ${view.screen}`}`, width),
3261
+ ...monitorStatusLines(view.connection, view.health, view.rows.some((row2) => row2.snapshot?.measurement !== undefined), compact).flatMap((line) => wrap(line, width)),
3262
+ ...small ? [
3263
+ clip(`Selected: ${view.selected?.node.agent ?? view.selectedSessionId ?? "none"}`, width)
3264
+ ] : [
3265
+ clip(`Known ${view.inventory.total} \xB7 monitored ${view.inventory.monitored} \xB7 active ${view.inventory.active} \xB7 inventory ${view.inventory.stale ? "stale" : "fresh"}${view.inventory.complete ? "" : " / partial"}`, width),
3266
+ clip(`Location: ${view.location}`, width)
3267
+ ]
2723
3268
  ];
2724
3269
  }
2725
3270
  var snapshotFields, scalar = (value) => value === undefined || value === null ? "unknown/unavailable" : typeof value === "number" && !Number.isFinite(value) ? "invalid" : String(value), terminalHelp;
2726
3271
  var init_terminalview = __esm(() => {
3272
+ init_historyclient();
2727
3273
  init_chartformat();
2728
3274
  init_format();
3275
+ init_statusformat();
3276
+ init_panelformat();
2729
3277
  snapshotFields = [
2730
3278
  "sessionId",
2731
3279
  "parentId",
@@ -2798,6 +3346,9 @@ var init_terminalview = __esm(() => {
2798
3346
  "Models charts: Left/Right metric \xB7 w window \xB7 [/] bucket \xB7 d request details",
2799
3347
  "Models list/request details: n/p pages \xB7 f exact agent filter \xB7 r refresh",
2800
3348
  "Up/Down or j/k: select session in overview; scroll other screens",
3349
+ "Overview: compact columns/cards; Enter or 2 for full identity and diagnostics",
3350
+ "Overview PageUp/PageDown selects by visible rows; #N identifies a row in narrow layouts",
3351
+ "Overview T means threshold; +N means more diagnostics in Details",
2801
3352
  "PageUp/PageDown: move by a page \xB7 Home/End: first/last",
2802
3353
  "Left/Right: history metric (six metrics) \xB7 w: cycle 5m/1h/24h/7d",
2803
3354
  "[ / ]: previous/next exact history sample or request",
@@ -2823,6 +3374,7 @@ import { isAbsolute as isAbsolute3 } from "path";
2823
3374
  class TerminalShell {
2824
3375
  controller;
2825
3376
  quit;
3377
+ work = { formattedOverviewRows: 0 };
2826
3378
  scroll = 0;
2827
3379
  metric = 0;
2828
3380
  sample = 0;
@@ -2836,6 +3388,11 @@ class TerminalShell {
2836
3388
  input = "";
2837
3389
  notice = "";
2838
3390
  rowOffset = 0;
3391
+ overviewScope = "";
3392
+ overviewPage = 1;
3393
+ labelKey = "";
3394
+ labels = [];
3395
+ layouts = new Map;
2839
3396
  historyIdentity = "";
2840
3397
  constructor(controller, quit) {
2841
3398
  this.controller = controller;
@@ -3024,7 +3581,7 @@ class TerminalShell {
3024
3581
  const delta = name === "down" || name === "j" ? 1 : name === "up" || name === "k" ? -1 : name === "pagedown" ? page : name === "pageup" ? -page : 0;
3025
3582
  if (view.screen === "overview" && !this.help) {
3026
3583
  const index = view.rows.findIndex((row3) => row3.node.sessionId === view.selectedSessionId);
3027
- const next = name === "home" ? 0 : name === "end" ? view.rows.length - 1 : Math.max(0, Math.min(view.rows.length - 1, index + delta));
3584
+ const next = name === "home" ? 0 : name === "end" ? view.rows.length - 1 : Math.max(0, Math.min(view.rows.length - 1, index + (name === "pagedown" ? this.overviewPage : name === "pageup" ? -this.overviewPage : delta)));
3028
3585
  const row2 = view.rows[next];
3029
3586
  if (row2 && (delta || name === "home" || name === "end"))
3030
3587
  this.controller.selectSession(row2.node.sessionId);
@@ -3055,18 +3612,24 @@ class TerminalShell {
3055
3612
  this.scroll = 0;
3056
3613
  this.historyIdentity = identity;
3057
3614
  }
3058
- const footer = this.editor ? `${this.editor}> ${this.input} [Enter apply; Esc cancel${this.editor === "location" ? "; Tab remembered" : ""}]` : "? help q quit \xB7 1 overview 2 detail 3 history 4 models \xB7 / search f filters l location r refresh";
3615
+ const footer = this.editor ? `${this.editor}> ${this.input} [Enter apply; Esc cancel${this.editor === "location" ? "; Tab remembered" : ""}]` : view.screen === "overview" && !this.help ? width < 40 ? width >= 22 ? "2 detail ? help q quit" : "2 detail ? q quit" : width < 72 ? "2 detail \xB7 q quit \xB7 ? help \xB7 \u2191\u2193 select" : "Enter detail \xB7 q quit \xB7 ? help \xB7 \u2191\u2193 select \xB7 3 history 4 models / search f filters l location r refresh" : width < 40 ? "? help q quit" : "? help q quit \xB7 1 overview 2 detail 3 history 4 models \xB7 / search f filters l location r refresh";
3059
3616
  if (height < 5)
3060
3617
  return [
3061
3618
  clip("Context Guard \xB7 ? help \xB7 q quit", width),
3062
3619
  ...wrapped([footer], width)
3063
3620
  ].slice(0, height).join("\n");
3064
- const header = headerLines(view, width);
3065
- const available = height - header.length - 1;
3621
+ const header = headerLines(view, width, height);
3622
+ if (view.screen === "overview" && !this.help) {
3623
+ while (header.length > height - 2 && /^(Known |Location: |Selected: )/.test(header.at(-1) ?? ""))
3624
+ header.pop();
3625
+ header.splice(Math.max(0, height - 2));
3626
+ }
3627
+ const available = Math.max(0, height - header.length - 1);
3066
3628
  const notices = [
3629
+ ...view.screen === "overview" && view.selectedSessionId && !view.rows.some((row2) => row2.node.sessionId === view.selectedSessionId) ? ["Selection hidden by filters \xB7 \u2191\u2193 selects a visible session"] : [],
3067
3630
  ...view.notices,
3068
3631
  ...view.inventory.notices,
3069
- ...view.health?.reasons ?? [],
3632
+ ...view.screen === "detail" ? view.health?.reasons.map((reason) => `Health reason code: ${reason}`) ?? [] : [],
3070
3633
  ...this.notice ? [this.notice] : [],
3071
3634
  ...view.screen !== "models" && Object.values(view.filters).some(Boolean) ? [
3072
3635
  `Filters: search=${view.filters.search} agent=${view.filters.agent} model=${view.filters.model}`
@@ -3076,23 +3639,86 @@ class TerminalShell {
3076
3639
  if (this.help)
3077
3640
  body = wrapped(terminalHelp, width);
3078
3641
  else if (view.screen === "overview") {
3079
- const rows = view.rows.map((row2) => wrapped([
3080
- `${row2.node.sessionId === view.selectedSessionId ? "> " : " "}${overviewRow(row2)}`
3081
- ], width));
3642
+ const scope = JSON.stringify([view.location, view.filters]);
3643
+ if (scope !== this.overviewScope) {
3644
+ this.rowOffset = 0;
3645
+ this.overviewScope = scope;
3646
+ }
3647
+ const heading = available >= 4 ? overviewHeading(width) : [];
3648
+ const noticeBudget = Math.min(notices.length, Math.floor((available - heading.length) / 4));
3649
+ const status = notices.slice(0, noticeBudget).map((notice, index) => clip(index === noticeBudget - 1 && notices.length > noticeBudget ? `${notices.length} notices \xB7 see Details` : notice, width));
3650
+ const budget = Math.max(1, available - heading.length - status.length);
3651
+ const labelKey = view.rows.map((row2) => row2.node.sessionId).join("\0");
3652
+ if (labelKey !== this.labelKey) {
3653
+ this.labels = overviewLabels(view.rows);
3654
+ this.labelKey = labelKey;
3655
+ }
3656
+ const ids = new Set(view.rows.map((row2) => row2.node.sessionId));
3657
+ for (const id of this.layouts.keys())
3658
+ if (!ids.has(id))
3659
+ this.layouts.delete(id);
3660
+ const heights = view.rows.map((row2) => {
3661
+ const key = JSON.stringify([
3662
+ width,
3663
+ budget,
3664
+ row2.node,
3665
+ row2.depth,
3666
+ row2.freshness,
3667
+ row2.pressure,
3668
+ row2.triggerDeltaTokens,
3669
+ row2.utilizationPercent,
3670
+ row2.snapshot?.measurement?.confidence,
3671
+ row2.snapshot?.compaction,
3672
+ row2.inventoryStale,
3673
+ row2.liveStale,
3674
+ row2.hidden
3675
+ ]);
3676
+ let layout = this.layouts.get(row2.node.sessionId);
3677
+ if (!layout || layout.key !== key) {
3678
+ layout = { key, height: overviewRowHeight(row2, width, budget) };
3679
+ this.layouts.set(row2.node.sessionId, layout);
3680
+ }
3681
+ return layout.height;
3682
+ });
3082
3683
  const selected = view.rows.findIndex((row2) => row2.node.sessionId === view.selectedSessionId);
3083
- const start = rows.slice(0, Math.max(0, selected)).reduce((n, row2) => n + row2.length, 0);
3084
- const end = start + (rows[selected]?.length ?? 0);
3085
- const status = wrapped(notices, width);
3086
- const budget = Math.max(1, available - Math.min(status.length, Math.floor(available / 3)));
3684
+ const start = heights.slice(0, Math.max(0, selected)).reduce((n, h) => n + h, 0);
3685
+ const end = start + (heights[selected] ?? 0);
3087
3686
  if (start < this.rowOffset)
3088
3687
  this.rowOffset = start;
3089
3688
  if (end > this.rowOffset + budget)
3090
3689
  this.rowOffset = Math.max(start, end - budget);
3091
- const flat = rows.flat();
3092
- this.rowOffset = Math.max(0, Math.min(this.rowOffset, Math.max(0, flat.length - budget)));
3690
+ const total = heights.reduce((n, h) => n + h, 0);
3691
+ this.rowOffset = Math.max(0, Math.min(this.rowOffset, Math.max(0, total - budget)));
3692
+ const visible = [];
3693
+ let cursor = 0, page = 0;
3694
+ for (const [index, row2] of view.rows.entries()) {
3695
+ const rowHeight = heights[index] ?? 1;
3696
+ if (cursor >= this.rowOffset && cursor + rowHeight <= this.rowOffset + budget)
3697
+ page++;
3698
+ if (cursor + rowHeight > this.rowOffset && cursor < this.rowOffset + budget) {
3699
+ const layout = this.layouts.get(row2.node.sessionId);
3700
+ const chosen = row2.node.sessionId === view.selectedSessionId;
3701
+ const label = this.labels[index] ?? `#${index + 1}`;
3702
+ if (layout) {
3703
+ if (!layout.lines || layout.selected !== chosen || layout.label !== label || layout.position !== index) {
3704
+ this.work.formattedOverviewRows++;
3705
+ layout.lines = overviewRowLines(row2, width, chosen, label, index, budget);
3706
+ layout.selected = chosen;
3707
+ layout.label = label;
3708
+ layout.position = index;
3709
+ }
3710
+ visible.push(...layout.lines.slice(Math.max(0, this.rowOffset - cursor), this.rowOffset + budget - cursor));
3711
+ }
3712
+ }
3713
+ cursor += rowHeight;
3714
+ }
3715
+ this.overviewPage = Math.max(1, page);
3093
3716
  body = [
3094
- ...status.slice(0, available - budget),
3095
- ...flat.length ? flat.slice(this.rowOffset, this.rowOffset + budget) : ["No matching sessions; / search, f filters, r refresh."]
3717
+ ...status,
3718
+ ...heading,
3719
+ ...total ? visible : [
3720
+ clip("No matching sessions; / search, f filters, r refresh.", width)
3721
+ ]
3096
3722
  ];
3097
3723
  return [
3098
3724
  ...header,
@@ -3135,7 +3761,7 @@ class TerminalShell {
3135
3761
  }
3136
3762
  }
3137
3763
  function mountTerminal(controller, renderer, text2, finished) {
3138
- let closed = false;
3764
+ let closed = false, pendingDraw = false, drawing = false;
3139
3765
  let unsubscribe = () => {
3140
3766
  };
3141
3767
  const shutdown = (failed = false) => {
@@ -3166,25 +3792,38 @@ function mountTerminal(controller, renderer, text2, finished) {
3166
3792
  const onSignal = () => shutdown();
3167
3793
  const signals = ["SIGINT", "SIGTERM", "SIGHUP"];
3168
3794
  const draw = () => {
3169
- if (closed)
3795
+ if (closed || drawing)
3170
3796
  return;
3797
+ pendingDraw = false;
3798
+ drawing = true;
3171
3799
  try {
3172
3800
  text2.content = shell.frame(renderer.width, renderer.height);
3173
3801
  } catch {
3174
3802
  fail();
3803
+ } finally {
3804
+ drawing = false;
3175
3805
  }
3176
3806
  };
3807
+ const scheduleDraw = () => {
3808
+ if (pendingDraw || closed || drawing)
3809
+ return;
3810
+ pendingDraw = true;
3811
+ queueMicrotask(() => {
3812
+ if (pendingDraw)
3813
+ draw();
3814
+ });
3815
+ };
3177
3816
  const key = (event) => {
3178
3817
  try {
3179
3818
  shell.key(event, Math.max(1, renderer.height - 4));
3180
- draw();
3819
+ scheduleDraw();
3181
3820
  } catch {
3182
3821
  fail();
3183
3822
  }
3184
3823
  };
3185
3824
  const paste = (event) => {
3186
3825
  shell.paste(new TextDecoder().decode(event.bytes));
3187
- draw();
3826
+ scheduleDraw();
3188
3827
  };
3189
3828
  try {
3190
3829
  renderer.root.add(text2);
@@ -3198,7 +3837,8 @@ function mountTerminal(controller, renderer, text2, finished) {
3198
3837
  process.on(signal, onSignal);
3199
3838
  process.on("uncaughtException", fail);
3200
3839
  process.on("unhandledRejection", fail);
3201
- unsubscribe = controller.subscribe(draw);
3840
+ unsubscribe = controller.subscribe(scheduleDraw);
3841
+ draw();
3202
3842
  if (closed) {
3203
3843
  unsubscribe();
3204
3844
  return { shell, shutdown };
@@ -3303,6 +3943,11 @@ function reconcile(a, b) {
3303
3943
  };
3304
3944
  }
3305
3945
  function analyzeModelRequests(records, options) {
3946
+ const accumulator = createModelAccumulator(options);
3947
+ accumulator.append(records);
3948
+ return accumulator.result();
3949
+ }
3950
+ function createModelAccumulator(options) {
3306
3951
  const maxRequests = options.maxRequests ?? 1e5;
3307
3952
  if (!Number.isSafeInteger(maxRequests) || maxRequests < 1 || maxRequests > 1e6)
3308
3953
  throw new Error("maxRequests must be an integer from 1 to 1000000");
@@ -3313,7 +3958,7 @@ function analyzeModelRequests(records, options) {
3313
3958
  throw new Error("Invalid analytics timestamp");
3314
3959
  if (options.from !== undefined && options.to !== undefined && options.from > options.to)
3315
3960
  throw new Error("--from must precede --to");
3316
- const coverage = {
3961
+ const counters = {
3317
3962
  source: "retained-request-records",
3318
3963
  scannedRecords: 0,
3319
3964
  unscannedRecords: 0,
@@ -3327,74 +3972,98 @@ function analyzeModelRequests(records, options) {
3327
3972
  partial: false
3328
3973
  };
3329
3974
  const requests = new Map;
3330
- for (const record2 of records) {
3331
- coverage.scannedRecords++;
3332
- if (record2.type === "gap") {
3333
- coverage.gapRecords++;
3334
- continue;
3335
- }
3336
- if (record2.type !== "request")
3337
- continue;
3338
- const fact = requestFact(record2);
3339
- if (!fact) {
3340
- coverage.missingIdentityRecords++;
3341
- continue;
3342
- }
3343
- fact.location = options.location;
3344
- const key = JSON.stringify([
3345
- fact.sessionId,
3346
- fact.requestId,
3347
- fact.startedAt
3348
- ]);
3349
- const previous = requests.get(key);
3350
- if (previous) {
3351
- coverage.duplicateRecords++;
3352
- const merged = reconcile(previous.fact, fact);
3353
- if (!merged)
3354
- previous.conflict = true;
3355
- else
3356
- previous.fact = merged;
3357
- } else {
3358
- if (requests.size === maxRequests) {
3359
- coverage.scannedRecords--;
3360
- coverage.unscannedRecords = records.length - coverage.scannedRecords;
3975
+ let totalRecords = 0, stopped = false;
3976
+ function append(records) {
3977
+ totalRecords += records.length;
3978
+ for (const record2 of records) {
3979
+ if (stopped)
3361
3980
  break;
3981
+ counters.scannedRecords++;
3982
+ if (record2.type === "gap") {
3983
+ counters.gapRecords++;
3984
+ continue;
3985
+ }
3986
+ if (record2.type !== "request")
3987
+ continue;
3988
+ const fact = requestFact(record2);
3989
+ if (!fact) {
3990
+ counters.missingIdentityRecords++;
3991
+ continue;
3992
+ }
3993
+ fact.location = options.location;
3994
+ const key = JSON.stringify([
3995
+ fact.sessionId,
3996
+ fact.requestId,
3997
+ fact.startedAt
3998
+ ]);
3999
+ const previous = requests.get(key);
4000
+ if (previous) {
4001
+ counters.duplicateRecords++;
4002
+ const merged = reconcile(previous.fact, fact);
4003
+ if (!merged)
4004
+ previous.conflict = true;
4005
+ else
4006
+ previous.fact = merged;
4007
+ } else {
4008
+ if (requests.size === maxRequests) {
4009
+ counters.scannedRecords--;
4010
+ stopped = true;
4011
+ break;
4012
+ }
4013
+ requests.set(key, { fact, conflict: false });
3362
4014
  }
3363
- requests.set(key, { fact, conflict: false });
3364
4015
  }
3365
4016
  }
3366
- const accepted = [];
3367
- for (const { fact: r, conflict } of requests.values()) {
3368
- if (conflict) {
3369
- coverage.conflictingRequests++;
3370
- continue;
3371
- }
3372
- if (r.completedAt === undefined) {
3373
- coverage.incompleteRequests++;
3374
- continue;
3375
- }
3376
- const duration = r.completedAt - r.startedAt;
3377
- if (!nonnegative2(r.completedAt) || !Number.isFinite(duration) || duration <= 0) {
3378
- coverage.invalidTimingRequests++;
3379
- continue;
4017
+ function* steps() {
4018
+ const coverage = {
4019
+ ...counters,
4020
+ unscannedRecords: totalRecords - counters.scannedRecords
4021
+ };
4022
+ const accepted = [];
4023
+ for (const { fact: r, conflict } of requests.values()) {
4024
+ yield;
4025
+ if (conflict) {
4026
+ coverage.conflictingRequests++;
4027
+ continue;
4028
+ }
4029
+ if (r.completedAt === undefined) {
4030
+ coverage.incompleteRequests++;
4031
+ continue;
4032
+ }
4033
+ const duration = r.completedAt - r.startedAt;
4034
+ if (!nonnegative2(r.completedAt) || !Number.isFinite(duration) || duration <= 0) {
4035
+ coverage.invalidTimingRequests++;
4036
+ continue;
4037
+ }
4038
+ 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)
4039
+ continue;
4040
+ accepted.push({ ...r, completedAt: r.completedAt });
3380
4041
  }
3381
- 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)
3382
- continue;
3383
- accepted.push({ ...r, completedAt: r.completedAt });
4042
+ const models = yield* summarizeModelRequestSteps(accepted);
4043
+ 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);
4044
+ return {
4045
+ schemaVersion: 1,
4046
+ location: options.location,
4047
+ models,
4048
+ coverage,
4049
+ requests: accepted
4050
+ };
3384
4051
  }
3385
- const models = summarizeModelRequests(accepted);
3386
- 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);
3387
- return {
3388
- schemaVersion: 1,
3389
- location: options.location,
3390
- models,
3391
- coverage,
3392
- requests: accepted
3393
- };
4052
+ return { append, steps, result: () => finishSteps(steps()) };
4053
+ }
4054
+ function finishSteps(steps) {
4055
+ let step = steps.next();
4056
+ while (!step.done)
4057
+ step = steps.next();
4058
+ return step.value;
3394
4059
  }
3395
4060
  function summarizeModelRequests(requests) {
4061
+ return finishSteps(summarizeModelRequestSteps(requests));
4062
+ }
4063
+ function* summarizeModelRequestSteps(requests) {
3396
4064
  const groups = new Map;
3397
4065
  for (const r of requests) {
4066
+ yield;
3398
4067
  const duration = r.completedAt - r.startedAt;
3399
4068
  let group = groups.get(r.model);
3400
4069
  if (!group) {