@tangle-network/agent-runtime 0.337.1 → 0.338.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{activation-DMehb166.d.ts → activation-CsbNax2X.d.ts} +92 -4
- package/dist/activation-kbkAuSmY.js +889 -0
- package/dist/activation-kbkAuSmY.js.map +1 -0
- package/dist/agent.d.ts +1 -1
- package/dist/agent.js +3 -6
- package/dist/agent.js.map +1 -1
- package/dist/{bundle-BPGjESee.js → bundle-DM60AI2B.js} +2 -2
- package/dist/{bundle-BPGjESee.js.map → bundle-DM60AI2B.js.map} +1 -1
- package/dist/candidate-execution/index.d.ts +2 -2
- package/dist/candidate-execution/index.js +4 -5
- package/dist/candidate-execution/index.js.map +1 -1
- package/dist/content-digest-sBTLujlK.js +9 -0
- package/dist/content-digest-sBTLujlK.js.map +1 -0
- package/dist/{delegate-aC14Cww3.js → delegate-Se1YZKMb.js} +2 -2
- package/dist/{delegate-aC14Cww3.js.map → delegate-Se1YZKMb.js.map} +1 -1
- package/dist/{coordination-driver-Dg9a6qsm.js → driver-control-acknowledgers-T2a1Ouay.js} +4514 -4440
- package/dist/driver-control-acknowledgers-T2a1Ouay.js.map +1 -0
- package/dist/durable.d.ts +1 -1
- package/dist/durable.js +223 -227
- package/dist/durable.js.map +1 -1
- package/dist/{graph-Cfx711ES.js → graph-CY0wH838.js} +65 -18
- package/dist/graph-CY0wH838.js.map +1 -0
- package/dist/{graph-registry-DAI6KNXB.js → graph-registry-By0tUj4S.js} +16 -15
- package/dist/graph-registry-By0tUj4S.js.map +1 -0
- package/dist/{improvement-cycle-DHKcb_YW.js → improve-CWGBT13K.js} +75 -1220
- package/dist/improve-CWGBT13K.js.map +1 -0
- package/dist/index.d.ts +6 -6
- package/dist/index.js +10 -13
- package/dist/index.js.map +1 -1
- package/dist/{integrations-DopfCC2a.d.ts → integrations-6xttbPi9.d.ts} +3 -3
- package/dist/integrations-dFi-ALk7.js.map +1 -1
- package/dist/intelligence.d.ts +7 -94
- package/dist/intelligence.js +505 -13
- package/dist/intelligence.js.map +1 -1
- package/dist/kernel.d.ts +2 -2
- package/dist/kernel.js +13 -12
- package/dist/{loop-runner-bin-B86AiPBf.d.ts → loop-runner-bin-CFnrAHpl.d.ts} +2 -2
- package/dist/{loop-runner-bin-BSKBNC38.js → loop-runner-bin-CdPlhy9g.js} +3 -3
- package/dist/{loop-runner-bin-BSKBNC38.js.map → loop-runner-bin-CdPlhy9g.js.map} +1 -1
- package/dist/loop-runner-bin.d.ts +1 -1
- package/dist/loop-runner-bin.js +1 -1
- package/dist/mcp/bin.js +3 -3
- package/dist/mcp/bin.js.map +1 -1
- package/dist/mcp/index.d.ts +7 -3
- package/dist/mcp/index.js +6 -8
- package/dist/mcp/index.js.map +1 -1
- package/dist/{openai-tools-DHO2QS1q.d.ts → openai-tools-zh2I_uD7.d.ts} +2 -2
- package/dist/platform.d.ts +7 -2
- package/dist/platform.js +16 -8
- package/dist/platform.js.map +1 -1
- package/dist/{prepare-CER-ZkPp.js → prepare-Bt0IouPq.js} +1241 -586
- package/dist/prepare-Bt0IouPq.js.map +1 -0
- package/dist/{profile-gNUsHNIJ.js → profile-Do-5xO_D.js} +89 -3
- package/dist/profile-Do-5xO_D.js.map +1 -0
- package/dist/profiles.d.ts +1 -1
- package/dist/profiles.js +1 -3
- package/dist/profiles.js.map +1 -1
- package/dist/{protected-model-port-BhpQRTw-.d.ts → protected-model-port-DGxQ8_YM.d.ts} +3 -3
- package/dist/{provision-supervisor-72YFf-i_.js → provision-supervisor-0jMufp4h.js} +12 -9
- package/dist/provision-supervisor-0jMufp4h.js.map +1 -0
- package/dist/record-value-B6bwJxLa.js +12 -0
- package/dist/record-value-B6bwJxLa.js.map +1 -0
- package/dist/{run-layout-Cu8H7aeh.js → run-cancellation-BoW5Sg9Y.js} +174 -95
- package/dist/run-cancellation-BoW5Sg9Y.js.map +1 -0
- package/dist/{runtime-qFLOq1R9.js → runtime-D37zRSs7.js} +432 -684
- package/dist/runtime-D37zRSs7.js.map +1 -0
- package/dist/{sandbox-profile-compat-QTkLe1Z6.js → sandbox-profile-compat-xSoVtdWw.js} +690 -697
- package/dist/sandbox-profile-compat-xSoVtdWw.js.map +1 -0
- package/dist/search-executor-DmgUm5p7.js +1916 -0
- package/dist/search-executor-DmgUm5p7.js.map +1 -0
- package/dist/{server-_FldJ9Cl.js → server-DQHJszfk.js} +4 -4
- package/dist/server-DQHJszfk.js.map +1 -0
- package/dist/{search-executor-CGxvizHh.js → structural-rollout-DAvIE8mw.js} +2262 -3984
- package/dist/structural-rollout-DAvIE8mw.js.map +1 -0
- package/dist/{supervise-H4BTN6uA.js → supervise-WuPAwfPh.js} +7793 -5338
- package/dist/supervise-WuPAwfPh.js.map +1 -0
- package/dist/{supervisor-C27ne4yv.js → supervisor-CPG1I9UN.js} +27379 -27899
- package/dist/supervisor-CPG1I9UN.js.map +1 -0
- package/dist/testing.d.ts +226 -1
- package/dist/testing.js +13 -14
- package/dist/testing.js.map +1 -1
- package/dist/{top-app-BaMw1W2z.js → top-app-C55Wa7uy.js} +339 -337
- package/dist/top-app-C55Wa7uy.js.map +1 -0
- package/dist/tui/bin.js +1 -1
- package/dist/tui/index.d.ts +17 -15
- package/dist/tui/index.js +2 -2
- package/dist/{types-CupCBY-j.d.ts → types-CrNWif8-.d.ts} +17655 -17406
- package/dist/{workspace-streams-JLjh5DC6.d.ts → workspace-streams-BZZds1Fw.d.ts} +18 -16
- package/package.json +4 -4
- package/skills/agent-graphs/SKILL.md +10 -3
- package/skills/agent-graphs/references/authoring.md +17 -0
- package/skills/profile-authoring/SKILL.md +3 -3
- package/skills/supervise/SKILL.md +2 -2
- package/dist/activation-BgWVlVu2.js +0 -234
- package/dist/activation-BgWVlVu2.js.map +0 -1
- package/dist/coordination-driver-Dg9a6qsm.js.map +0 -1
- package/dist/graph-Cfx711ES.js.map +0 -1
- package/dist/graph-registry-DAI6KNXB.js.map +0 -1
- package/dist/improvement-cycle-DHKcb_YW.js.map +0 -1
- package/dist/prepare-CER-ZkPp.js.map +0 -1
- package/dist/profile-gNUsHNIJ.js.map +0 -1
- package/dist/protected-model-port-BuRsviSL.js +0 -766
- package/dist/protected-model-port-BuRsviSL.js.map +0 -1
- package/dist/provision-supervisor-72YFf-i_.js.map +0 -1
- package/dist/run-layout-Cu8H7aeh.js.map +0 -1
- package/dist/runtime-qFLOq1R9.js.map +0 -1
- package/dist/sandbox-profile-compat-QTkLe1Z6.js.map +0 -1
- package/dist/search-executor-CGxvizHh.js.map +0 -1
- package/dist/server-_FldJ9Cl.js.map +0 -1
- package/dist/supervise-H4BTN6uA.js.map +0 -1
- package/dist/supervisor-C27ne4yv.js.map +0 -1
- package/dist/top-app-BaMw1W2z.js.map +0 -1
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { N as writeWorkerSteer, _ as safeWorkerFile, b as supervisorWorkersDir, f as legacySupervisorRunsRoot, l as cancelWorker, n as cancelRun, y as supervisorRunsRoot } from "./run-cancellation-BoW5Sg9Y.js";
|
|
2
2
|
import { randomUUID } from "node:crypto";
|
|
3
3
|
import { existsSync, readFileSync, readdirSync, statSync } from "node:fs";
|
|
4
4
|
import { basename, join, resolve } from "node:path";
|
|
5
5
|
import { emitKeypressEvents } from "node:readline";
|
|
6
|
-
//#region src/tui/top-
|
|
6
|
+
//#region src/tui/top-snapshot.ts
|
|
7
7
|
/**
|
|
8
|
-
* The
|
|
9
|
-
*
|
|
8
|
+
* The snapshot reader for the supervisor-run TUI: turn the on-disk run layout into a `TopSnapshot`.
|
|
9
|
+
* ANSI frame presentation lives in `./top-frame`; this module owns reading and projection.
|
|
10
10
|
*
|
|
11
11
|
* This module never derives a run path itself. Every directory and per-worker file it reads comes
|
|
12
12
|
* from `../runtime/supervise/run-layout` — the same module the writers use — so the reader cannot
|
|
@@ -29,20 +29,6 @@ const emptySpend = {
|
|
|
29
29
|
usd: 0,
|
|
30
30
|
ms: 0
|
|
31
31
|
};
|
|
32
|
-
const ansiPattern = /\x1b\[[0-9;?]*[ -/]*[@-~]/g;
|
|
33
|
-
const colors = {
|
|
34
|
-
reset: "\x1B[0m",
|
|
35
|
-
dim: "\x1B[2m",
|
|
36
|
-
bold: "\x1B[1m",
|
|
37
|
-
red: "\x1B[31m",
|
|
38
|
-
green: "\x1B[32m",
|
|
39
|
-
yellow: "\x1B[33m",
|
|
40
|
-
blue: "\x1B[34m",
|
|
41
|
-
magenta: "\x1B[35m",
|
|
42
|
-
cyan: "\x1B[36m",
|
|
43
|
-
white: "\x1B[37m",
|
|
44
|
-
inverse: "\x1B[7m"
|
|
45
|
-
};
|
|
46
32
|
/**
|
|
47
33
|
* Read every supervisor run under one workspace into a single point-in-time snapshot.
|
|
48
34
|
*
|
|
@@ -228,113 +214,6 @@ function buildSupervisorView(state, stateDir, events, progressTail, now, workerE
|
|
|
228
214
|
totals
|
|
229
215
|
};
|
|
230
216
|
}
|
|
231
|
-
/** Render one snapshot to an ANSI frame. Use this when nothing needs to be clickable. */
|
|
232
|
-
function renderTopFrame(snapshot, options = {}) {
|
|
233
|
-
return renderTopFrameWithLayout(snapshot, options).frame;
|
|
234
|
-
}
|
|
235
|
-
/**
|
|
236
|
-
* Render one snapshot, returning the frame together with the row→entity map a mouse click resolves
|
|
237
|
-
* against. The layout is the only thing that knows which row is which run or worker, so emitting it
|
|
238
|
-
* alongside the text is what keeps click handling out of the renderer.
|
|
239
|
-
*/
|
|
240
|
-
function renderTopFrameWithLayout(snapshot, options = {}) {
|
|
241
|
-
const width = Math.max(88, options.width ?? 128);
|
|
242
|
-
const height = Math.max(24, options.height ?? 42);
|
|
243
|
-
const color = options.color ?? false;
|
|
244
|
-
const focus = options.focus ?? "supervisors";
|
|
245
|
-
const mode = options.mode ?? "overview";
|
|
246
|
-
const selectedSupervisor = selectSupervisor(snapshot, options.selectedSupervisorId);
|
|
247
|
-
const selectedWorker = selectedSupervisor ? selectWorker(selectedSupervisor, options.selectedWorkerId) : void 0;
|
|
248
|
-
const targets = [];
|
|
249
|
-
const lines = [];
|
|
250
|
-
const push = (line = "", target) => {
|
|
251
|
-
if (lines.length >= height) return;
|
|
252
|
-
if (target) targets.push({
|
|
253
|
-
row: lines.length,
|
|
254
|
-
...target
|
|
255
|
-
});
|
|
256
|
-
lines.push(fitAnsi(line, width));
|
|
257
|
-
};
|
|
258
|
-
const aggregate = aggregateSnapshot(snapshot);
|
|
259
|
-
push(`${paint("SUPERVISOR TOP", "bold", color)} ${paint(snapshot.root, "dim", color)} runs ${aggregate.supervisors} workers ${aggregate.workers} live ${aggregate.running} done ${aggregate.done} down ${aggregate.down} ${formatMoney(aggregate.usd)} tok ${formatTokens(aggregate.tokensInput)}/${formatTokens(aggregate.tokensOutput)}`);
|
|
260
|
-
push(`${paint("keys", "dim", color)} up/down select left/right run tab focus enter detail o overview l log s steer c cancel mouse q quit`);
|
|
261
|
-
if (options.notice) push(paint(options.notice, "yellow", color));
|
|
262
|
-
if (snapshot.completeness === "partial") push(paint(`partial snapshot: ${snapshot.diagnostics.length} source${snapshot.diagnostics.length === 1 ? "" : "s"} skipped or truncated; runs loaded ${snapshot.loaded}/${snapshot.discovered}`, "yellow", color));
|
|
263
|
-
if (options.steerInput?.active) push(renderSteerInput(options.steerInput, width, color));
|
|
264
|
-
push(rule(width, color));
|
|
265
|
-
if (snapshot.supervisors.length === 0) {
|
|
266
|
-
if (snapshot.discovered > 0) {
|
|
267
|
-
push(paint("NO READABLE SUPERVISORS", "yellow", color));
|
|
268
|
-
push(`${snapshot.discovered} run director${snapshot.discovered === 1 ? "y" : "ies"} found under ${supervisorRunsRoot(snapshot.root)}, none readable`);
|
|
269
|
-
} else {
|
|
270
|
-
push(paint("NO SUPERVISORS", "yellow", color));
|
|
271
|
-
push(`No run state under ${supervisorRunsRoot(snapshot.root)}`);
|
|
272
|
-
}
|
|
273
|
-
push(`A run appears here once its driver writes state.json; re-point with: agent-runtime-top <root>`);
|
|
274
|
-
return {
|
|
275
|
-
frame: `${lines.join("\n")}\n`,
|
|
276
|
-
targets
|
|
277
|
-
};
|
|
278
|
-
}
|
|
279
|
-
push(sectionTitle("SUPERVISORS", focus === "supervisors", color));
|
|
280
|
-
push(` id status workers spend tok in/out latency task`);
|
|
281
|
-
for (const supervisor of snapshot.supervisors.slice(0, 9)) {
|
|
282
|
-
const selected = supervisor.id === selectedSupervisor?.id;
|
|
283
|
-
const status = statusText(supervisor.status, color);
|
|
284
|
-
const pointer = selected ? paint(">", "cyan", color) : " ";
|
|
285
|
-
const rowFocus = selected && focus === "supervisors" ? "inverse" : void 0;
|
|
286
|
-
const workerSummary = `${supervisor.totals.running}/${supervisor.totals.workers} live`;
|
|
287
|
-
const task = compact(supervisor.task, 34);
|
|
288
|
-
const line = `${pointer} ${pad(supervisor.id, 21)} ${pad(status, 12)} ${pad(workerSummary, 13)} ${pad(formatMoney(supervisor.totals.usd), 12)} ${pad(`${formatTokens(supervisor.totals.tokensInput)}/${formatTokens(supervisor.totals.tokensOutput)}`, 16)} ${pad(formatDuration(supervisor.totals.latencyMs), 12)} ${task}`;
|
|
289
|
-
push(rowFocus ? paint(line, rowFocus, color) : line, {
|
|
290
|
-
kind: "supervisor",
|
|
291
|
-
id: supervisor.id
|
|
292
|
-
});
|
|
293
|
-
}
|
|
294
|
-
if (!selectedSupervisor) return {
|
|
295
|
-
frame: `${lines.join("\n")}\n`,
|
|
296
|
-
targets
|
|
297
|
-
};
|
|
298
|
-
push();
|
|
299
|
-
push(sectionTitle(`WORKERS ${selectedSupervisor.id}`, focus === "workers", color));
|
|
300
|
-
push(` id state runtime latency cost tok in/out score detail`);
|
|
301
|
-
for (const worker of selectedSupervisor.workers.slice(0, 12)) {
|
|
302
|
-
const selected = worker.id === selectedWorker?.id;
|
|
303
|
-
const pointer = selected ? paint(">", "cyan", color) : " ";
|
|
304
|
-
const rowFocus = selected && focus === "workers" ? "inverse" : void 0;
|
|
305
|
-
const score = worker.verdict ?? (worker.status === "done" ? "done" : worker.status);
|
|
306
|
-
const detail = worker.reason ?? worker.outRef ?? worker.parent ?? "";
|
|
307
|
-
const spend = totalSpend(worker);
|
|
308
|
-
const line = `${pointer} ${pad(worker.label, 12)} ${pad(statusText(worker.status, color), 12)} ${pad(worker.runtime ?? "-", 10)} ${pad(formatDuration(worker.latencyMs), 12)} ${pad(formatMoney(spend.usd), 11)} ${pad(`${formatTokens(spend.tokensInput)}/${formatTokens(spend.tokensOutput)}`, 16)} ${pad(compact(score, 9), 10)} ${compact(detail, 30)}`;
|
|
309
|
-
push(rowFocus ? paint(line, rowFocus, color) : line, {
|
|
310
|
-
kind: "worker",
|
|
311
|
-
id: worker.id,
|
|
312
|
-
supervisorId: selectedSupervisor.id
|
|
313
|
-
});
|
|
314
|
-
}
|
|
315
|
-
if (selectedSupervisor.workers.length === 0) push(" no workers spawned yet");
|
|
316
|
-
push();
|
|
317
|
-
push(sectionTitle("OBSERVABILITY", false, color));
|
|
318
|
-
for (const line of renderStatusBars(selectedSupervisor, width, color)) push(line);
|
|
319
|
-
push();
|
|
320
|
-
for (const line of renderCostBars(selectedSupervisor, width, color)) push(line);
|
|
321
|
-
push();
|
|
322
|
-
for (const line of renderTokenBars(selectedSupervisor, width, color)) push(line);
|
|
323
|
-
push();
|
|
324
|
-
for (const line of renderLatencyHistogram(selectedSupervisor, width, color)) push(line);
|
|
325
|
-
push();
|
|
326
|
-
if (mode === "log") {
|
|
327
|
-
push(sectionTitle("JOURNAL TAIL", false, color));
|
|
328
|
-
for (const line of renderJournalTail(selectedSupervisor, width, color)) push(line);
|
|
329
|
-
} else {
|
|
330
|
-
push(sectionTitle(mode === "detail" ? "DETAIL" : "RUN SUMMARY", false, color));
|
|
331
|
-
for (const line of renderDetail(selectedSupervisor, selectedWorker, mode, width, color)) push(line);
|
|
332
|
-
}
|
|
333
|
-
return {
|
|
334
|
-
frame: `${lines.join("\n")}\n`,
|
|
335
|
-
targets
|
|
336
|
-
};
|
|
337
|
-
}
|
|
338
217
|
function ensureWorker(workers, id, label) {
|
|
339
218
|
const existing = workers.get(id);
|
|
340
219
|
if (existing) return existing;
|
|
@@ -476,78 +355,344 @@ function percentile(sorted, p) {
|
|
|
476
355
|
if (sorted.length === 0) return 0;
|
|
477
356
|
return sorted[Math.min(sorted.length - 1, Math.max(0, Math.ceil(sorted.length * p) - 1))] ?? 0;
|
|
478
357
|
}
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
358
|
+
/**
|
|
359
|
+
* Read one source file under a run directory. An absent file is not a defect on its own — the
|
|
360
|
+
* writer may not have reached it yet — so it reads as `undefined` with no diagnostic; only a
|
|
361
|
+
* failed read raises one. `path` stays run-relative and never carries file contents.
|
|
362
|
+
*/
|
|
363
|
+
function readSourceFile(file, source, path) {
|
|
364
|
+
if (!existsSync(file)) return {
|
|
365
|
+
value: void 0,
|
|
366
|
+
diagnostics: []
|
|
367
|
+
};
|
|
368
|
+
try {
|
|
369
|
+
return {
|
|
370
|
+
value: readFileSync(file, "utf8"),
|
|
371
|
+
diagnostics: []
|
|
372
|
+
};
|
|
373
|
+
} catch {
|
|
374
|
+
return {
|
|
375
|
+
value: void 0,
|
|
376
|
+
diagnostics: [{
|
|
377
|
+
source,
|
|
378
|
+
path,
|
|
379
|
+
reason: "unreadable"
|
|
380
|
+
}]
|
|
381
|
+
};
|
|
382
|
+
}
|
|
483
383
|
}
|
|
484
|
-
function
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
spend: totalSpend(worker)
|
|
488
|
-
})).sort((a, b) => b.spend.usd - a.spend.usd).slice(0, 6);
|
|
489
|
-
const maxUsd = Math.max(1e-6, ...workers.map((row) => row.spend.usd), supervisor.driverSpend.usd);
|
|
490
|
-
const out = [`cost total ${formatMoney(supervisor.totals.usd)} driver ${formatMoney(supervisor.driverSpend.usd)}`];
|
|
491
|
-
const barWidth = Math.max(18, Math.min(46, width - 38));
|
|
492
|
-
for (const row of workers) out.push(` ${pad(row.worker.label, 12)} ${bar(row.spend.usd, maxUsd, barWidth, "$", color, "magenta")} ${formatMoney(row.spend.usd)}`);
|
|
493
|
-
if (workers.length === 0) out.push(" no worker cost yet");
|
|
494
|
-
return out;
|
|
384
|
+
function tailLines(raw, maxLines) {
|
|
385
|
+
if (raw === void 0) return [];
|
|
386
|
+
return raw.trim().split("\n").filter(Boolean).slice(-maxLines);
|
|
495
387
|
}
|
|
496
|
-
function
|
|
497
|
-
const
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
const out = [`tokens total in ${formatTokens(supervisor.totals.tokensInput)} / out ${formatTokens(supervisor.totals.tokensOutput)} driver ${formatTokens(supervisor.driverSpend.tokensInput)}/${formatTokens(supervisor.driverSpend.tokensOutput)}`];
|
|
504
|
-
const barWidth = Math.max(18, Math.min(46, width - 44));
|
|
505
|
-
for (const row of rows) out.push(` ${pad(row.worker.label, 12)} ${stackedTokenBar(row.spend.tokensInput, row.spend.tokensOutput, maxTokens, barWidth, color)} ${formatTokens(row.spend.tokensInput)}/${formatTokens(row.spend.tokensOutput)}`);
|
|
506
|
-
if (rows.length === 0) out.push(" no worker tokens yet");
|
|
507
|
-
return out;
|
|
388
|
+
function readProgressTail(dir, maxLines) {
|
|
389
|
+
const path = "progress.ndjson";
|
|
390
|
+
const read = readSourceFile(join(dir, path), "progress", path);
|
|
391
|
+
return {
|
|
392
|
+
value: tailLines(read.value, maxLines),
|
|
393
|
+
diagnostics: read.diagnostics
|
|
394
|
+
};
|
|
508
395
|
}
|
|
509
|
-
function
|
|
510
|
-
const
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
396
|
+
function readWorkerEventTails(dir, maxLines) {
|
|
397
|
+
const out = /* @__PURE__ */ new Map();
|
|
398
|
+
if (!existsSync(dir)) return {
|
|
399
|
+
value: out,
|
|
400
|
+
diagnostics: []
|
|
401
|
+
};
|
|
402
|
+
const workersDir = basename(dir);
|
|
403
|
+
let entries;
|
|
404
|
+
try {
|
|
405
|
+
entries = readdirSync(dir);
|
|
406
|
+
} catch {
|
|
407
|
+
return {
|
|
408
|
+
value: out,
|
|
409
|
+
diagnostics: [{
|
|
410
|
+
source: "worker-tail",
|
|
411
|
+
path: workersDir,
|
|
412
|
+
reason: "unreadable"
|
|
413
|
+
}]
|
|
414
|
+
};
|
|
516
415
|
}
|
|
517
|
-
const
|
|
518
|
-
const
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
`tokens in/out ${formatTokens(spend.tokensInput)}/${formatTokens(spend.tokensOutput)} cost ${formatMoney(spend.usd)} iterations ${spend.iterations}`,
|
|
529
|
-
`parent ${worker.parent ?? "-"} runtime ${worker.runtime ?? "-"} out ${worker.outRef ?? "-"}${worker.infra ? " infra" : ""}`,
|
|
530
|
-
`events ${compact(worker.eventFile ?? "-", width - 8)}`,
|
|
531
|
-
...worker.reason ? [`reason ${compact(worker.reason, width - 7)}`] : [],
|
|
532
|
-
...renderWorkerTail(worker, width, color)
|
|
533
|
-
];
|
|
416
|
+
const diagnostics = [];
|
|
417
|
+
for (const entry of entries) {
|
|
418
|
+
if (!entry.endsWith(".ndjson") || entry.endsWith(".inbox.ndjson")) continue;
|
|
419
|
+
const file = join(dir, entry);
|
|
420
|
+
const read = readSourceFile(file, "worker-tail", join(workersDir, entry));
|
|
421
|
+
diagnostics.push(...read.diagnostics);
|
|
422
|
+
const lines = tailLines(read.value, maxLines);
|
|
423
|
+
out.set(entry.slice(0, -7), {
|
|
424
|
+
file,
|
|
425
|
+
lines
|
|
426
|
+
});
|
|
534
427
|
}
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
supervisor.maxLifetimeSeconds !== void 0 ? `ttl ${supervisor.maxLifetimeSeconds}s` : void 0
|
|
540
|
-
].filter((value) => value !== void 0);
|
|
541
|
-
return [
|
|
542
|
-
`task ${compact(supervisor.task, width - 7)}`,
|
|
543
|
-
`workspace ${compact(supervisor.workspaceDir, width - 12)}`,
|
|
544
|
-
`verify ${compact(supervisor.verifyCmd ?? "-", width - 9)}`,
|
|
545
|
-
`models worker ${supervisor.workerModel ?? "-"} / driver ${supervisor.driverModel ?? "-"} caps ${caps.join(" | ") || "-"}`,
|
|
546
|
-
`progress ${compact(supervisor.progress ?? supervisor.verdict ?? supervisor.status, width - 10)}`
|
|
547
|
-
];
|
|
428
|
+
return {
|
|
429
|
+
value: out,
|
|
430
|
+
diagnostics
|
|
431
|
+
};
|
|
548
432
|
}
|
|
549
|
-
function
|
|
550
|
-
|
|
433
|
+
function readSupervisorState(dir) {
|
|
434
|
+
const path = "state.json";
|
|
435
|
+
const read = readSourceFile(join(dir, path), "supervisor-state", path);
|
|
436
|
+
if (read.diagnostics.length > 0) return {
|
|
437
|
+
value: void 0,
|
|
438
|
+
diagnostics: read.diagnostics
|
|
439
|
+
};
|
|
440
|
+
if (read.value === void 0) return {
|
|
441
|
+
value: void 0,
|
|
442
|
+
diagnostics: [{
|
|
443
|
+
source: "supervisor-state",
|
|
444
|
+
path,
|
|
445
|
+
reason: "missing"
|
|
446
|
+
}]
|
|
447
|
+
};
|
|
448
|
+
let parsed;
|
|
449
|
+
try {
|
|
450
|
+
parsed = JSON.parse(read.value);
|
|
451
|
+
} catch {
|
|
452
|
+
return {
|
|
453
|
+
value: void 0,
|
|
454
|
+
diagnostics: [{
|
|
455
|
+
source: "supervisor-state",
|
|
456
|
+
path,
|
|
457
|
+
reason: "partial-json"
|
|
458
|
+
}]
|
|
459
|
+
};
|
|
460
|
+
}
|
|
461
|
+
if (!isSupervisorState(parsed)) return {
|
|
462
|
+
value: void 0,
|
|
463
|
+
diagnostics: [{
|
|
464
|
+
source: "supervisor-state",
|
|
465
|
+
path,
|
|
466
|
+
reason: "invalid-state"
|
|
467
|
+
}]
|
|
468
|
+
};
|
|
469
|
+
return {
|
|
470
|
+
value: parsed,
|
|
471
|
+
diagnostics: []
|
|
472
|
+
};
|
|
473
|
+
}
|
|
474
|
+
function isSupervisorState(value) {
|
|
475
|
+
return value !== void 0 && typeof value.id === "string" && typeof value.status === "string" && typeof value.task === "string" && typeof value.workspaceDir === "string" && typeof value.budget === "number";
|
|
476
|
+
}
|
|
477
|
+
function isSpawnEvent(value) {
|
|
478
|
+
return value.kind === "spawned" && typeof value.id === "string" || value.kind === "settled" && typeof value.id === "string" || value.kind === "cancelled" && typeof value.id === "string" || value.kind === "metered" && typeof value.id === "string" || value.kind === "progress" && typeof value.id === "string";
|
|
479
|
+
}
|
|
480
|
+
function isRecord(value) {
|
|
481
|
+
return typeof value === "object" && value !== null;
|
|
482
|
+
}
|
|
483
|
+
function isDirectory(path) {
|
|
484
|
+
try {
|
|
485
|
+
return statSync(path).isDirectory();
|
|
486
|
+
} catch {
|
|
487
|
+
return false;
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
function numberValue(value) {
|
|
491
|
+
return typeof value === "number" && Number.isFinite(value) ? value : 0;
|
|
492
|
+
}
|
|
493
|
+
function parseTime(value) {
|
|
494
|
+
if (!value) return void 0;
|
|
495
|
+
const parsed = Date.parse(value);
|
|
496
|
+
return Number.isFinite(parsed) ? parsed : void 0;
|
|
497
|
+
}
|
|
498
|
+
function sum(values) {
|
|
499
|
+
return values.reduce((total, value) => total + value, 0);
|
|
500
|
+
}
|
|
501
|
+
//#endregion
|
|
502
|
+
//#region src/tui/top-frame.ts
|
|
503
|
+
const ansiPattern = /\x1b\[[0-9;?]*[ -/]*[@-~]/g;
|
|
504
|
+
const colors = {
|
|
505
|
+
reset: "\x1B[0m",
|
|
506
|
+
dim: "\x1B[2m",
|
|
507
|
+
bold: "\x1B[1m",
|
|
508
|
+
red: "\x1B[31m",
|
|
509
|
+
green: "\x1B[32m",
|
|
510
|
+
yellow: "\x1B[33m",
|
|
511
|
+
blue: "\x1B[34m",
|
|
512
|
+
magenta: "\x1B[35m",
|
|
513
|
+
cyan: "\x1B[36m",
|
|
514
|
+
white: "\x1B[37m",
|
|
515
|
+
inverse: "\x1B[7m"
|
|
516
|
+
};
|
|
517
|
+
/** Render one snapshot to an ANSI frame. Use this when nothing needs to be clickable. */
|
|
518
|
+
function renderTopFrame(snapshot, options = {}) {
|
|
519
|
+
return renderTopFrameWithLayout(snapshot, options).frame;
|
|
520
|
+
}
|
|
521
|
+
/**
|
|
522
|
+
* Render one snapshot, returning the frame together with the row→entity map a mouse click resolves
|
|
523
|
+
* against. The layout is the only thing that knows which row is which run or worker, so emitting it
|
|
524
|
+
* alongside the text is what keeps click handling out of the renderer.
|
|
525
|
+
*/
|
|
526
|
+
function renderTopFrameWithLayout(snapshot, options = {}) {
|
|
527
|
+
const width = Math.max(88, options.width ?? 128);
|
|
528
|
+
const height = Math.max(24, options.height ?? 42);
|
|
529
|
+
const color = options.color ?? false;
|
|
530
|
+
const focus = options.focus ?? "supervisors";
|
|
531
|
+
const mode = options.mode ?? "overview";
|
|
532
|
+
const selectedSupervisor = selectSupervisor(snapshot, options.selectedSupervisorId);
|
|
533
|
+
const selectedWorker = selectedSupervisor ? selectWorker(selectedSupervisor, options.selectedWorkerId) : void 0;
|
|
534
|
+
const targets = [];
|
|
535
|
+
const lines = [];
|
|
536
|
+
const push = (line = "", target) => {
|
|
537
|
+
if (lines.length >= height) return;
|
|
538
|
+
if (target) targets.push({
|
|
539
|
+
row: lines.length,
|
|
540
|
+
...target
|
|
541
|
+
});
|
|
542
|
+
lines.push(fitAnsi(line, width));
|
|
543
|
+
};
|
|
544
|
+
const aggregate = aggregateSnapshot(snapshot);
|
|
545
|
+
push(`${paint("SUPERVISOR TOP", "bold", color)} ${paint(snapshot.root, "dim", color)} runs ${aggregate.supervisors} workers ${aggregate.workers} live ${aggregate.running} done ${aggregate.done} down ${aggregate.down} ${formatMoney(aggregate.usd)} tok ${formatTokens(aggregate.tokensInput)}/${formatTokens(aggregate.tokensOutput)}`);
|
|
546
|
+
push(`${paint("keys", "dim", color)} up/down select left/right run tab focus enter detail o overview l log s steer c cancel mouse q quit`);
|
|
547
|
+
if (options.notice) push(paint(options.notice, "yellow", color));
|
|
548
|
+
if (snapshot.completeness === "partial") push(paint(`partial snapshot: ${snapshot.diagnostics.length} source${snapshot.diagnostics.length === 1 ? "" : "s"} skipped or truncated; runs loaded ${snapshot.loaded}/${snapshot.discovered}`, "yellow", color));
|
|
549
|
+
if (options.steerInput?.active) push(renderSteerInput(options.steerInput, width, color));
|
|
550
|
+
push(rule(width, color));
|
|
551
|
+
if (snapshot.supervisors.length === 0) {
|
|
552
|
+
if (snapshot.discovered > 0) {
|
|
553
|
+
push(paint("NO READABLE SUPERVISORS", "yellow", color));
|
|
554
|
+
push(`${snapshot.discovered} run director${snapshot.discovered === 1 ? "y" : "ies"} found under ${supervisorRunsRoot(snapshot.root)}, none readable`);
|
|
555
|
+
} else {
|
|
556
|
+
push(paint("NO SUPERVISORS", "yellow", color));
|
|
557
|
+
push(`No run state under ${supervisorRunsRoot(snapshot.root)}`);
|
|
558
|
+
}
|
|
559
|
+
push(`A run appears here once its driver writes state.json; re-point with: agent-runtime-top <root>`);
|
|
560
|
+
return {
|
|
561
|
+
frame: `${lines.join("\n")}\n`,
|
|
562
|
+
targets
|
|
563
|
+
};
|
|
564
|
+
}
|
|
565
|
+
push(sectionTitle("SUPERVISORS", focus === "supervisors", color));
|
|
566
|
+
push(` id status workers spend tok in/out latency task`);
|
|
567
|
+
for (const supervisor of snapshot.supervisors.slice(0, 9)) {
|
|
568
|
+
const selected = supervisor.id === selectedSupervisor?.id;
|
|
569
|
+
const status = statusText(supervisor.status, color);
|
|
570
|
+
const pointer = selected ? paint(">", "cyan", color) : " ";
|
|
571
|
+
const rowFocus = selected && focus === "supervisors" ? "inverse" : void 0;
|
|
572
|
+
const workerSummary = `${supervisor.totals.running}/${supervisor.totals.workers} live`;
|
|
573
|
+
const task = compact(supervisor.task, 34);
|
|
574
|
+
const line = `${pointer} ${pad(supervisor.id, 21)} ${pad(status, 12)} ${pad(workerSummary, 13)} ${pad(formatMoney(supervisor.totals.usd), 12)} ${pad(`${formatTokens(supervisor.totals.tokensInput)}/${formatTokens(supervisor.totals.tokensOutput)}`, 16)} ${pad(formatDuration(supervisor.totals.latencyMs), 12)} ${task}`;
|
|
575
|
+
push(rowFocus ? paint(line, rowFocus, color) : line, {
|
|
576
|
+
kind: "supervisor",
|
|
577
|
+
id: supervisor.id
|
|
578
|
+
});
|
|
579
|
+
}
|
|
580
|
+
if (!selectedSupervisor) return {
|
|
581
|
+
frame: `${lines.join("\n")}\n`,
|
|
582
|
+
targets
|
|
583
|
+
};
|
|
584
|
+
push();
|
|
585
|
+
push(sectionTitle(`WORKERS ${selectedSupervisor.id}`, focus === "workers", color));
|
|
586
|
+
push(` id state runtime latency cost tok in/out score detail`);
|
|
587
|
+
for (const worker of selectedSupervisor.workers.slice(0, 12)) {
|
|
588
|
+
const selected = worker.id === selectedWorker?.id;
|
|
589
|
+
const pointer = selected ? paint(">", "cyan", color) : " ";
|
|
590
|
+
const rowFocus = selected && focus === "workers" ? "inverse" : void 0;
|
|
591
|
+
const score = worker.verdict ?? (worker.status === "done" ? "done" : worker.status);
|
|
592
|
+
const detail = worker.reason ?? worker.outRef ?? worker.parent ?? "";
|
|
593
|
+
const spend = totalSpend(worker);
|
|
594
|
+
const line = `${pointer} ${pad(worker.label, 12)} ${pad(statusText(worker.status, color), 12)} ${pad(worker.runtime ?? "-", 10)} ${pad(formatDuration(worker.latencyMs), 12)} ${pad(formatMoney(spend.usd), 11)} ${pad(`${formatTokens(spend.tokensInput)}/${formatTokens(spend.tokensOutput)}`, 16)} ${pad(compact(score, 9), 10)} ${compact(detail, 30)}`;
|
|
595
|
+
push(rowFocus ? paint(line, rowFocus, color) : line, {
|
|
596
|
+
kind: "worker",
|
|
597
|
+
id: worker.id,
|
|
598
|
+
supervisorId: selectedSupervisor.id
|
|
599
|
+
});
|
|
600
|
+
}
|
|
601
|
+
if (selectedSupervisor.workers.length === 0) push(" no workers spawned yet");
|
|
602
|
+
push();
|
|
603
|
+
push(sectionTitle("OBSERVABILITY", false, color));
|
|
604
|
+
for (const line of renderStatusBars(selectedSupervisor, width, color)) push(line);
|
|
605
|
+
push();
|
|
606
|
+
for (const line of renderCostBars(selectedSupervisor, width, color)) push(line);
|
|
607
|
+
push();
|
|
608
|
+
for (const line of renderTokenBars(selectedSupervisor, width, color)) push(line);
|
|
609
|
+
push();
|
|
610
|
+
for (const line of renderLatencyHistogram(selectedSupervisor, width, color)) push(line);
|
|
611
|
+
push();
|
|
612
|
+
if (mode === "log") {
|
|
613
|
+
push(sectionTitle("JOURNAL TAIL", false, color));
|
|
614
|
+
for (const line of renderJournalTail(selectedSupervisor, width, color)) push(line);
|
|
615
|
+
} else {
|
|
616
|
+
push(sectionTitle(mode === "detail" ? "DETAIL" : "RUN SUMMARY", false, color));
|
|
617
|
+
for (const line of renderDetail(selectedSupervisor, selectedWorker, mode, width, color)) push(line);
|
|
618
|
+
}
|
|
619
|
+
return {
|
|
620
|
+
frame: `${lines.join("\n")}\n`,
|
|
621
|
+
targets
|
|
622
|
+
};
|
|
623
|
+
}
|
|
624
|
+
function renderStatusBars(supervisor, width, color) {
|
|
625
|
+
const total = Math.max(1, supervisor.totals.workers);
|
|
626
|
+
const barWidth = Math.max(12, Math.min(28, width - 58));
|
|
627
|
+
return [`status ${pad("running", 9)} ${bar(supervisor.totals.running, total, barWidth, "#", color, "yellow")} ${supervisor.totals.running} ${pad("done", 5)} ${bar(supervisor.totals.done, total, barWidth, "#", color, "green")} ${supervisor.totals.done} ${pad("down", 5)} ${bar(supervisor.totals.down, total, barWidth, "#", color, "red")} ${supervisor.totals.down}`];
|
|
628
|
+
}
|
|
629
|
+
function renderCostBars(supervisor, width, color) {
|
|
630
|
+
const workers = supervisor.workers.map((worker) => ({
|
|
631
|
+
worker,
|
|
632
|
+
spend: totalSpend(worker)
|
|
633
|
+
})).sort((a, b) => b.spend.usd - a.spend.usd).slice(0, 6);
|
|
634
|
+
const maxUsd = Math.max(1e-6, ...workers.map((row) => row.spend.usd), supervisor.driverSpend.usd);
|
|
635
|
+
const out = [`cost total ${formatMoney(supervisor.totals.usd)} driver ${formatMoney(supervisor.driverSpend.usd)}`];
|
|
636
|
+
const barWidth = Math.max(18, Math.min(46, width - 38));
|
|
637
|
+
for (const row of workers) out.push(` ${pad(row.worker.label, 12)} ${bar(row.spend.usd, maxUsd, barWidth, "$", color, "magenta")} ${formatMoney(row.spend.usd)}`);
|
|
638
|
+
if (workers.length === 0) out.push(" no worker cost yet");
|
|
639
|
+
return out;
|
|
640
|
+
}
|
|
641
|
+
function renderTokenBars(supervisor, width, color) {
|
|
642
|
+
const rows = supervisor.workers.map((worker) => ({
|
|
643
|
+
worker,
|
|
644
|
+
spend: totalSpend(worker),
|
|
645
|
+
total: totalSpend(worker).tokensInput + totalSpend(worker).tokensOutput
|
|
646
|
+
})).sort((a, b) => b.total - a.total).slice(0, 6);
|
|
647
|
+
const maxTokens = Math.max(1, ...rows.map((row) => row.total), supervisor.driverSpend.tokensInput + supervisor.driverSpend.tokensOutput);
|
|
648
|
+
const out = [`tokens total in ${formatTokens(supervisor.totals.tokensInput)} / out ${formatTokens(supervisor.totals.tokensOutput)} driver ${formatTokens(supervisor.driverSpend.tokensInput)}/${formatTokens(supervisor.driverSpend.tokensOutput)}`];
|
|
649
|
+
const barWidth = Math.max(18, Math.min(46, width - 44));
|
|
650
|
+
for (const row of rows) out.push(` ${pad(row.worker.label, 12)} ${stackedTokenBar(row.spend.tokensInput, row.spend.tokensOutput, maxTokens, barWidth, color)} ${formatTokens(row.spend.tokensInput)}/${formatTokens(row.spend.tokensOutput)}`);
|
|
651
|
+
if (rows.length === 0) out.push(" no worker tokens yet");
|
|
652
|
+
return out;
|
|
653
|
+
}
|
|
654
|
+
function renderLatencyHistogram(supervisor, width, color) {
|
|
655
|
+
const values = supervisor.workers.map((worker) => worker.latencyMs).filter((value) => value > 0);
|
|
656
|
+
const summary = supervisor.totals.workerLatency;
|
|
657
|
+
const out = [`latency n=${summary.n} min ${formatDuration(summary.min)} p50 ${formatDuration(summary.median)} p90 ${formatDuration(summary.p90)} max ${formatDuration(summary.max)}`];
|
|
658
|
+
if (values.length === 0) {
|
|
659
|
+
out.push(" no latency samples yet");
|
|
660
|
+
return out;
|
|
661
|
+
}
|
|
662
|
+
const bins = histogram(values, 6);
|
|
663
|
+
const maxCount = Math.max(1, ...bins.map((bin) => bin.count));
|
|
664
|
+
const barWidth = Math.max(18, Math.min(48, width - 34));
|
|
665
|
+
for (const bin of bins) out.push(` ${pad(`${formatDuration(bin.min)}-${formatDuration(bin.max)}`, 15)} ${bar(bin.count, maxCount, barWidth, "#", color, "blue")} ${bin.count}`);
|
|
666
|
+
return out;
|
|
667
|
+
}
|
|
668
|
+
function renderDetail(supervisor, worker, mode, width, color) {
|
|
669
|
+
if (mode === "detail" && worker) {
|
|
670
|
+
const spend = totalSpend(worker);
|
|
671
|
+
return [
|
|
672
|
+
`worker ${paint(worker.label, "bold", color)} (${worker.id}) ${statusText(worker.status, color)} latency ${formatDuration(worker.latencyMs)}`,
|
|
673
|
+
`tokens in/out ${formatTokens(spend.tokensInput)}/${formatTokens(spend.tokensOutput)} cost ${formatMoney(spend.usd)} iterations ${spend.iterations}`,
|
|
674
|
+
`parent ${worker.parent ?? "-"} runtime ${worker.runtime ?? "-"} out ${worker.outRef ?? "-"}${worker.infra ? " infra" : ""}`,
|
|
675
|
+
`events ${compact(worker.eventFile ?? "-", width - 8)}`,
|
|
676
|
+
...worker.reason ? [`reason ${compact(worker.reason, width - 7)}`] : [],
|
|
677
|
+
...renderWorkerTail(worker, width, color)
|
|
678
|
+
];
|
|
679
|
+
}
|
|
680
|
+
const caps = [
|
|
681
|
+
supervisor.maxSandboxes !== void 0 ? `boxes ${supervisor.maxSandboxes}` : void 0,
|
|
682
|
+
supervisor.maxUsd !== void 0 ? `max ${formatMoney(supervisor.maxUsd)}` : void 0,
|
|
683
|
+
supervisor.maxDepth !== void 0 ? `depth ${supervisor.maxDepth}` : void 0,
|
|
684
|
+
supervisor.maxLifetimeSeconds !== void 0 ? `ttl ${supervisor.maxLifetimeSeconds}s` : void 0
|
|
685
|
+
].filter((value) => value !== void 0);
|
|
686
|
+
return [
|
|
687
|
+
`task ${compact(supervisor.task, width - 7)}`,
|
|
688
|
+
`workspace ${compact(supervisor.workspaceDir, width - 12)}`,
|
|
689
|
+
`verify ${compact(supervisor.verifyCmd ?? "-", width - 9)}`,
|
|
690
|
+
`models worker ${supervisor.workerModel ?? "-"} / driver ${supervisor.driverModel ?? "-"} caps ${caps.join(" | ") || "-"}`,
|
|
691
|
+
`progress ${compact(supervisor.progress ?? supervisor.verdict ?? supervisor.status, width - 10)}`
|
|
692
|
+
];
|
|
693
|
+
}
|
|
694
|
+
function renderJournalTail(supervisor, width, color) {
|
|
695
|
+
if (supervisor.journalTail.length === 0) return [" no journal events yet"];
|
|
551
696
|
return supervisor.journalTail.map((event) => {
|
|
552
697
|
const at = event.at ? formatClock(event.at) : "--:--:--";
|
|
553
698
|
if (event.kind === "spawned") return ` ${at} ${paint("spawn", "yellow", color)} ${pad(event.label ?? event.id, 16)} ${event.id}`;
|
|
@@ -676,149 +821,6 @@ function selectWorker(supervisor, requested) {
|
|
|
676
821
|
}
|
|
677
822
|
return supervisor.workers[0];
|
|
678
823
|
}
|
|
679
|
-
/**
|
|
680
|
-
* Read one source file under a run directory. An absent file is not a defect on its own — the
|
|
681
|
-
* writer may not have reached it yet — so it reads as `undefined` with no diagnostic; only a
|
|
682
|
-
* failed read raises one. `path` stays run-relative and never carries file contents.
|
|
683
|
-
*/
|
|
684
|
-
function readSourceFile(file, source, path) {
|
|
685
|
-
if (!existsSync(file)) return {
|
|
686
|
-
value: void 0,
|
|
687
|
-
diagnostics: []
|
|
688
|
-
};
|
|
689
|
-
try {
|
|
690
|
-
return {
|
|
691
|
-
value: readFileSync(file, "utf8"),
|
|
692
|
-
diagnostics: []
|
|
693
|
-
};
|
|
694
|
-
} catch {
|
|
695
|
-
return {
|
|
696
|
-
value: void 0,
|
|
697
|
-
diagnostics: [{
|
|
698
|
-
source,
|
|
699
|
-
path,
|
|
700
|
-
reason: "unreadable"
|
|
701
|
-
}]
|
|
702
|
-
};
|
|
703
|
-
}
|
|
704
|
-
}
|
|
705
|
-
function tailLines(raw, maxLines) {
|
|
706
|
-
if (raw === void 0) return [];
|
|
707
|
-
return raw.trim().split("\n").filter(Boolean).slice(-maxLines);
|
|
708
|
-
}
|
|
709
|
-
function readProgressTail(dir, maxLines) {
|
|
710
|
-
const path = "progress.ndjson";
|
|
711
|
-
const read = readSourceFile(join(dir, path), "progress", path);
|
|
712
|
-
return {
|
|
713
|
-
value: tailLines(read.value, maxLines),
|
|
714
|
-
diagnostics: read.diagnostics
|
|
715
|
-
};
|
|
716
|
-
}
|
|
717
|
-
function readWorkerEventTails(dir, maxLines) {
|
|
718
|
-
const out = /* @__PURE__ */ new Map();
|
|
719
|
-
if (!existsSync(dir)) return {
|
|
720
|
-
value: out,
|
|
721
|
-
diagnostics: []
|
|
722
|
-
};
|
|
723
|
-
const workersDir = basename(dir);
|
|
724
|
-
let entries;
|
|
725
|
-
try {
|
|
726
|
-
entries = readdirSync(dir);
|
|
727
|
-
} catch {
|
|
728
|
-
return {
|
|
729
|
-
value: out,
|
|
730
|
-
diagnostics: [{
|
|
731
|
-
source: "worker-tail",
|
|
732
|
-
path: workersDir,
|
|
733
|
-
reason: "unreadable"
|
|
734
|
-
}]
|
|
735
|
-
};
|
|
736
|
-
}
|
|
737
|
-
const diagnostics = [];
|
|
738
|
-
for (const entry of entries) {
|
|
739
|
-
if (!entry.endsWith(".ndjson") || entry.endsWith(".inbox.ndjson")) continue;
|
|
740
|
-
const file = join(dir, entry);
|
|
741
|
-
const read = readSourceFile(file, "worker-tail", join(workersDir, entry));
|
|
742
|
-
diagnostics.push(...read.diagnostics);
|
|
743
|
-
const lines = tailLines(read.value, maxLines);
|
|
744
|
-
out.set(entry.slice(0, -7), {
|
|
745
|
-
file,
|
|
746
|
-
lines
|
|
747
|
-
});
|
|
748
|
-
}
|
|
749
|
-
return {
|
|
750
|
-
value: out,
|
|
751
|
-
diagnostics
|
|
752
|
-
};
|
|
753
|
-
}
|
|
754
|
-
function readSupervisorState(dir) {
|
|
755
|
-
const path = "state.json";
|
|
756
|
-
const read = readSourceFile(join(dir, path), "supervisor-state", path);
|
|
757
|
-
if (read.diagnostics.length > 0) return {
|
|
758
|
-
value: void 0,
|
|
759
|
-
diagnostics: read.diagnostics
|
|
760
|
-
};
|
|
761
|
-
if (read.value === void 0) return {
|
|
762
|
-
value: void 0,
|
|
763
|
-
diagnostics: [{
|
|
764
|
-
source: "supervisor-state",
|
|
765
|
-
path,
|
|
766
|
-
reason: "missing"
|
|
767
|
-
}]
|
|
768
|
-
};
|
|
769
|
-
let parsed;
|
|
770
|
-
try {
|
|
771
|
-
parsed = JSON.parse(read.value);
|
|
772
|
-
} catch {
|
|
773
|
-
return {
|
|
774
|
-
value: void 0,
|
|
775
|
-
diagnostics: [{
|
|
776
|
-
source: "supervisor-state",
|
|
777
|
-
path,
|
|
778
|
-
reason: "partial-json"
|
|
779
|
-
}]
|
|
780
|
-
};
|
|
781
|
-
}
|
|
782
|
-
if (!isSupervisorState(parsed)) return {
|
|
783
|
-
value: void 0,
|
|
784
|
-
diagnostics: [{
|
|
785
|
-
source: "supervisor-state",
|
|
786
|
-
path,
|
|
787
|
-
reason: "invalid-state"
|
|
788
|
-
}]
|
|
789
|
-
};
|
|
790
|
-
return {
|
|
791
|
-
value: parsed,
|
|
792
|
-
diagnostics: []
|
|
793
|
-
};
|
|
794
|
-
}
|
|
795
|
-
function isSupervisorState(value) {
|
|
796
|
-
return value !== void 0 && typeof value.id === "string" && typeof value.status === "string" && typeof value.task === "string" && typeof value.workspaceDir === "string" && typeof value.budget === "number";
|
|
797
|
-
}
|
|
798
|
-
function isSpawnEvent(value) {
|
|
799
|
-
return value.kind === "spawned" && typeof value.id === "string" || value.kind === "settled" && typeof value.id === "string" || value.kind === "cancelled" && typeof value.id === "string" || value.kind === "metered" && typeof value.id === "string" || value.kind === "progress" && typeof value.id === "string";
|
|
800
|
-
}
|
|
801
|
-
function isRecord(value) {
|
|
802
|
-
return typeof value === "object" && value !== null;
|
|
803
|
-
}
|
|
804
|
-
function isDirectory(path) {
|
|
805
|
-
try {
|
|
806
|
-
return statSync(path).isDirectory();
|
|
807
|
-
} catch {
|
|
808
|
-
return false;
|
|
809
|
-
}
|
|
810
|
-
}
|
|
811
|
-
function numberValue(value) {
|
|
812
|
-
return typeof value === "number" && Number.isFinite(value) ? value : 0;
|
|
813
|
-
}
|
|
814
|
-
function parseTime(value) {
|
|
815
|
-
if (!value) return void 0;
|
|
816
|
-
const parsed = Date.parse(value);
|
|
817
|
-
return Number.isFinite(parsed) ? parsed : void 0;
|
|
818
|
-
}
|
|
819
|
-
function sum(values) {
|
|
820
|
-
return values.reduce((total, value) => total + value, 0);
|
|
821
|
-
}
|
|
822
824
|
function formatClock(value) {
|
|
823
825
|
const parsed = parseTime(value);
|
|
824
826
|
if (parsed === void 0) return "--:--:--";
|
|
@@ -895,7 +897,7 @@ function fitAnsi(value, width) {
|
|
|
895
897
|
//#region src/tui/top-app.ts
|
|
896
898
|
/**
|
|
897
899
|
* The interactive side of the supervisor-run TUI: a keypress/mouse loop over the frames
|
|
898
|
-
* `./top-
|
|
900
|
+
* `./top-frame` renders, plus the operator controls that write back to the run — steer a live
|
|
899
901
|
* worker, cancel one worker, and request cancellation of a whole run.
|
|
900
902
|
*
|
|
901
903
|
* The controls go through the owned run layout: steers via `writeWorkerSteer` (the durable inbox
|
|
@@ -1207,6 +1209,6 @@ function wrap(value, size) {
|
|
|
1207
1209
|
return (value % size + size) % size;
|
|
1208
1210
|
}
|
|
1209
1211
|
//#endregion
|
|
1210
|
-
export {
|
|
1212
|
+
export { loadTopSnapshot as a, renderTopFrameWithLayout as i, runTopApp as n, renderTopFrame as r, renderTopOnce as t };
|
|
1211
1213
|
|
|
1212
|
-
//# sourceMappingURL=top-app-
|
|
1214
|
+
//# sourceMappingURL=top-app-C55Wa7uy.js.map
|