@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/CHANGELOG.md +37 -0
- package/README.md +19 -11
- package/dist/cli.js +906 -237
- package/dist/core.js +88 -59
- package/dist/index.js +710 -166
- package/dist/tui.js +1416 -702
- package/dist/types/archive.d.ts +40 -0
- package/dist/types/core.d.ts +1 -1
- package/dist/types/dashboard/controller.d.ts +2 -0
- package/dist/types/dashboard/inventory.d.ts +1 -1
- package/dist/types/dashboard/terminal.d.ts +8 -0
- package/dist/types/dashboard/terminalview.d.ts +9 -2
- package/dist/types/dashboard/view.d.ts +4 -1
- package/dist/types/history.d.ts +39 -2
- package/dist/types/historyclient.d.ts +15 -0
- package/dist/types/historyserver.d.ts +2 -1
- package/dist/types/modelanalytics.d.ts +11 -0
- package/dist/types/modelanalyticsserver.d.ts +2 -1
- package/dist/types/tui/charts.d.ts +2 -3
- package/dist/types/tui/compactformat.d.ts +12 -3
- package/dist/types/tui/historystate.d.ts +49 -0
- package/dist/types/tui/inventory.d.ts +12 -0
- package/dist/types/tui/panelcontrols.d.ts +8 -0
- package/dist/types/tui/panelformat.d.ts +12 -0
- package/dist/types/tui/panelview.d.ts +22 -0
- package/dist/types/tui/quantity.d.ts +1 -0
- package/dist/types/tui/sidebar.d.ts +0 -5
- package/dist/types/tui/state.d.ts +3 -1
- package/dist/types/tui/statusformat.d.ts +19 -0
- package/package.json +2 -1
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
|
-
|
|
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
|
|
673
|
-
const
|
|
674
|
-
|
|
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
|
-
|
|
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
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
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
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
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
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
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
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
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
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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:
|
|
2277
|
-
|
|
2278
|
-
|
|
2279
|
-
|
|
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
|
|
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
|
-
|
|
2360
|
-
|
|
2361
|
-
|
|
2362
|
-
|
|
2363
|
-
|
|
2364
|
-
|
|
2365
|
-
|
|
2366
|
-
|
|
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
|
|
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
|
|
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} |
|
|
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}
|
|
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
|
-
|
|
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(
|
|
2721
|
-
|
|
2722
|
-
|
|
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
|
-
|
|
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
|
|
3080
|
-
|
|
3081
|
-
|
|
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 =
|
|
3084
|
-
const end = start + (
|
|
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
|
|
3092
|
-
this.rowOffset = Math.max(0, Math.min(this.rowOffset, Math.max(0,
|
|
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
|
|
3095
|
-
...
|
|
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
|
-
|
|
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
|
-
|
|
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(
|
|
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
|
|
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
|
-
|
|
3331
|
-
|
|
3332
|
-
|
|
3333
|
-
|
|
3334
|
-
|
|
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
|
-
|
|
3367
|
-
|
|
3368
|
-
|
|
3369
|
-
|
|
3370
|
-
|
|
3371
|
-
|
|
3372
|
-
|
|
3373
|
-
|
|
3374
|
-
|
|
3375
|
-
|
|
3376
|
-
|
|
3377
|
-
|
|
3378
|
-
|
|
3379
|
-
|
|
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
|
-
|
|
3382
|
-
|
|
3383
|
-
|
|
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
|
-
|
|
3386
|
-
|
|
3387
|
-
|
|
3388
|
-
|
|
3389
|
-
|
|
3390
|
-
|
|
3391
|
-
|
|
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) {
|