@tangle-network/agent-runtime 0.337.2 → 0.339.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.
Files changed (155) hide show
  1. package/dist/{activation-DRHM55Oo.d.ts → activation-CJIIl_Ey.d.ts} +90 -22
  2. package/dist/activation-CVSFxis9.js +876 -0
  3. package/dist/activation-CVSFxis9.js.map +1 -0
  4. package/dist/agent.d.ts +4 -4
  5. package/dist/agent.js +4 -7
  6. package/dist/agent.js.map +1 -1
  7. package/dist/{bundle-BPGjESee.js → bundle-DVOhBTic.js} +2 -2
  8. package/dist/{bundle-BPGjESee.js.map → bundle-DVOhBTic.js.map} +1 -1
  9. package/dist/candidate-execution/index.d.ts +4 -63
  10. package/dist/candidate-execution/index.js +6 -146
  11. package/dist/candidate-execution/index.js.map +1 -1
  12. package/dist/content-digest-sBTLujlK.js +9 -0
  13. package/dist/content-digest-sBTLujlK.js.map +1 -0
  14. package/dist/{delegate-B-T_ZB50.js → delegate-CCWE_AkU.js} +2 -2
  15. package/dist/{delegate-B-T_ZB50.js.map → delegate-CCWE_AkU.js.map} +1 -1
  16. package/dist/{coordination-driver-BHkFVqIq.js → driver-control-acknowledgers-C53ggdJ6.js} +4520 -4442
  17. package/dist/driver-control-acknowledgers-C53ggdJ6.js.map +1 -0
  18. package/dist/{durable-file-B_eJr6Mb.js → durable-file-gNI9ILAn.js} +1 -3
  19. package/dist/durable-file-gNI9ILAn.js.map +1 -0
  20. package/dist/durable.d.ts +3 -35
  21. package/dist/durable.js +225 -229
  22. package/dist/durable.js.map +1 -1
  23. package/dist/{graph-C4RFlWAJ.js → graph-CI9JXKqO.js} +65 -18
  24. package/dist/graph-CI9JXKqO.js.map +1 -0
  25. package/dist/{graph-registry-DAI6KNXB.js → graph-registry-BnO64cn8.js} +18 -17
  26. package/dist/graph-registry-BnO64cn8.js.map +1 -0
  27. package/dist/{improvement-cycle-D1I4hQvf.js → improve-BOXTLWJ3.js} +76 -1248
  28. package/dist/improve-BOXTLWJ3.js.map +1 -0
  29. package/dist/{improvement-adapter-D5gwwoXQ.d.ts → improvement-adapter-HUt_-cWA.d.ts} +2 -20
  30. package/dist/index.d.ts +10 -207
  31. package/dist/index.js +91 -305
  32. package/dist/index.js.map +1 -1
  33. package/dist/intelligence.d.ts +10 -229
  34. package/dist/intelligence.js +506 -88
  35. package/dist/intelligence.js.map +1 -1
  36. package/dist/{jsonl-file-DBv31uPi.js → jsonl-file-C9Kde_0Z.js} +2 -2
  37. package/dist/{jsonl-file-DBv31uPi.js.map → jsonl-file-C9Kde_0Z.js.map} +1 -1
  38. package/dist/kernel.d.ts +4 -3
  39. package/dist/kernel.js +12 -13
  40. package/dist/loop-runner-CWzG1bH4.d.ts +32 -0
  41. package/dist/loop-runner-bin.d.ts +32 -2
  42. package/dist/loop-runner-bin.js +93 -1
  43. package/dist/loop-runner-bin.js.map +1 -0
  44. package/dist/loop-runner-pmPIHFVY.js +59 -0
  45. package/dist/loop-runner-pmPIHFVY.js.map +1 -0
  46. package/dist/mcp/bin.js +3 -3
  47. package/dist/mcp/bin.js.map +1 -1
  48. package/dist/mcp/index.d.ts +101 -416
  49. package/dist/mcp/index.js +122 -206
  50. package/dist/mcp/index.js.map +1 -1
  51. package/dist/mcp/memory-bin.js +231 -1
  52. package/dist/mcp/memory-bin.js.map +1 -1
  53. package/dist/model-policy-CyCUwHed.js.map +1 -1
  54. package/dist/placement.d.ts +2 -9
  55. package/dist/placement.js +1 -1
  56. package/dist/placement.js.map +1 -1
  57. package/dist/platform.d.ts +194 -2
  58. package/dist/platform.js +145 -9
  59. package/dist/platform.js.map +1 -1
  60. package/dist/{prepare-CER-ZkPp.js → prepare-bXkvPMJA.js} +1213 -710
  61. package/dist/prepare-bXkvPMJA.js.map +1 -0
  62. package/dist/{profile-gNUsHNIJ.js → profile-Dpei_Sq0.js} +89 -3
  63. package/dist/profile-Dpei_Sq0.js.map +1 -0
  64. package/dist/{profile-materialization-BnoEf1co.d.ts → profile-materialization-B1wm2SPT.d.ts} +3 -5
  65. package/dist/profiles.d.ts +2 -2
  66. package/dist/profiles.js +4 -43
  67. package/dist/profiles.js.map +1 -1
  68. package/dist/{protected-model-port-BhpQRTw-.d.ts → protected-model-port-BGgpy2r9.d.ts} +4 -30
  69. package/dist/protected-redaction-DFCeo4vg.js.map +1 -1
  70. package/dist/{provision-supervisor-DGegCUH4.js → provision-supervisor-CJ6Zz6dz.js} +13 -10
  71. package/dist/provision-supervisor-CJ6Zz6dz.js.map +1 -0
  72. package/dist/record-value-B6bwJxLa.js +12 -0
  73. package/dist/record-value-B6bwJxLa.js.map +1 -0
  74. package/dist/{run-layout-Cu8H7aeh.js → run-cancellation-C9oL62sW.js} +175 -100
  75. package/dist/run-cancellation-C9oL62sW.js.map +1 -0
  76. package/dist/{runtime-BOGpotnH.js → runtime-B2fjL9Oi.js} +2639 -3187
  77. package/dist/runtime-B2fjL9Oi.js.map +1 -0
  78. package/dist/{runtime-hooks-Bj6wJHlH.d.ts → runtime-hooks-nsqxSUFL.d.ts} +2 -6
  79. package/dist/{sandbox-profile-compat-QTkLe1Z6.js → sandbox-profile-compat-BzVBFqrz.js} +690 -699
  80. package/dist/sandbox-profile-compat-BzVBFqrz.js.map +1 -0
  81. package/dist/search-executor-zMumiak_.js +1916 -0
  82. package/dist/search-executor-zMumiak_.js.map +1 -0
  83. package/dist/{server-hZWM-G8w.js → server-_4voDNrl.js} +362 -67
  84. package/dist/server-_4voDNrl.js.map +1 -0
  85. package/dist/{search-executor-Cm1WZmL-.js → structural-rollout-DpLCXzaL.js} +1680 -3403
  86. package/dist/structural-rollout-DpLCXzaL.js.map +1 -0
  87. package/dist/{substrate-B0TYNrXn.js → substrate-CGDFTSRh.js} +2 -9
  88. package/dist/substrate-CGDFTSRh.js.map +1 -0
  89. package/dist/{supervise-CvAKVfue.js → supervise-qAwXmPtD.js} +7768 -5360
  90. package/dist/supervise-qAwXmPtD.js.map +1 -0
  91. package/dist/{supervisor-Rz8AGpYb.js → supervisor-DHrUJhEZ.js} +26301 -26964
  92. package/dist/supervisor-DHrUJhEZ.js.map +1 -0
  93. package/dist/testing.d.ts +227 -2
  94. package/dist/testing.js +13 -14
  95. package/dist/testing.js.map +1 -1
  96. package/dist/tool-loop.d.ts +1 -1
  97. package/dist/{tool-server-Dqer2M4_.js → tool-server-XV4aJwoQ.js} +2 -2
  98. package/dist/tool-server-XV4aJwoQ.js.map +1 -0
  99. package/dist/top-snapshot-CbZE1JsU.js +502 -0
  100. package/dist/top-snapshot-CbZE1JsU.js.map +1 -0
  101. package/dist/tui/bin.js +711 -1
  102. package/dist/tui/bin.js.map +1 -1
  103. package/dist/tui/index.d.ts +5 -71
  104. package/dist/tui/index.js +3 -3
  105. package/dist/{types-Dl5L4r8Z.d.ts → types-CaOXqRTq.d.ts} +16230 -17376
  106. package/dist/workspace-store/index.d.ts +2 -27
  107. package/dist/workspace-store/index.js +2 -2
  108. package/dist/workspace-store/index.js.map +1 -1
  109. package/dist/{workspace-streams-JLjh5DC6.d.ts → workspace-streams-BiS4ABah.d.ts} +19 -32
  110. package/package.json +2 -2
  111. package/skills/agent-graphs/SKILL.md +10 -3
  112. package/skills/agent-graphs/references/authoring.md +18 -0
  113. package/skills/profile-authoring/SKILL.md +3 -3
  114. package/skills/supervise/SKILL.md +2 -2
  115. package/dist/activation-BDP6Eivv.js +0 -234
  116. package/dist/activation-BDP6Eivv.js.map +0 -1
  117. package/dist/coder-yhVWbdWc.js +0 -40
  118. package/dist/coder-yhVWbdWc.js.map +0 -1
  119. package/dist/coordination-driver-BHkFVqIq.js.map +0 -1
  120. package/dist/delegation-status-CF4D0NZ_.js +0 -358
  121. package/dist/delegation-status-CF4D0NZ_.js.map +0 -1
  122. package/dist/durable-file-B_eJr6Mb.js.map +0 -1
  123. package/dist/graph-C4RFlWAJ.js.map +0 -1
  124. package/dist/graph-registry-DAI6KNXB.js.map +0 -1
  125. package/dist/improvement-cycle-D1I4hQvf.js.map +0 -1
  126. package/dist/integrations-DopfCC2a.d.ts +0 -190
  127. package/dist/integrations-dFi-ALk7.js +0 -132
  128. package/dist/integrations-dFi-ALk7.js.map +0 -1
  129. package/dist/kb-gate-C8z2juK8.d.ts +0 -80
  130. package/dist/kb-gate-DpaSwXVx.js +0 -80
  131. package/dist/kb-gate-DpaSwXVx.js.map +0 -1
  132. package/dist/loop-runner-bin-DOuBIJ2O.d.ts +0 -164
  133. package/dist/loop-runner-bin-Dv5QvYDH.js +0 -246
  134. package/dist/loop-runner-bin-Dv5QvYDH.js.map +0 -1
  135. package/dist/memory-server-DXQ8IxGR.js +0 -234
  136. package/dist/memory-server-DXQ8IxGR.js.map +0 -1
  137. package/dist/openai-tools-DoQ32vpe.js +0 -44
  138. package/dist/openai-tools-DoQ32vpe.js.map +0 -1
  139. package/dist/openai-tools-Oxk4KQrY.d.ts +0 -24
  140. package/dist/prepare-CER-ZkPp.js.map +0 -1
  141. package/dist/profile-gNUsHNIJ.js.map +0 -1
  142. package/dist/protected-model-port-BuRsviSL.js +0 -766
  143. package/dist/protected-model-port-BuRsviSL.js.map +0 -1
  144. package/dist/provision-supervisor-DGegCUH4.js.map +0 -1
  145. package/dist/run-layout-Cu8H7aeh.js.map +0 -1
  146. package/dist/runtime-BOGpotnH.js.map +0 -1
  147. package/dist/sandbox-profile-compat-QTkLe1Z6.js.map +0 -1
  148. package/dist/search-executor-Cm1WZmL-.js.map +0 -1
  149. package/dist/server-hZWM-G8w.js.map +0 -1
  150. package/dist/substrate-B0TYNrXn.js.map +0 -1
  151. package/dist/supervise-CvAKVfue.js.map +0 -1
  152. package/dist/supervisor-Rz8AGpYb.js.map +0 -1
  153. package/dist/tool-server-Dqer2M4_.js.map +0 -1
  154. package/dist/top-app-BaMw1W2z.js +0 -1212
  155. package/dist/top-app-BaMw1W2z.js.map +0 -1
