@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
package/dist/tui/bin.js CHANGED
@@ -1,5 +1,715 @@
1
1
  #!/usr/bin/env node
2
- import { n as runTopApp } from "../top-app-BaMw1W2z.js";
2
+ import { _ as supervisorRunsRoot, n as cancelRun, s as cancelWorker, x as writeWorkerSteer } from "../run-cancellation-C9oL62sW.js";
3
+ import { a as parseTime, i as parseSpend, n as loadTopSnapshot, o as totalSpend, r as numberValue, t as aggregateSnapshot } from "../top-snapshot-CbZE1JsU.js";
4
+ import { randomUUID } from "node:crypto";
5
+ import { resolve } from "node:path";
6
+ import { emitKeypressEvents } from "node:readline";
7
+ //#region src/tui/top-frame.ts
8
+ const ansiPattern = /\x1b\[[0-9;?]*[ -/]*[@-~]/g;
9
+ const colors = {
10
+ reset: "\x1B[0m",
11
+ dim: "\x1B[2m",
12
+ bold: "\x1B[1m",
13
+ red: "\x1B[31m",
14
+ green: "\x1B[32m",
15
+ yellow: "\x1B[33m",
16
+ blue: "\x1B[34m",
17
+ magenta: "\x1B[35m",
18
+ cyan: "\x1B[36m",
19
+ white: "\x1B[37m",
20
+ inverse: "\x1B[7m"
21
+ };
22
+ /**
23
+ * Render one snapshot, returning the frame together with the row→entity map a mouse click resolves
24
+ * against. The layout is the only thing that knows which row is which run or worker, so emitting it
25
+ * alongside the text is what keeps click handling out of the renderer.
26
+ */
27
+ function renderTopFrameWithLayout(snapshot, options = {}) {
28
+ const width = Math.max(88, options.width ?? 128);
29
+ const height = Math.max(24, options.height ?? 42);
30
+ const color = options.color ?? false;
31
+ const focus = options.focus ?? "supervisors";
32
+ const mode = options.mode ?? "overview";
33
+ const selectedSupervisor = selectSupervisor(snapshot, options.selectedSupervisorId);
34
+ const selectedWorker = selectedSupervisor ? selectWorker(selectedSupervisor, options.selectedWorkerId) : void 0;
35
+ const targets = [];
36
+ const lines = [];
37
+ const push = (line = "", target) => {
38
+ if (lines.length >= height) return;
39
+ if (target) targets.push({
40
+ row: lines.length,
41
+ ...target
42
+ });
43
+ lines.push(fitAnsi(line, width));
44
+ };
45
+ const aggregate = aggregateSnapshot(snapshot);
46
+ 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)}`);
47
+ 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`);
48
+ if (options.notice) push(paint(options.notice, "yellow", color));
49
+ 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));
50
+ if (options.steerInput?.active) push(renderSteerInput(options.steerInput, width, color));
51
+ push(rule(width, color));
52
+ if (snapshot.supervisors.length === 0) {
53
+ if (snapshot.discovered > 0) {
54
+ push(paint("NO READABLE SUPERVISORS", "yellow", color));
55
+ push(`${snapshot.discovered} run director${snapshot.discovered === 1 ? "y" : "ies"} found under ${supervisorRunsRoot(snapshot.root)}, none readable`);
56
+ } else {
57
+ push(paint("NO SUPERVISORS", "yellow", color));
58
+ push(`No run state under ${supervisorRunsRoot(snapshot.root)}`);
59
+ }
60
+ push(`A run appears here once its driver writes state.json; re-point with: agent-runtime-top <root>`);
61
+ return {
62
+ frame: `${lines.join("\n")}\n`,
63
+ targets
64
+ };
65
+ }
66
+ push(sectionTitle("SUPERVISORS", focus === "supervisors", color));
67
+ push(` id status workers spend tok in/out latency task`);
68
+ for (const supervisor of snapshot.supervisors.slice(0, 9)) {
69
+ const selected = supervisor.id === selectedSupervisor?.id;
70
+ const status = statusText(supervisor.status, color);
71
+ const pointer = selected ? paint(">", "cyan", color) : " ";
72
+ const rowFocus = selected && focus === "supervisors" ? "inverse" : void 0;
73
+ const workerSummary = `${supervisor.totals.running}/${supervisor.totals.workers} live`;
74
+ const task = compact(supervisor.task, 34);
75
+ 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}`;
76
+ push(rowFocus ? paint(line, rowFocus, color) : line, {
77
+ kind: "supervisor",
78
+ id: supervisor.id
79
+ });
80
+ }
81
+ if (!selectedSupervisor) return {
82
+ frame: `${lines.join("\n")}\n`,
83
+ targets
84
+ };
85
+ push();
86
+ push(sectionTitle(`WORKERS ${selectedSupervisor.id}`, focus === "workers", color));
87
+ push(` id state runtime latency cost tok in/out score detail`);
88
+ for (const worker of selectedSupervisor.workers.slice(0, 12)) {
89
+ const selected = worker.id === selectedWorker?.id;
90
+ const pointer = selected ? paint(">", "cyan", color) : " ";
91
+ const rowFocus = selected && focus === "workers" ? "inverse" : void 0;
92
+ const score = worker.verdict ?? (worker.status === "done" ? "done" : worker.status);
93
+ const detail = worker.reason ?? worker.outRef ?? worker.parent ?? "";
94
+ const spend = totalSpend(worker);
95
+ 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)}`;
96
+ push(rowFocus ? paint(line, rowFocus, color) : line, {
97
+ kind: "worker",
98
+ id: worker.id,
99
+ supervisorId: selectedSupervisor.id
100
+ });
101
+ }
102
+ if (selectedSupervisor.workers.length === 0) push(" no workers spawned yet");
103
+ push();
104
+ push(sectionTitle("OBSERVABILITY", false, color));
105
+ for (const line of renderStatusBars(selectedSupervisor, width, color)) push(line);
106
+ push();
107
+ for (const line of renderCostBars(selectedSupervisor, width, color)) push(line);
108
+ push();
109
+ for (const line of renderTokenBars(selectedSupervisor, width, color)) push(line);
110
+ push();
111
+ for (const line of renderLatencyHistogram(selectedSupervisor, width, color)) push(line);
112
+ push();
113
+ if (mode === "log") {
114
+ push(sectionTitle("JOURNAL TAIL", false, color));
115
+ for (const line of renderJournalTail(selectedSupervisor, width, color)) push(line);
116
+ } else {
117
+ push(sectionTitle(mode === "detail" ? "DETAIL" : "RUN SUMMARY", false, color));
118
+ for (const line of renderDetail(selectedSupervisor, selectedWorker, mode, width, color)) push(line);
119
+ }
120
+ return {
121
+ frame: `${lines.join("\n")}\n`,
122
+ targets
123
+ };
124
+ }
125
+ function renderStatusBars(supervisor, width, color) {
126
+ const total = Math.max(1, supervisor.totals.workers);
127
+ const barWidth = Math.max(12, Math.min(28, width - 58));
128
+ 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}`];
129
+ }
130
+ function renderCostBars(supervisor, width, color) {
131
+ const workers = supervisor.workers.map((worker) => ({
132
+ worker,
133
+ spend: totalSpend(worker)
134
+ })).sort((a, b) => b.spend.usd - a.spend.usd).slice(0, 6);
135
+ const maxUsd = Math.max(1e-6, ...workers.map((row) => row.spend.usd), supervisor.driverSpend.usd);
136
+ const out = [`cost total ${formatMoney(supervisor.totals.usd)} driver ${formatMoney(supervisor.driverSpend.usd)}`];
137
+ const barWidth = Math.max(18, Math.min(46, width - 38));
138
+ for (const row of workers) out.push(` ${pad(row.worker.label, 12)} ${bar(row.spend.usd, maxUsd, barWidth, "$", color, "magenta")} ${formatMoney(row.spend.usd)}`);
139
+ if (workers.length === 0) out.push(" no worker cost yet");
140
+ return out;
141
+ }
142
+ function renderTokenBars(supervisor, width, color) {
143
+ const rows = supervisor.workers.map((worker) => ({
144
+ worker,
145
+ spend: totalSpend(worker),
146
+ total: totalSpend(worker).tokensInput + totalSpend(worker).tokensOutput
147
+ })).sort((a, b) => b.total - a.total).slice(0, 6);
148
+ const maxTokens = Math.max(1, ...rows.map((row) => row.total), supervisor.driverSpend.tokensInput + supervisor.driverSpend.tokensOutput);
149
+ const out = [`tokens total in ${formatTokens(supervisor.totals.tokensInput)} / out ${formatTokens(supervisor.totals.tokensOutput)} driver ${formatTokens(supervisor.driverSpend.tokensInput)}/${formatTokens(supervisor.driverSpend.tokensOutput)}`];
150
+ const barWidth = Math.max(18, Math.min(46, width - 44));
151
+ 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)}`);
152
+ if (rows.length === 0) out.push(" no worker tokens yet");
153
+ return out;
154
+ }
155
+ function renderLatencyHistogram(supervisor, width, color) {
156
+ const values = supervisor.workers.map((worker) => worker.latencyMs).filter((value) => value > 0);
157
+ const summary = supervisor.totals.workerLatency;
158
+ const out = [`latency n=${summary.n} min ${formatDuration(summary.min)} p50 ${formatDuration(summary.median)} p90 ${formatDuration(summary.p90)} max ${formatDuration(summary.max)}`];
159
+ if (values.length === 0) {
160
+ out.push(" no latency samples yet");
161
+ return out;
162
+ }
163
+ const bins = histogram(values, 6);
164
+ const maxCount = Math.max(1, ...bins.map((bin) => bin.count));
165
+ const barWidth = Math.max(18, Math.min(48, width - 34));
166
+ for (const bin of bins) out.push(` ${pad(`${formatDuration(bin.min)}-${formatDuration(bin.max)}`, 15)} ${bar(bin.count, maxCount, barWidth, "#", color, "blue")} ${bin.count}`);
167
+ return out;
168
+ }
169
+ function renderDetail(supervisor, worker, mode, width, color) {
170
+ if (mode === "detail" && worker) {
171
+ const spend = totalSpend(worker);
172
+ return [
173
+ `worker ${paint(worker.label, "bold", color)} (${worker.id}) ${statusText(worker.status, color)} latency ${formatDuration(worker.latencyMs)}`,
174
+ `tokens in/out ${formatTokens(spend.tokensInput)}/${formatTokens(spend.tokensOutput)} cost ${formatMoney(spend.usd)} iterations ${spend.iterations}`,
175
+ `parent ${worker.parent ?? "-"} runtime ${worker.runtime ?? "-"} out ${worker.outRef ?? "-"}${worker.infra ? " infra" : ""}`,
176
+ `events ${compact(worker.eventFile ?? "-", width - 8)}`,
177
+ ...worker.reason ? [`reason ${compact(worker.reason, width - 7)}`] : [],
178
+ ...renderWorkerTail(worker, width, color)
179
+ ];
180
+ }
181
+ const caps = [
182
+ supervisor.maxSandboxes !== void 0 ? `boxes ${supervisor.maxSandboxes}` : void 0,
183
+ supervisor.maxUsd !== void 0 ? `max ${formatMoney(supervisor.maxUsd)}` : void 0,
184
+ supervisor.maxDepth !== void 0 ? `depth ${supervisor.maxDepth}` : void 0,
185
+ supervisor.maxLifetimeSeconds !== void 0 ? `ttl ${supervisor.maxLifetimeSeconds}s` : void 0
186
+ ].filter((value) => value !== void 0);
187
+ return [
188
+ `task ${compact(supervisor.task, width - 7)}`,
189
+ `workspace ${compact(supervisor.workspaceDir, width - 12)}`,
190
+ `verify ${compact(supervisor.verifyCmd ?? "-", width - 9)}`,
191
+ `models worker ${supervisor.workerModel ?? "-"} / driver ${supervisor.driverModel ?? "-"} caps ${caps.join(" | ") || "-"}`,
192
+ `progress ${compact(supervisor.progress ?? supervisor.verdict ?? supervisor.status, width - 10)}`
193
+ ];
194
+ }
195
+ function renderJournalTail(supervisor, width, color) {
196
+ if (supervisor.journalTail.length === 0) return [" no journal events yet"];
197
+ return supervisor.journalTail.map((event) => {
198
+ const at = event.at ? formatClock(event.at) : "--:--:--";
199
+ if (event.kind === "spawned") return ` ${at} ${paint("spawn", "yellow", color)} ${pad(event.label ?? event.id, 16)} ${event.id}`;
200
+ if (event.kind === "settled") {
201
+ const spend = parseSpend(event.spent);
202
+ return ` ${at} ${statusText(event.status ?? "settled", color)} ${pad(event.id, 18)} ${formatMoney(spend.usd)} ${formatTokens(spend.tokensInput)}/${formatTokens(spend.tokensOutput)}`;
203
+ }
204
+ if (event.kind === "metered") {
205
+ const spend = parseSpend(event.spend);
206
+ return ` ${at} ${paint("meter", "cyan", color)} ${pad(event.id, 18)} ${formatMoney(spend.usd)} ${formatTokens(spend.tokensInput)}/${formatTokens(spend.tokensOutput)}`;
207
+ }
208
+ if (event.kind === "progress") {
209
+ const spend = parseSpend(event.spend);
210
+ return ` ${at} ${paint("live", "blue", color)} ${pad(event.id, 18)} ${formatMoney(spend.usd)} ${formatTokens(spend.tokensInput)}/${formatTokens(spend.tokensOutput)}`;
211
+ }
212
+ return ` ${at} ${paint("cancel", "red", color)} ${pad(event.id, 18)} ${compact(event.reason ?? "", width - 32)}`;
213
+ });
214
+ }
215
+ function renderWorkerTail(worker, width, color) {
216
+ if (worker.liveTail.length === 0) return ["live events: none recorded yet"];
217
+ return ["live events:", ...worker.liveTail.slice(-5).map((line) => ` ${paint(compact(formatWorkerEvent(line), width - 2), "dim", color)}`)];
218
+ }
219
+ function renderSteerInput(input, width, color) {
220
+ const target = input.workerLabel ?? "-";
221
+ const value = compact(input.value, Math.max(10, width - target.length - 14));
222
+ return `${paint("steer", "cyan", color)} ${target} > ${value}${paint("_", "inverse", color)}`;
223
+ }
224
+ function formatWorkerEvent(line) {
225
+ try {
226
+ const parsed = JSON.parse(line);
227
+ const at = typeof parsed.at === "string" ? formatClock(parsed.at) : "--:--:--";
228
+ const kind = typeof parsed.kind === "string" ? parsed.kind : "event";
229
+ if (kind === "progress") return `${at} progress iter ${numberValue(parsed.iteration)} ${String(parsed.phase ?? "")}`;
230
+ if (kind === "message") {
231
+ const state = parsed.queued === true ? "queued" : "sent";
232
+ const source = typeof parsed.source === "string" && parsed.source.length > 0 ? `${parsed.source} ` : "";
233
+ const message = typeof parsed.message === "string" && parsed.message.length > 0 ? ` ${parsed.message}` : "";
234
+ return `${at} ${source}steer ${state} delivered=${String(parsed.delivered)}${message}`;
235
+ }
236
+ if (kind === "verify") return `${at} verify passed=${String(parsed.passed)} exit=${String(parsed.exitCode ?? "-")}${evidenceSnippet(parsed.outputTail, 160)}`;
237
+ if (kind === "finished") {
238
+ const branch = typeof parsed.branch === "string" ? ` branch=${parsed.branch}` : "";
239
+ const files = typeof parsed.filesChanged === "number" ? ` files=${parsed.filesChanged}` : "";
240
+ const tests = typeof parsed.testPassed === "boolean" ? ` tests=${String(parsed.testPassed)}` : "";
241
+ return `${at} finished passed=${String(parsed.passed)} patchBytes=${numberValue(parsed.patchBytes)}${files}${tests}${branch}${evidenceSnippet(parsed.evidence, 220)}`;
242
+ }
243
+ if (kind === "error") return `${at} error ${String(parsed.error ?? "")}`;
244
+ return `${at} ${kind}`;
245
+ } catch {
246
+ return line;
247
+ }
248
+ }
249
+ /** One-line the settle evidence / verify tail a worker event carries so the
250
+ * failure is legible in the tail view (the full block stays in the ndjson). */
251
+ function evidenceSnippet(value, max) {
252
+ if (typeof value !== "string") return "";
253
+ const oneLine = value.replace(/\s+/g, " ").trim();
254
+ return oneLine ? ` :: ${compact(oneLine, max)}` : "";
255
+ }
256
+ function histogram(values, count) {
257
+ const min = Math.min(...values);
258
+ const max = Math.max(...values);
259
+ if (min === max) return [{
260
+ min,
261
+ max,
262
+ count: values.length
263
+ }];
264
+ const step = (max - min) / count;
265
+ return Array.from({ length: count }, (_, index) => {
266
+ const low = min + step * index;
267
+ const high = index === count - 1 ? max : min + step * (index + 1);
268
+ return {
269
+ min: low,
270
+ max: high,
271
+ count: values.filter((value) => value >= low && (index === count - 1 ? value <= high : value < high)).length
272
+ };
273
+ }).filter((bin) => bin.count > 0);
274
+ }
275
+ function bar(value, max, width, ch, color, colorName) {
276
+ const filled = Math.max(0, Math.min(width, Math.round(value / Math.max(max, 1e-6) * width)));
277
+ return paint(ch.repeat(filled).padEnd(width, "."), colorName, color);
278
+ }
279
+ function stackedTokenBar(input, output, max, width, color) {
280
+ const inputWidth = Math.round(input / Math.max(max, 1) * width);
281
+ const outputWidth = Math.round(output / Math.max(max, 1) * width);
282
+ const clippedInput = Math.min(width, inputWidth);
283
+ const clippedOutput = Math.min(width - clippedInput, outputWidth);
284
+ const rest = Math.max(0, width - clippedInput - clippedOutput);
285
+ return `${paint("#".repeat(clippedInput), "cyan", color)}${paint("+".repeat(clippedOutput), "green", color)}${".".repeat(rest)}`;
286
+ }
287
+ function sectionTitle(title, focused, color) {
288
+ return paint(focused ? `[${title}]` : title, focused ? "cyan" : "bold", color);
289
+ }
290
+ function statusText(status, color) {
291
+ if ([
292
+ "completed",
293
+ "done",
294
+ "delivered",
295
+ "valid"
296
+ ].includes(status)) return paint(status.toUpperCase(), "green", color);
297
+ if ([
298
+ "failed",
299
+ "down",
300
+ "cancelled",
301
+ "invalid",
302
+ "error"
303
+ ].includes(status)) return paint(status.toUpperCase(), "red", color);
304
+ if ([
305
+ "running",
306
+ "spawned",
307
+ "settled"
308
+ ].includes(status)) return paint(status.toUpperCase(), "yellow", color);
309
+ return paint(status.toUpperCase(), "white", color);
310
+ }
311
+ function selectSupervisor(snapshot, requested) {
312
+ if (requested) {
313
+ const found = snapshot.supervisors.find((supervisor) => supervisor.id === requested);
314
+ if (found) return found;
315
+ }
316
+ return snapshot.supervisors[0];
317
+ }
318
+ function selectWorker(supervisor, requested) {
319
+ if (requested) {
320
+ const found = supervisor.workers.find((worker) => worker.id === requested);
321
+ if (found) return found;
322
+ }
323
+ return supervisor.workers[0];
324
+ }
325
+ function formatClock(value) {
326
+ const parsed = parseTime(value);
327
+ if (parsed === void 0) return "--:--:--";
328
+ return new Date(parsed).toISOString().slice(11, 19);
329
+ }
330
+ function formatMoney(value) {
331
+ if (value === 0) return "$0";
332
+ if (Math.abs(value) < .01) return `$${value.toFixed(4)}`;
333
+ return `$${value.toFixed(2)}`;
334
+ }
335
+ function formatTokens(value) {
336
+ if (value >= 1e6) return `${(value / 1e6).toFixed(1)}m`;
337
+ if (value >= 1e3) return `${(value / 1e3).toFixed(1)}k`;
338
+ return `${Math.round(value)}`;
339
+ }
340
+ function formatDuration(ms) {
341
+ if (!Number.isFinite(ms) || ms <= 0) return "0s";
342
+ if (ms < 1e3) return `${Math.round(ms)}ms`;
343
+ const seconds = ms / 1e3;
344
+ if (seconds < 60) return `${seconds.toFixed(seconds < 10 ? 1 : 0)}s`;
345
+ const minutes = seconds / 60;
346
+ if (minutes < 60) return `${minutes.toFixed(minutes < 10 ? 1 : 0)}m`;
347
+ return `${(minutes / 60).toFixed(1)}h`;
348
+ }
349
+ function compact(value, width) {
350
+ if (width <= 1) return "";
351
+ if (value.length <= width) return value;
352
+ return `${value.slice(0, width - 1)}~`;
353
+ }
354
+ function pad(value, width) {
355
+ const visible = visibleLength(value);
356
+ if (visible >= width) return fitAnsi(value, width);
357
+ return `${value}${" ".repeat(width - visible)}`;
358
+ }
359
+ function rule(width, color) {
360
+ return paint("-".repeat(width), "dim", color);
361
+ }
362
+ function paint(value, colorName, enabled) {
363
+ if (!enabled || !colorName) return value;
364
+ return `${colors[colorName]}${value}${colors.reset}`;
365
+ }
366
+ function visibleLength(value) {
367
+ return stripAnsi(value).length;
368
+ }
369
+ function stripAnsi(value) {
370
+ return value.replace(ansiPattern, "");
371
+ }
372
+ function fitAnsi(value, width) {
373
+ if (visibleLength(value) <= width) return value;
374
+ let out = "";
375
+ let visible = 0;
376
+ for (let i = 0; i < value.length; i += 1) {
377
+ const ch = value[i];
378
+ if (ch === "\x1B") {
379
+ const rest = value.slice(i);
380
+ const match = /^\x1b\[[0-9;?]*[ -/]*[@-~]/.exec(rest);
381
+ if (match) {
382
+ out += match[0];
383
+ i += match[0].length - 1;
384
+ continue;
385
+ }
386
+ }
387
+ if (visible >= width - 1) {
388
+ out += "~";
389
+ break;
390
+ }
391
+ out += ch;
392
+ visible += 1;
393
+ }
394
+ if (out.includes("\x1B[") && !out.endsWith(colors.reset)) out += colors.reset;
395
+ return out;
396
+ }
397
+ //#endregion
398
+ //#region src/tui/top-app.ts
399
+ /**
400
+ * The interactive side of the supervisor-run TUI: a keypress/mouse loop over the frames
401
+ * `./top-frame` renders, plus the operator controls that write back to the run — steer a live
402
+ * worker, cancel one worker, and request cancellation of a whole run.
403
+ *
404
+ * The controls go through the owned run layout: steers via `writeWorkerSteer` (the durable inbox
405
+ * append) and worker cancellation via `cancelWorker` (the acknowledged operation the runtime's
406
+ * turn loop applies). Run-level cancellation targets the ROOT, which has no acknowledged runtime
407
+ * path yet for a non-retained tree — it stays a `cancel.request.json` write inside the run
408
+ * directory for a host process to honor. Nothing here joins a path from the workspace root.
409
+ *
410
+ * Extracted from the `loops` repo (`src/top.ts`). The module-level singletons are the origin's
411
+ * shape and are kept: this drives one terminal, and `runTopApp` is its one entry point.
412
+ *
413
+ * @experimental
414
+ */
415
+ let state = initialState([], process.cwd());
416
+ let snapshot = {
417
+ root: state.root,
418
+ generatedAt: 0,
419
+ supervisors: [],
420
+ completeness: "complete",
421
+ diagnostics: [],
422
+ discovered: 0,
423
+ loaded: 0
424
+ };
425
+ let targets = [];
426
+ let timer;
427
+ /**
428
+ * Render exactly one frame and return it. This is the non-interactive path — `--once`, a pipe, a
429
+ * test — so it never touches raw mode, the alternate screen, or `process.exit`.
430
+ */
431
+ function renderTopOnce(options = {}) {
432
+ state = initialState([...options.argv ?? process.argv.slice(2)], options.cwd ?? process.cwd());
433
+ snapshot = loadTopSnapshot(state.root);
434
+ clampSelection();
435
+ return renderTopFrameWithLayout(snapshot, {
436
+ color: state.color,
437
+ width: process.stdout.columns ?? 128,
438
+ focus: state.focus,
439
+ mode: state.mode,
440
+ ...state.selectedSupervisorId ? { selectedSupervisorId: state.selectedSupervisorId } : {},
441
+ ...state.selectedWorkerId ? { selectedWorkerId: state.selectedWorkerId } : {},
442
+ steerInput: renderSteerInputState()
443
+ }).frame;
444
+ }
445
+ /**
446
+ * Run the TUI. With a TTY on both ends and no `--once` this takes over the terminal until `q`;
447
+ * otherwise it writes a single frame to stdout and returns.
448
+ */
449
+ function runTopApp(options = {}) {
450
+ const args = [...options.argv ?? process.argv.slice(2)];
451
+ if (!process.stdout.isTTY || !process.stdin.isTTY || args.includes("--once")) {
452
+ process.stdout.write(renderTopOnce(options));
453
+ return;
454
+ }
455
+ state = initialState(args, options.cwd ?? process.cwd());
456
+ snapshot = loadTopSnapshot(state.root);
457
+ start();
458
+ }
459
+ function initialState(args, cwd) {
460
+ return {
461
+ root: resolve(args.find((arg) => !arg.startsWith("-")) ?? cwd),
462
+ color: Boolean(process.stdout.isTTY) && !args.includes("--no-color"),
463
+ focus: args.includes("--detail") ? "workers" : "supervisors",
464
+ mode: args.includes("--log") ? "log" : args.includes("--detail") ? "detail" : "overview",
465
+ selectedSupervisorId: void 0,
466
+ selectedWorkerId: void 0,
467
+ notice: void 0,
468
+ steerInput: {
469
+ active: false,
470
+ value: ""
471
+ }
472
+ };
473
+ }
474
+ function start() {
475
+ process.stdout.write("\x1B[?1049h\x1B[?25l\x1B[?1000h\x1B[?1006h");
476
+ emitKeypressEvents(process.stdin);
477
+ process.stdin.setRawMode(true);
478
+ process.stdin.resume();
479
+ process.stdin.on("keypress", onKey);
480
+ process.stdin.on("data", onData);
481
+ process.on("SIGINT", stop);
482
+ draw();
483
+ timer = setInterval(draw, 1e3);
484
+ }
485
+ function stop() {
486
+ if (timer) clearInterval(timer);
487
+ process.stdin.setRawMode(false);
488
+ process.stdout.write("\x1B[?1000l\x1B[?1006l\x1B[?25h\x1B[?1049l");
489
+ process.exit(0);
490
+ }
491
+ function draw() {
492
+ snapshot = loadTopSnapshot(state.root);
493
+ clampSelection();
494
+ const rendered = renderTopFrameWithLayout(snapshot, {
495
+ width: process.stdout.columns ?? 128,
496
+ height: process.stdout.rows ?? 42,
497
+ color: state.color,
498
+ focus: state.focus,
499
+ mode: state.mode,
500
+ ...state.notice ? { notice: state.notice } : {},
501
+ ...state.selectedSupervisorId ? { selectedSupervisorId: state.selectedSupervisorId } : {},
502
+ ...state.selectedWorkerId ? { selectedWorkerId: state.selectedWorkerId } : {},
503
+ steerInput: renderSteerInputState()
504
+ });
505
+ targets = rendered.targets;
506
+ process.stdout.write(`\x1b[2J\x1b[H${rendered.frame}`);
507
+ }
508
+ function onKey(str, key) {
509
+ state.notice = void 0;
510
+ if (key.ctrl && key.name === "c") stop();
511
+ if (state.steerInput.active) {
512
+ handleSteerInput(str, key);
513
+ draw();
514
+ return;
515
+ }
516
+ if (key.name === "q" || key.name === "escape") stop();
517
+ if (key.name === "tab") state.focus = state.focus === "supervisors" ? "workers" : "supervisors";
518
+ else if (key.name === "up") moveSelection(-1);
519
+ else if (key.name === "down") moveSelection(1);
520
+ else if (key.name === "left") moveSupervisor(-1);
521
+ else if (key.name === "right") moveSupervisor(1);
522
+ else if (key.name === "return") {
523
+ state.mode = "detail";
524
+ state.focus = "workers";
525
+ } else if (key.name === "o") state.mode = "overview";
526
+ else if (key.name === "l") state.mode = "log";
527
+ else if (key.name === "s") startSteerInput();
528
+ else if (key.name === "c") requestCancel();
529
+ draw();
530
+ }
531
+ function handleSteerInput(str, key) {
532
+ if (key.name === "escape") {
533
+ state.steerInput = {
534
+ active: false,
535
+ value: ""
536
+ };
537
+ return;
538
+ }
539
+ if (key.name === "return") {
540
+ submitSteerInput();
541
+ return;
542
+ }
543
+ if (key.name === "backspace") {
544
+ state.steerInput.value = state.steerInput.value.slice(0, -1);
545
+ return;
546
+ }
547
+ if (key.ctrl && key.name === "u") {
548
+ state.steerInput.value = "";
549
+ return;
550
+ }
551
+ if (key.ctrl || key.name === "tab" || key.name === "up" || key.name === "down" || key.name === "left" || key.name === "right") return;
552
+ if (!str || str === "\r" || str === "\n") return;
553
+ if ([...str].every((char) => char >= " " && char !== "")) state.steerInput.value += str;
554
+ }
555
+ function onData(data) {
556
+ const click = parseMouse(data.toString("utf8"));
557
+ if (!click) return;
558
+ const target = targets.find((candidate) => candidate.row === click.row);
559
+ if (!target) return;
560
+ state.notice = void 0;
561
+ if (target.kind === "supervisor") {
562
+ state.focus = "supervisors";
563
+ state.selectedSupervisorId = target.id;
564
+ state.selectedWorkerId = void 0;
565
+ } else {
566
+ state.focus = "workers";
567
+ state.mode = "detail";
568
+ state.selectedSupervisorId = target.supervisorId;
569
+ state.selectedWorkerId = target.id;
570
+ }
571
+ draw();
572
+ }
573
+ function startSteerInput() {
574
+ state.mode = "detail";
575
+ state.focus = "workers";
576
+ if (!selectedSupervisor()) {
577
+ state.notice = "no supervisor selected";
578
+ return;
579
+ }
580
+ if (!selectedWorker()) {
581
+ state.notice = "no worker selected";
582
+ return;
583
+ }
584
+ state.steerInput = {
585
+ active: true,
586
+ value: ""
587
+ };
588
+ }
589
+ function submitSteerInput() {
590
+ const supervisor = selectedSupervisor();
591
+ const worker = selectedWorker();
592
+ const message = state.steerInput.value;
593
+ state.steerInput = {
594
+ active: false,
595
+ value: ""
596
+ };
597
+ if (!supervisor) {
598
+ state.notice = "no supervisor selected";
599
+ return;
600
+ }
601
+ if (!worker) {
602
+ state.notice = "no worker selected";
603
+ return;
604
+ }
605
+ try {
606
+ const written = writeWorkerSteer(state.root, supervisor.id, worker.id, {
607
+ operationId: randomUUID(),
608
+ message,
609
+ source: "agent-runtime-top"
610
+ });
611
+ state.notice = `steer queued for ${supervisor.id}/${worker.label} (op ${written.request.operationId})`;
612
+ } catch (err) {
613
+ state.notice = `steer failed: ${err instanceof Error ? err.message : String(err)}`;
614
+ }
615
+ }
616
+ function parseMouse(raw) {
617
+ const match = /\x1b\[<(\d+);(\d+);(\d+)([mM])/.exec(raw);
618
+ if (match?.[4] !== "M") return void 0;
619
+ const col = Number(match[2]);
620
+ const row = Number(match[3]);
621
+ if (!Number.isFinite(col) || !Number.isFinite(row)) return void 0;
622
+ return {
623
+ col: col - 1,
624
+ row: row - 1
625
+ };
626
+ }
627
+ function moveSelection(delta) {
628
+ if (state.focus === "supervisors") {
629
+ moveSupervisor(delta);
630
+ return;
631
+ }
632
+ const supervisor = selectedSupervisor();
633
+ if (!supervisor || supervisor.workers.length === 0) return;
634
+ const next = wrap(Math.max(0, supervisor.workers.findIndex((worker) => worker.id === state.selectedWorkerId)) + delta, supervisor.workers.length);
635
+ const worker = supervisor.workers[next];
636
+ if (worker) state.selectedWorkerId = worker.id;
637
+ }
638
+ function moveSupervisor(delta) {
639
+ if (snapshot.supervisors.length === 0) return;
640
+ const next = wrap(Math.max(0, snapshot.supervisors.findIndex((supervisor) => supervisor.id === state.selectedSupervisorId)) + delta, snapshot.supervisors.length);
641
+ const supervisor = snapshot.supervisors[next];
642
+ if (!supervisor) return;
643
+ state.selectedSupervisorId = supervisor.id;
644
+ state.selectedWorkerId = supervisor.workers[0]?.id;
645
+ }
646
+ function clampSelection() {
647
+ if (snapshot.supervisors.length === 0) {
648
+ state.selectedSupervisorId = void 0;
649
+ state.selectedWorkerId = void 0;
650
+ return;
651
+ }
652
+ const supervisor = selectedSupervisor() ?? snapshot.supervisors[0];
653
+ if (!supervisor) return;
654
+ state.selectedSupervisorId = supervisor.id;
655
+ const worker = supervisor.workers.find((candidate) => candidate.id === state.selectedWorkerId) ?? supervisor.workers[0];
656
+ state.selectedWorkerId = worker?.id;
657
+ }
658
+ function selectedSupervisor() {
659
+ return snapshot.supervisors.find((supervisor) => supervisor.id === state.selectedSupervisorId);
660
+ }
661
+ function selectedWorker() {
662
+ return selectedSupervisor()?.workers.find((worker) => worker.id === state.selectedWorkerId);
663
+ }
664
+ function renderSteerInputState() {
665
+ const worker = selectedWorker();
666
+ return {
667
+ active: state.steerInput.active,
668
+ value: state.steerInput.value,
669
+ ...worker?.label ? { workerLabel: worker.label } : {}
670
+ };
671
+ }
672
+ function requestCancel() {
673
+ const supervisor = selectedSupervisor();
674
+ if (!supervisor) {
675
+ state.notice = "no supervisor selected";
676
+ return;
677
+ }
678
+ if (supervisor.status !== "running") {
679
+ state.notice = `${supervisor.id} is ${supervisor.status}; no cancel request written`;
680
+ return;
681
+ }
682
+ const worker = state.focus === "workers" ? selectedWorker() : void 0;
683
+ if (worker) {
684
+ if (worker.status !== "running") {
685
+ state.notice = `${worker.label} is ${worker.status}; nothing to cancel`;
686
+ return;
687
+ }
688
+ try {
689
+ const record = cancelWorker(supervisor.stateDir, worker.id, randomUUID(), {
690
+ reason: "operator requested cancel from TUI",
691
+ source: "agent-runtime-top"
692
+ });
693
+ 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})`;
694
+ } catch (err) {
695
+ state.notice = `cancel request failed: ${err instanceof Error ? err.message : String(err)}`;
696
+ }
697
+ return;
698
+ }
699
+ try {
700
+ const record = cancelRun(supervisor.stateDir, randomUUID(), {
701
+ reason: "operator requested cancel from TUI",
702
+ source: "agent-runtime-top"
703
+ });
704
+ 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})`;
705
+ } catch (err) {
706
+ state.notice = `cancel request failed: ${err instanceof Error ? err.message : String(err)}`;
707
+ }
708
+ }
709
+ function wrap(value, size) {
710
+ return (value % size + size) % size;
711
+ }
712
+ //#endregion
3
713
  //#region src/tui/bin.ts
4
714
  /**
5
715
  * `agent-runtime-top` — watch the supervisor runs of one workspace.