@@ -1,1212 +0,0 @@
1
- import { _ as supervisorWorkersDir, a as legacySupervisorRunsRoot, g as supervisorRunsRoot, j as writeWorkerSteer, m as safeWorkerFile, n as cancelWorker, t as cancelRun } from "./run-layout-Cu8H7aeh.js";
2
- import { randomUUID } from "node:crypto";
3
- import { existsSync, readFileSync, readdirSync, statSync } from "node:fs";
4
- import { basename, join, resolve } from "node:path";
5
- import { emitKeypressEvents } from "node:readline";
6
- //#region src/tui/top-model.ts
7
- /**
8
- * The read side of the supervisor-run TUI: turn the on-disk run layout into one `TopSnapshot`, and
9
- * render that snapshot to an ANSI frame.
10
- *
11
- * This module never derives a run path itself. Every directory and per-worker file it reads comes
12
- * from `../runtime/supervise/run-layout` — the same module the writers use — so the reader cannot
13
- * drift from the layout the way an independently-versioned copy of the contract would.
14
- *
15
- * Extracted from the `loops` repo (`src/top-model.ts`), which held a hand-joined copy of the
16
- * layout. Rendering is deliberately unchanged: raw ANSI, zero dependencies.
17
- *
18
- * This is the OPERATOR view: what is on disk right now, for a human watching a workspace. It
19
- * carries no model-call identity, so its per-supervisor totals cannot be joined with root stream
20
- * events without double counting. A client that needs one execution's totals reads
21
- * `projectPursuit` from `../durable` instead.
22
- *
23
- * @experimental
24
- */
25
- const emptySpend = {
26
- iterations: 0,
27
- tokensInput: 0,
28
- tokensOutput: 0,
29
- usd: 0,
30
- ms: 0
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
- /**
47
- * Read every supervisor run under one workspace into a single point-in-time snapshot.
48
- *
49
- * Pure with respect to the process: it only reads, and it never throws for a writer mid-append.
50
- * An unreadable or half-written source is still skipped — an operator view must survive a live
51
- * writer — but every skip is reported as one bounded `TopSnapshotDiagnostic`, so a client can
52
- * tell a removed run from a partial read. `now` is injectable so elapsed time is deterministic
53
- * under test.
54
- */
55
- function loadTopSnapshot(rootDir, now = Date.now()) {
56
- const root = resolve(rootDir);
57
- const supervisors = [];
58
- const diagnostics = [];
59
- let discovered = 0;
60
- const report = (runId, sources) => {
61
- for (const source of sources) diagnostics.push({
62
- ...source,
63
- runId
64
- });
65
- };
66
- for (const runsRoot of [supervisorRunsRoot(root), legacySupervisorRunsRoot(root)]) {
67
- if (!existsSync(runsRoot)) continue;
68
- for (const entry of readdirSync(runsRoot)) {
69
- const dir = join(runsRoot, entry);
70
- if (!isDirectory(dir)) continue;
71
- discovered += 1;
72
- const state = readSupervisorState(dir);
73
- report(entry, state.diagnostics);
74
- if (!state.value) continue;
75
- const journal = readJournal(dir);
76
- const progress = readProgressTail(dir, 8);
77
- const workerEvents = readWorkerEventTails(supervisorWorkersDir(dir), 8);
78
- report(entry, [
79
- ...journal.diagnostics,
80
- ...progress.diagnostics,
81
- ...workerEvents.diagnostics
82
- ]);
83
- supervisors.push(buildSupervisorView(state.value, dir, journal.value, progress.value, now, workerEvents.value));
84
- }
85
- }
86
- supervisors.sort((a, b) => String(b.startedAt ?? "").localeCompare(String(a.startedAt ?? "")));
87
- return {
88
- root,
89
- generatedAt: now,
90
- supervisors,
91
- completeness: diagnostics.length === 0 ? "complete" : "partial",
92
- diagnostics,
93
- discovered,
94
- loaded: supervisors.length
95
- };
96
- }
97
- /**
98
- * The journal file a run dir actually carries. `createFileRunContext` — this repo's own durable
99
- * writer — names it `spawn-journal.jsonl`; `journal.jsonl` is the retired bare-event file older
100
- * runs left behind. First existing name wins; a reader that knew only the retired name would show
101
- * zero workers for every current run.
102
- */
103
- const journalFileNames = ["spawn-journal.jsonl", "journal.jsonl"];
104
- function readJournal(eventDir) {
105
- const path = journalFileNames.find((candidate) => existsSync(join(eventDir, candidate)));
106
- if (!path) return {
107
- value: [],
108
- diagnostics: []
109
- };
110
- const read = readSourceFile(join(eventDir, path), "journal", path);
111
- if (read.value === void 0) return {
112
- value: [],
113
- diagnostics: read.diagnostics
114
- };
115
- const out = [];
116
- let partial = false;
117
- for (const line of read.value.split("\n")) {
118
- const trimmed = line.trim();
119
- if (!trimmed) continue;
120
- try {
121
- const parsed = JSON.parse(trimmed);
122
- const event = parsed.event ?? parsed;
123
- if (isSpawnEvent(event)) out.push(event);
124
- } catch {
125
- partial = true;
126
- }
127
- }
128
- return {
129
- value: out,
130
- diagnostics: partial ? [{
131
- source: "journal",
132
- path,
133
- reason: "partial-json"
134
- }] : []
135
- };
136
- }
137
- function buildSupervisorView(state, stateDir, events, progressTail, now, workerEventTails = /* @__PURE__ */ new Map()) {
138
- const workers = /* @__PURE__ */ new Map();
139
- let driverSpend = emptySpend;
140
- const journalRootId = events.find((event) => event.kind === "spawned" && !event.parent && event.label === "root")?.id;
141
- const isRoot = (id) => id === state.id || id === journalRootId;
142
- for (const event of events) {
143
- if (event.kind === "spawned") {
144
- if (isRoot(event.id) || !event.parent && event.label === "root") continue;
145
- const worker = ensureWorker(workers, event.id, event.label ?? event.id);
146
- worker.label = event.label ?? worker.label;
147
- if (event.parent) worker.parent = event.parent;
148
- if (event.runtime) worker.runtime = event.runtime;
149
- worker.status = "running";
150
- if (event.at) worker.startedAt = event.at;
151
- const budget = parseBudget(event.budget);
152
- if (budget) worker.budget = budget;
153
- continue;
154
- }
155
- if (event.kind === "settled") {
156
- if (isRoot(event.id)) continue;
157
- const worker = ensureWorker(workers, event.id, event.id);
158
- worker.status = event.status === "down" ? "down" : "done";
159
- if (event.at) worker.endedAt = event.at;
160
- if (event.outRef) worker.outRef = event.outRef;
161
- if (event.infra !== void 0) worker.infra = event.infra;
162
- const verdict = describeVerdict(event.verdict);
163
- if (verdict) worker.verdict = verdict;
164
- worker.spend = addSpend(worker.spend, parseSpend(event.spent));
165
- worker.metered = emptySpend;
166
- continue;
167
- }
168
- if (event.kind === "cancelled") {
169
- if (isRoot(event.id)) continue;
170
- const worker = ensureWorker(workers, event.id, event.id);
171
- worker.status = "cancelled";
172
- if (event.at) worker.endedAt = event.at;
173
- if (event.reason) worker.reason = event.reason;
174
- if (event.spent !== void 0) {
175
- worker.spend = addSpend(worker.spend, parseSpend(event.spent));
176
- worker.metered = emptySpend;
177
- }
178
- continue;
179
- }
180
- if (event.kind === "metered") {
181
- const spend = parseSpend(event.spend);
182
- if (isRoot(event.id)) {
183
- if (event.accountingOnly !== true) driverSpend = addSpend(driverSpend, spend);
184
- } else {
185
- const worker = ensureWorker(workers, event.id, event.id);
186
- if (event.accountingOnly !== true) worker.metered = addSpend(worker.metered, spend);
187
- }
188
- continue;
189
- }
190
- if (event.kind === "progress") {
191
- if (isRoot(event.id)) continue;
192
- const worker = ensureWorker(workers, event.id, event.id);
193
- worker.metered = parseSpend(event.spend);
194
- }
195
- }
196
- const workerViews = [...workers.values()].map((worker) => {
197
- const tail = workerEventTails.get(worker.label) ?? workerEventTails.get(safeWorkerFile(worker.label)) ?? workerEventTails.get(safeWorkerFile(worker.id)) ?? workerEventTails.get(worker.id);
198
- worker.liveTail = tail?.lines ?? [];
199
- if (tail?.file) worker.eventFile = tail.file;
200
- return finalizeWorker(worker, now);
201
- }).sort((a, b) => compareWorker(a, b));
202
- const totals = supervisorTotals(state, workerViews, driverSpend, now);
203
- return {
204
- id: state.id,
205
- stateDir,
206
- status: state.status,
207
- task: state.task,
208
- workspaceDir: state.workspaceDir,
209
- budget: state.budget,
210
- ...state.verifyCmd ? { verifyCmd: state.verifyCmd } : {},
211
- ...state.workerModel ? { workerModel: state.workerModel } : {},
212
- ...state.driverModel ? { driverModel: state.driverModel } : {},
213
- ...state.verdict ? { verdict: state.verdict } : {},
214
- ...state.progress ? { progress: state.progress } : {},
215
- ...state.startedAt ? { startedAt: state.startedAt } : {},
216
- ...state.completedAt ? { completedAt: state.completedAt } : {},
217
- ...state.maxSandboxes !== void 0 ? { maxSandboxes: state.maxSandboxes } : {},
218
- ...state.maxLifetimeSeconds !== void 0 ? { maxLifetimeSeconds: state.maxLifetimeSeconds } : {},
219
- ...state.idleTimeoutSeconds !== void 0 ? { idleTimeoutSeconds: state.idleTimeoutSeconds } : {},
220
- ...state.maxUsd !== void 0 ? { maxUsd: state.maxUsd } : {},
221
- ...state.maxDepth !== void 0 ? { maxDepth: state.maxDepth } : {},
222
- ...state.result?.spentUsd !== void 0 ? { resultSpentUsd: state.result.spentUsd } : {},
223
- ...state.result?.spentTokens !== void 0 ? { resultSpentTokens: state.result.spentTokens } : {},
224
- workers: workerViews,
225
- progressTail: [...progressTail],
226
- journalTail: events.slice(-12),
227
- driverSpend,
228
- totals
229
- };
230
- }
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
- function ensureWorker(workers, id, label) {
339
- const existing = workers.get(id);
340
- if (existing) return existing;
341
- const created = {
342
- id,
343
- label,
344
- status: "running",
345
- spend: emptySpend,
346
- metered: emptySpend,
347
- liveTail: []
348
- };
349
- workers.set(id, created);
350
- return created;
351
- }
352
- function finalizeWorker(worker, now) {
353
- const startMs = parseTime(worker.startedAt);
354
- const endMs = parseTime(worker.endedAt) ?? (worker.status === "running" ? now : startMs);
355
- const latencyMs = startMs !== void 0 && endMs !== void 0 ? Math.max(0, endMs - startMs) : 0;
356
- return {
357
- id: worker.id,
358
- label: worker.label,
359
- ...worker.eventFile ? { eventFile: worker.eventFile } : {},
360
- ...worker.parent ? { parent: worker.parent } : {},
361
- ...worker.runtime ? { runtime: worker.runtime } : {},
362
- status: worker.status,
363
- ...worker.verdict ? { verdict: worker.verdict } : {},
364
- ...worker.infra !== void 0 ? { infra: worker.infra } : {},
365
- ...worker.startedAt ? { startedAt: worker.startedAt } : {},
366
- ...worker.endedAt ? { endedAt: worker.endedAt } : {},
367
- latencyMs,
368
- ...worker.budget ? { budget: worker.budget } : {},
369
- spend: worker.spend,
370
- metered: worker.metered,
371
- liveTail: worker.liveTail,
372
- ...worker.outRef ? { outRef: worker.outRef } : {},
373
- ...worker.reason ? { reason: worker.reason } : {}
374
- };
375
- }
376
- function supervisorTotals(state, workers, driverSpend, now) {
377
- const workerSpends = workers.map(totalSpend);
378
- const tokenInput = sum(workerSpends.map((spend) => spend.tokensInput)) + driverSpend.tokensInput;
379
- const tokenOutput = sum(workerSpends.map((spend) => spend.tokensOutput)) + driverSpend.tokensOutput;
380
- const computedUsd = sum(workerSpends.map((spend) => spend.usd)) + driverSpend.usd;
381
- const usd = computedUsd > 0 ? computedUsd : state.result?.spentUsd ?? 0;
382
- const started = parseTime(state.startedAt);
383
- const completed = parseTime(state.completedAt);
384
- const latencyMs = started !== void 0 ? Math.max(0, (completed ?? now) - started) : 0;
385
- const latencies = workers.map((worker) => worker.latencyMs).filter((ms) => ms > 0);
386
- return {
387
- workers: workers.length,
388
- running: workers.filter((worker) => worker.status === "running").length,
389
- done: workers.filter((worker) => worker.status === "done").length,
390
- down: workers.filter((worker) => worker.status === "down").length,
391
- cancelled: workers.filter((worker) => worker.status === "cancelled").length,
392
- inFlight: workers.filter((worker) => worker.status === "running").length,
393
- settled: workers.filter((worker) => worker.status !== "running").length,
394
- tokensInput: tokenInput,
395
- tokensOutput: tokenOutput,
396
- tokensTotal: tokenInput + tokenOutput,
397
- usd,
398
- latencyMs,
399
- workerLatency: distribution(latencies)
400
- };
401
- }
402
- function aggregateSnapshot(snapshot) {
403
- return {
404
- supervisors: snapshot.supervisors.length,
405
- workers: sum(snapshot.supervisors.map((supervisor) => supervisor.totals.workers)),
406
- running: sum(snapshot.supervisors.map((supervisor) => supervisor.totals.running)),
407
- done: sum(snapshot.supervisors.map((supervisor) => supervisor.totals.done)),
408
- down: sum(snapshot.supervisors.map((supervisor) => supervisor.totals.down)),
409
- usd: sum(snapshot.supervisors.map((supervisor) => supervisor.totals.usd)),
410
- tokensInput: sum(snapshot.supervisors.map((supervisor) => supervisor.totals.tokensInput)),
411
- tokensOutput: sum(snapshot.supervisors.map((supervisor) => supervisor.totals.tokensOutput))
412
- };
413
- }
414
- function compareWorker(a, b) {
415
- const aStart = parseTime(a.startedAt) ?? 0;
416
- const bStart = parseTime(b.startedAt) ?? 0;
417
- if (a.status === "running" && b.status !== "running") return -1;
418
- if (b.status === "running" && a.status !== "running") return 1;
419
- return aStart - bStart || a.label.localeCompare(b.label);
420
- }
421
- function totalSpend(worker) {
422
- return addSpend(worker.spend, worker.metered);
423
- }
424
- function addSpend(a, b) {
425
- return {
426
- iterations: a.iterations + b.iterations,
427
- tokensInput: a.tokensInput + b.tokensInput,
428
- tokensOutput: a.tokensOutput + b.tokensOutput,
429
- usd: a.usd + b.usd,
430
- ms: a.ms + b.ms
431
- };
432
- }
433
- function parseSpend(value) {
434
- if (!isRecord(value)) return emptySpend;
435
- const tokens = isRecord(value.tokens) ? value.tokens : {};
436
- return {
437
- iterations: numberValue(value.iterations),
438
- tokensInput: numberValue(tokens.input),
439
- tokensOutput: numberValue(tokens.output),
440
- usd: numberValue(value.usd),
441
- ms: numberValue(value.ms)
442
- };
443
- }
444
- function parseBudget(value) {
445
- if (!isRecord(value)) return void 0;
446
- const budget = {
447
- ...typeof value.maxIterations === "number" ? { maxIterations: value.maxIterations } : {},
448
- ...typeof value.maxTokens === "number" ? { maxTokens: value.maxTokens } : {},
449
- ...typeof value.maxUsd === "number" ? { maxUsd: value.maxUsd } : {}
450
- };
451
- return Object.keys(budget).length ? budget : void 0;
452
- }
453
- function describeVerdict(value) {
454
- if (!isRecord(value)) return void 0;
455
- if (typeof value.valid === "boolean") return value.valid ? "valid" : "invalid";
456
- if (typeof value.score === "number") return `score ${value.score.toFixed(2)}`;
457
- }
458
- function distribution(values) {
459
- if (values.length === 0) return {
460
- n: 0,
461
- min: 0,
462
- median: 0,
463
- p90: 0,
464
- max: 0
465
- };
466
- const sorted = [...values].sort((a, b) => a - b);
467
- return {
468
- n: sorted.length,
469
- min: sorted[0] ?? 0,
470
- median: percentile(sorted, .5),
471
- p90: percentile(sorted, .9),
472
- max: sorted[sorted.length - 1] ?? 0
473
- };
474
- }
475
- function percentile(sorted, p) {
476
- if (sorted.length === 0) return 0;
477
- return sorted[Math.min(sorted.length - 1, Math.max(0, Math.ceil(sorted.length * p) - 1))] ?? 0;
478
- }
479
- function renderStatusBars(supervisor, width, color) {
480
- const total = Math.max(1, supervisor.totals.workers);
481
- const barWidth = Math.max(12, Math.min(28, width - 58));
482
- 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}`];
483
- }
484
- function renderCostBars(supervisor, width, color) {
485
- const workers = supervisor.workers.map((worker) => ({
486
- worker,
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;
495
- }
496
- function renderTokenBars(supervisor, width, color) {
497
- const rows = supervisor.workers.map((worker) => ({
498
- worker,
499
- spend: totalSpend(worker),
500
- total: totalSpend(worker).tokensInput + totalSpend(worker).tokensOutput
501
- })).sort((a, b) => b.total - a.total).slice(0, 6);
502
- const maxTokens = Math.max(1, ...rows.map((row) => row.total), supervisor.driverSpend.tokensInput + supervisor.driverSpend.tokensOutput);
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;
508
- }
509
- function renderLatencyHistogram(supervisor, width, color) {
510
- const values = supervisor.workers.map((worker) => worker.latencyMs).filter((value) => value > 0);
511
- const summary = supervisor.totals.workerLatency;
512
- const out = [`latency n=${summary.n} min ${formatDuration(summary.min)} p50 ${formatDuration(summary.median)} p90 ${formatDuration(summary.p90)} max ${formatDuration(summary.max)}`];
513
- if (values.length === 0) {
514
- out.push(" no latency samples yet");
515
- return out;
516
- }
517
- const bins = histogram(values, 6);
518
- const maxCount = Math.max(1, ...bins.map((bin) => bin.count));
519
- const barWidth = Math.max(18, Math.min(48, width - 34));
520
- for (const bin of bins) out.push(` ${pad(`${formatDuration(bin.min)}-${formatDuration(bin.max)}`, 15)} ${bar(bin.count, maxCount, barWidth, "#", color, "blue")} ${bin.count}`);
521
- return out;
522
- }
523
- function renderDetail(supervisor, worker, mode, width, color) {
524
- if (mode === "detail" && worker) {
525
- const spend = totalSpend(worker);
526
- return [
527
- `worker ${paint(worker.label, "bold", color)} (${worker.id}) ${statusText(worker.status, color)} latency ${formatDuration(worker.latencyMs)}`,
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
- ];
534
- }
535
- const caps = [
536
- supervisor.maxSandboxes !== void 0 ? `boxes ${supervisor.maxSandboxes}` : void 0,
537
- supervisor.maxUsd !== void 0 ? `max ${formatMoney(supervisor.maxUsd)}` : void 0,
538
- supervisor.maxDepth !== void 0 ? `depth ${supervisor.maxDepth}` : void 0,
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
- ];
548
- }
549
- function renderJournalTail(supervisor, width, color) {
550
- if (supervisor.journalTail.length === 0) return [" no journal events yet"];
551
- return supervisor.journalTail.map((event) => {
552
- const at = event.at ? formatClock(event.at) : "--:--:--";
553
- if (event.kind === "spawned") return ` ${at} ${paint("spawn", "yellow", color)} ${pad(event.label ?? event.id, 16)} ${event.id}`;
554
- if (event.kind === "settled") {
555
- const spend = parseSpend(event.spent);
556
- return ` ${at} ${statusText(event.status ?? "settled", color)} ${pad(event.id, 18)} ${formatMoney(spend.usd)} ${formatTokens(spend.tokensInput)}/${formatTokens(spend.tokensOutput)}`;
557
- }
558
- if (event.kind === "metered") {
559
- const spend = parseSpend(event.spend);
560
- return ` ${at} ${paint("meter", "cyan", color)} ${pad(event.id, 18)} ${formatMoney(spend.usd)} ${formatTokens(spend.tokensInput)}/${formatTokens(spend.tokensOutput)}`;
561
- }
562
- if (event.kind === "progress") {
563
- const spend = parseSpend(event.spend);
564
- return ` ${at} ${paint("live", "blue", color)} ${pad(event.id, 18)} ${formatMoney(spend.usd)} ${formatTokens(spend.tokensInput)}/${formatTokens(spend.tokensOutput)}`;
565
- }
566
- return ` ${at} ${paint("cancel", "red", color)} ${pad(event.id, 18)} ${compact(event.reason ?? "", width - 32)}`;
567
- });
568
- }
569
- function renderWorkerTail(worker, width, color) {
570
- if (worker.liveTail.length === 0) return ["live events: none recorded yet"];
571
- return ["live events:", ...worker.liveTail.slice(-5).map((line) => ` ${paint(compact(formatWorkerEvent(line), width - 2), "dim", color)}`)];
572
- }
573
- function renderSteerInput(input, width, color) {
574
- const target = input.workerLabel ?? "-";
575
- const value = compact(input.value, Math.max(10, width - target.length - 14));
576
- return `${paint("steer", "cyan", color)} ${target} > ${value}${paint("_", "inverse", color)}`;
577
- }
578
- function formatWorkerEvent(line) {
579
- try {
580
- const parsed = JSON.parse(line);
581
- const at = typeof parsed.at === "string" ? formatClock(parsed.at) : "--:--:--";
582
- const kind = typeof parsed.kind === "string" ? parsed.kind : "event";
583
- if (kind === "progress") return `${at} progress iter ${numberValue(parsed.iteration)} ${String(parsed.phase ?? "")}`;
584
- if (kind === "message") {
585
- const state = parsed.queued === true ? "queued" : "sent";
586
- const source = typeof parsed.source === "string" && parsed.source.length > 0 ? `${parsed.source} ` : "";
587
- const message = typeof parsed.message === "string" && parsed.message.length > 0 ? ` ${parsed.message}` : "";
588
- return `${at} ${source}steer ${state} delivered=${String(parsed.delivered)}${message}`;
589
- }
590
- if (kind === "verify") return `${at} verify passed=${String(parsed.passed)} exit=${String(parsed.exitCode ?? "-")}${evidenceSnippet(parsed.outputTail, 160)}`;
591
- if (kind === "finished") {
592
- const branch = typeof parsed.branch === "string" ? ` branch=${parsed.branch}` : "";
593
- const files = typeof parsed.filesChanged === "number" ? ` files=${parsed.filesChanged}` : "";
594
- const tests = typeof parsed.testPassed === "boolean" ? ` tests=${String(parsed.testPassed)}` : "";
595
- return `${at} finished passed=${String(parsed.passed)} patchBytes=${numberValue(parsed.patchBytes)}${files}${tests}${branch}${evidenceSnippet(parsed.evidence, 220)}`;
596
- }
597
- if (kind === "error") return `${at} error ${String(parsed.error ?? "")}`;
598
- return `${at} ${kind}`;
599
- } catch {
600
- return line;
601
- }
602
- }
603
- /** One-line the settle evidence / verify tail a worker event carries so the
604
- * failure is legible in the tail view (the full block stays in the ndjson). */
605
- function evidenceSnippet(value, max) {
606
- if (typeof value !== "string") return "";
607
- const oneLine = value.replace(/\s+/g, " ").trim();
608
- return oneLine ? ` :: ${compact(oneLine, max)}` : "";
609
- }
610
- function histogram(values, count) {
611
- const min = Math.min(...values);
612
- const max = Math.max(...values);
613
- if (min === max) return [{
614
- min,
615
- max,
616
- count: values.length
617
- }];
618
- const step = (max - min) / count;
619
- return Array.from({ length: count }, (_, index) => {
620
- const low = min + step * index;
621
- const high = index === count - 1 ? max : min + step * (index + 1);
622
- return {
623
- min: low,
624
- max: high,
625
- count: values.filter((value) => value >= low && (index === count - 1 ? value <= high : value < high)).length
626
- };
627
- }).filter((bin) => bin.count > 0);
628
- }
629
- function bar(value, max, width, ch, color, colorName) {
630
- const filled = Math.max(0, Math.min(width, Math.round(value / Math.max(max, 1e-6) * width)));
631
- return paint(ch.repeat(filled).padEnd(width, "."), colorName, color);
632
- }
633
- function stackedTokenBar(input, output, max, width, color) {
634
- const inputWidth = Math.round(input / Math.max(max, 1) * width);
635
- const outputWidth = Math.round(output / Math.max(max, 1) * width);
636
- const clippedInput = Math.min(width, inputWidth);
637
- const clippedOutput = Math.min(width - clippedInput, outputWidth);
638
- const rest = Math.max(0, width - clippedInput - clippedOutput);
639
- return `${paint("#".repeat(clippedInput), "cyan", color)}${paint("+".repeat(clippedOutput), "green", color)}${".".repeat(rest)}`;
640
- }
641
- function sectionTitle(title, focused, color) {
642
- return paint(focused ? `[${title}]` : title, focused ? "cyan" : "bold", color);
643
- }
644
- function statusText(status, color) {
645
- if ([
646
- "completed",
647
- "done",
648
- "delivered",
649
- "valid"
650
- ].includes(status)) return paint(status.toUpperCase(), "green", color);
651
- if ([
652
- "failed",
653
- "down",
654
- "cancelled",
655
- "invalid",
656
- "error"
657
- ].includes(status)) return paint(status.toUpperCase(), "red", color);
658
- if ([
659
- "running",
660
- "spawned",
661
- "settled"
662
- ].includes(status)) return paint(status.toUpperCase(), "yellow", color);
663
- return paint(status.toUpperCase(), "white", color);
664
- }
665
- function selectSupervisor(snapshot, requested) {
666
- if (requested) {
667
- const found = snapshot.supervisors.find((supervisor) => supervisor.id === requested);
668
- if (found) return found;
669
- }
670
- return snapshot.supervisors[0];
671
- }
672
- function selectWorker(supervisor, requested) {
673
- if (requested) {
674
- const found = supervisor.workers.find((worker) => worker.id === requested);
675
- if (found) return found;
676
- }
677
- return supervisor.workers[0];
678
- }
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
- function formatClock(value) {
823
- const parsed = parseTime(value);
824
- if (parsed === void 0) return "--:--:--";
825
- return new Date(parsed).toISOString().slice(11, 19);
826
- }
827
- function formatMoney(value) {
828
- if (value === 0) return "$0";
829
- if (Math.abs(value) < .01) return `$${value.toFixed(4)}`;
830
- return `$${value.toFixed(2)}`;
831
- }
832
- function formatTokens(value) {
833
- if (value >= 1e6) return `${(value / 1e6).toFixed(1)}m`;
834
- if (value >= 1e3) return `${(value / 1e3).toFixed(1)}k`;
835
- return `${Math.round(value)}`;
836
- }
837
- function formatDuration(ms) {
838
- if (!Number.isFinite(ms) || ms <= 0) return "0s";
839
- if (ms < 1e3) return `${Math.round(ms)}ms`;
840
- const seconds = ms / 1e3;
841
- if (seconds < 60) return `${seconds.toFixed(seconds < 10 ? 1 : 0)}s`;
842
- const minutes = seconds / 60;
843
- if (minutes < 60) return `${minutes.toFixed(minutes < 10 ? 1 : 0)}m`;
844
- return `${(minutes / 60).toFixed(1)}h`;
845
- }
846
- function compact(value, width) {
847
- if (width <= 1) return "";
848
- if (value.length <= width) return value;
849
- return `${value.slice(0, width - 1)}~`;
850
- }
851
- function pad(value, width) {
852
- const visible = visibleLength(value);
853
- if (visible >= width) return fitAnsi(value, width);
854
- return `${value}${" ".repeat(width - visible)}`;
855
- }
856
- function rule(width, color) {
857
- return paint("-".repeat(width), "dim", color);
858
- }
859
- function paint(value, colorName, enabled) {
860
- if (!enabled || !colorName) return value;
861
- return `${colors[colorName]}${value}${colors.reset}`;
862
- }
863
- function visibleLength(value) {
864
- return stripAnsi(value).length;
865
- }
866
- function stripAnsi(value) {
867
- return value.replace(ansiPattern, "");
868
- }
869
- function fitAnsi(value, width) {
870
- if (visibleLength(value) <= width) return value;
871
- let out = "";
872
- let visible = 0;
873
- for (let i = 0; i < value.length; i += 1) {
874
- const ch = value[i];
875
- if (ch === "\x1B") {
876
- const rest = value.slice(i);
877
- const match = /^\x1b\[[0-9;?]*[ -/]*[@-~]/.exec(rest);
878
- if (match) {
879
- out += match[0];
880
- i += match[0].length - 1;
881
- continue;
882
- }
883
- }
884
- if (visible >= width - 1) {
885
- out += "~";
886
- break;
887
- }
888
- out += ch;
889
- visible += 1;
890
- }
891
- if (out.includes("\x1B[") && !out.endsWith(colors.reset)) out += colors.reset;
892
- return out;
893
- }
894
- //#endregion
895
- //#region src/tui/top-app.ts
896
- /**
897
- * The interactive side of the supervisor-run TUI: a keypress/mouse loop over the frames
898
- * `./top-model` renders, plus the operator controls that write back to the run — steer a live
899
- * worker, cancel one worker, and request cancellation of a whole run.
900
- *
901
- * The controls go through the owned run layout: steers via `writeWorkerSteer` (the durable inbox
902
- * append) and worker cancellation via `cancelWorker` (the acknowledged operation the runtime's
903
- * turn loop applies). Run-level cancellation targets the ROOT, which has no acknowledged runtime
904
- * path yet for a non-retained tree — it stays a `cancel.request.json` write inside the run
905
- * directory for a host process to honor. Nothing here joins a path from the workspace root.
906
- *
907
- * Extracted from the `loops` repo (`src/top.ts`). The module-level singletons are the origin's
908
- * shape and are kept: this drives one terminal, and `runTopApp` is its one entry point.
909
- *
910
- * @experimental
911
- */
912
- let state = initialState([], process.cwd());
913
- let snapshot = {
914
- root: state.root,
915
- generatedAt: 0,
916
- supervisors: [],
917
- completeness: "complete",
918
- diagnostics: [],
919
- discovered: 0,
920
- loaded: 0
921
- };
922
- let targets = [];
923
- let timer;
924
- /**
925
- * Render exactly one frame and return it. This is the non-interactive path — `--once`, a pipe, a
926
- * test — so it never touches raw mode, the alternate screen, or `process.exit`.
927
- */
928
- function renderTopOnce(options = {}) {
929
- state = initialState([...options.argv ?? process.argv.slice(2)], options.cwd ?? process.cwd());
930
- snapshot = loadTopSnapshot(state.root);
931
- clampSelection();
932
- return renderTopFrameWithLayout(snapshot, {
933
- color: state.color,
934
- width: process.stdout.columns ?? 128,
935
- focus: state.focus,
936
- mode: state.mode,
937
- ...state.selectedSupervisorId ? { selectedSupervisorId: state.selectedSupervisorId } : {},
938
- ...state.selectedWorkerId ? { selectedWorkerId: state.selectedWorkerId } : {},
939
- steerInput: renderSteerInputState()
940
- }).frame;
941
- }
942
- /**
943
- * Run the TUI. With a TTY on both ends and no `--once` this takes over the terminal until `q`;
944
- * otherwise it writes a single frame to stdout and returns.
945
- */
946
- function runTopApp(options = {}) {
947
- const args = [...options.argv ?? process.argv.slice(2)];
948
- if (!process.stdout.isTTY || !process.stdin.isTTY || args.includes("--once")) {
949
- process.stdout.write(renderTopOnce(options));
950
- return;
951
- }
952
- state = initialState(args, options.cwd ?? process.cwd());
953
- snapshot = loadTopSnapshot(state.root);
954
- start();
955
- }
956
- function initialState(args, cwd) {
957
- return {
958
- root: resolve(args.find((arg) => !arg.startsWith("-")) ?? cwd),
959
- color: Boolean(process.stdout.isTTY) && !args.includes("--no-color"),
960
- focus: args.includes("--detail") ? "workers" : "supervisors",
961
- mode: args.includes("--log") ? "log" : args.includes("--detail") ? "detail" : "overview",
962
- selectedSupervisorId: void 0,
963
- selectedWorkerId: void 0,
964
- notice: void 0,
965
- steerInput: {
966
- active: false,
967
- value: ""
968
- }
969
- };
970
- }
971
- function start() {
972
- process.stdout.write("\x1B[?1049h\x1B[?25l\x1B[?1000h\x1B[?1006h");
973
- emitKeypressEvents(process.stdin);
974
- process.stdin.setRawMode(true);
975
- process.stdin.resume();
976
- process.stdin.on("keypress", onKey);
977
- process.stdin.on("data", onData);
978
- process.on("SIGINT", stop);
979
- draw();
980
- timer = setInterval(draw, 1e3);
981
- }
982
- function stop() {
983
- if (timer) clearInterval(timer);
984
- process.stdin.setRawMode(false);
985
- process.stdout.write("\x1B[?1000l\x1B[?1006l\x1B[?25h\x1B[?1049l");
986
- process.exit(0);
987
- }
988
- function draw() {
989
- snapshot = loadTopSnapshot(state.root);
990
- clampSelection();
991
- const rendered = renderTopFrameWithLayout(snapshot, {
992
- width: process.stdout.columns ?? 128,
993
- height: process.stdout.rows ?? 42,
994
- color: state.color,
995
- focus: state.focus,
996
- mode: state.mode,
997
- ...state.notice ? { notice: state.notice } : {},
998
- ...state.selectedSupervisorId ? { selectedSupervisorId: state.selectedSupervisorId } : {},
999
- ...state.selectedWorkerId ? { selectedWorkerId: state.selectedWorkerId } : {},
1000
- steerInput: renderSteerInputState()
1001
- });
1002
- targets = rendered.targets;
1003
- process.stdout.write(`\x1b[2J\x1b[H${rendered.frame}`);
1004
- }
1005
- function onKey(str, key) {
1006
- state.notice = void 0;
1007
- if (key.ctrl && key.name === "c") stop();
1008
- if (state.steerInput.active) {
1009
- handleSteerInput(str, key);
1010
- draw();
1011
- return;
1012
- }
1013
- if (key.name === "q" || key.name === "escape") stop();
1014
- if (key.name === "tab") state.focus = state.focus === "supervisors" ? "workers" : "supervisors";
1015
- else if (key.name === "up") moveSelection(-1);
1016
- else if (key.name === "down") moveSelection(1);
1017
- else if (key.name === "left") moveSupervisor(-1);
1018
- else if (key.name === "right") moveSupervisor(1);
1019
- else if (key.name === "return") {
1020
- state.mode = "detail";
1021
- state.focus = "workers";
1022
- } else if (key.name === "o") state.mode = "overview";
1023
- else if (key.name === "l") state.mode = "log";
1024
- else if (key.name === "s") startSteerInput();
1025
- else if (key.name === "c") requestCancel();
1026
- draw();
1027
- }
1028
- function handleSteerInput(str, key) {
1029
- if (key.name === "escape") {
1030
- state.steerInput = {
1031
- active: false,
1032
- value: ""
1033
- };
1034
- return;
1035
- }
1036
- if (key.name === "return") {
1037
- submitSteerInput();
1038
- return;
1039
- }
1040
- if (key.name === "backspace") {
1041
- state.steerInput.value = state.steerInput.value.slice(0, -1);
1042
- return;
1043
- }
1044
- if (key.ctrl && key.name === "u") {
1045
- state.steerInput.value = "";
1046
- return;
1047
- }
1048
- if (key.ctrl || key.name === "tab" || key.name === "up" || key.name === "down" || key.name === "left" || key.name === "right") return;
1049
- if (!str || str === "\r" || str === "\n") return;
1050
- if ([...str].every((char) => char >= " " && char !== "")) state.steerInput.value += str;
1051
- }
1052
- function onData(data) {
1053
- const click = parseMouse(data.toString("utf8"));
1054
- if (!click) return;
1055
- const target = targets.find((candidate) => candidate.row === click.row);
1056
- if (!target) return;
1057
- state.notice = void 0;
1058
- if (target.kind === "supervisor") {
1059
- state.focus = "supervisors";
1060
- state.selectedSupervisorId = target.id;
1061
- state.selectedWorkerId = void 0;
1062
- } else {
1063
- state.focus = "workers";
1064
- state.mode = "detail";
1065
- state.selectedSupervisorId = target.supervisorId;
1066
- state.selectedWorkerId = target.id;
1067
- }
1068
- draw();
1069
- }
1070
- function startSteerInput() {
1071
- state.mode = "detail";
1072
- state.focus = "workers";
1073
- if (!selectedSupervisor()) {
1074
- state.notice = "no supervisor selected";
1075
- return;
1076
- }
1077
- if (!selectedWorker()) {
1078
- state.notice = "no worker selected";
1079
- return;
1080
- }
1081
- state.steerInput = {
1082
- active: true,
1083
- value: ""
1084
- };
1085
- }
1086
- function submitSteerInput() {
1087
- const supervisor = selectedSupervisor();
1088
- const worker = selectedWorker();
1089
- const message = state.steerInput.value;
1090
- state.steerInput = {
1091
- active: false,
1092
- value: ""
1093
- };
1094
- if (!supervisor) {
1095
- state.notice = "no supervisor selected";
1096
- return;
1097
- }
1098
- if (!worker) {
1099
- state.notice = "no worker selected";
1100
- return;
1101
- }
1102
- try {
1103
- const written = writeWorkerSteer(state.root, supervisor.id, worker.id, {
1104
- operationId: randomUUID(),
1105
- message,
1106
- source: "agent-runtime-top"
1107
- });
1108
- state.notice = `steer queued for ${supervisor.id}/${worker.label} (op ${written.request.operationId})`;
1109
- } catch (err) {
1110
- state.notice = `steer failed: ${err instanceof Error ? err.message : String(err)}`;
1111
- }
1112
- }
1113
- function parseMouse(raw) {
1114
- const match = /\x1b\[<(\d+);(\d+);(\d+)([mM])/.exec(raw);
1115
- if (match?.[4] !== "M") return void 0;
1116
- const col = Number(match[2]);
1117
- const row = Number(match[3]);
1118
- if (!Number.isFinite(col) || !Number.isFinite(row)) return void 0;
1119
- return {
1120
- col: col - 1,
1121
- row: row - 1
1122
- };
1123
- }
1124
- function moveSelection(delta) {
1125
- if (state.focus === "supervisors") {
1126
- moveSupervisor(delta);
1127
- return;
1128
- }
1129
- const supervisor = selectedSupervisor();
1130
- if (!supervisor || supervisor.workers.length === 0) return;
1131
- const next = wrap(Math.max(0, supervisor.workers.findIndex((worker) => worker.id === state.selectedWorkerId)) + delta, supervisor.workers.length);
1132
- const worker = supervisor.workers[next];
1133
- if (worker) state.selectedWorkerId = worker.id;
1134
- }
1135
- function moveSupervisor(delta) {
1136
- if (snapshot.supervisors.length === 0) return;
1137
- const next = wrap(Math.max(0, snapshot.supervisors.findIndex((supervisor) => supervisor.id === state.selectedSupervisorId)) + delta, snapshot.supervisors.length);
1138
- const supervisor = snapshot.supervisors[next];
1139
- if (!supervisor) return;
1140
- state.selectedSupervisorId = supervisor.id;
1141
- state.selectedWorkerId = supervisor.workers[0]?.id;
1142
- }
1143
- function clampSelection() {
1144
- if (snapshot.supervisors.length === 0) {
1145
- state.selectedSupervisorId = void 0;
1146
- state.selectedWorkerId = void 0;
1147
- return;
1148
- }
1149
- const supervisor = selectedSupervisor() ?? snapshot.supervisors[0];
1150
- if (!supervisor) return;
1151
- state.selectedSupervisorId = supervisor.id;
1152
- const worker = supervisor.workers.find((candidate) => candidate.id === state.selectedWorkerId) ?? supervisor.workers[0];
1153
- state.selectedWorkerId = worker?.id;
1154
- }
1155
- function selectedSupervisor() {
1156
- return snapshot.supervisors.find((supervisor) => supervisor.id === state.selectedSupervisorId);
1157
- }
1158
- function selectedWorker() {
1159
- return selectedSupervisor()?.workers.find((worker) => worker.id === state.selectedWorkerId);
1160
- }
1161
- function renderSteerInputState() {
1162
- const worker = selectedWorker();
1163
- return {
1164
- active: state.steerInput.active,
1165
- value: state.steerInput.value,
1166
- ...worker?.label ? { workerLabel: worker.label } : {}
1167
- };
1168
- }
1169
- function requestCancel() {
1170
- const supervisor = selectedSupervisor();
1171
- if (!supervisor) {
1172
- state.notice = "no supervisor selected";
1173
- return;
1174
- }
1175
- if (supervisor.status !== "running") {
1176
- state.notice = `${supervisor.id} is ${supervisor.status}; no cancel request written`;
1177
- return;
1178
- }
1179
- const worker = state.focus === "workers" ? selectedWorker() : void 0;
1180
- if (worker) {
1181
- if (worker.status !== "running") {
1182
- state.notice = `${worker.label} is ${worker.status}; nothing to cancel`;
1183
- return;
1184
- }
1185
- try {
1186
- const record = cancelWorker(supervisor.stateDir, worker.id, randomUUID(), {
1187
- reason: "operator requested cancel from TUI",
1188
- source: "agent-runtime-top"
1189
- });
1190
- state.notice = record.effect === "unknown" ? `cancel queued for ${supervisor.id}/${worker.label} (op ${record.operationId}); awaiting runtime acknowledgement` : `cancel ${record.effect} for ${supervisor.id}/${worker.label} (op ${record.operationId})`;
1191
- } catch (err) {
1192
- state.notice = `cancel request failed: ${err instanceof Error ? err.message : String(err)}`;
1193
- }
1194
- return;
1195
- }
1196
- try {
1197
- const record = cancelRun(supervisor.stateDir, randomUUID(), {
1198
- reason: "operator requested cancel from TUI",
1199
- source: "agent-runtime-top"
1200
- });
1201
- state.notice = record.effect === "unknown" ? `run cancel queued for ${supervisor.id} (op ${record.operationId}); awaiting runtime acknowledgement` : `run cancel ${record.effect} for ${supervisor.id} (op ${record.operationId})`;
1202
- } catch (err) {
1203
- state.notice = `cancel request failed: ${err instanceof Error ? err.message : String(err)}`;
1204
- }
1205
- }
1206
- function wrap(value, size) {
1207
- return (value % size + size) % size;
1208
- }
1209
- //#endregion
1210
- export { renderTopFrameWithLayout as a, renderTopFrame as i, runTopApp as n, loadTopSnapshot as r, renderTopOnce as t };
1211
-
1212
- //# sourceMappingURL=top-app-BaMw1W2z.js.map