@tangle-network/agent-runtime 0.53.0 → 0.54.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 (71) hide show
  1. package/README.md +27 -16
  2. package/dist/agent.d.ts +1 -1
  3. package/dist/agent.js +5 -3
  4. package/dist/agent.js.map +1 -1
  5. package/dist/analyst-loop.d.ts +1 -1
  6. package/dist/analyst-loop.js +2 -2
  7. package/dist/{chunk-K5M3SHEU.js → chunk-2BDXWZUC.js} +5 -34
  8. package/dist/chunk-2BDXWZUC.js.map +1 -0
  9. package/dist/chunk-A73RADPB.js +281 -0
  10. package/dist/chunk-A73RADPB.js.map +1 -0
  11. package/dist/chunk-FRBWCJLP.js +396 -0
  12. package/dist/chunk-FRBWCJLP.js.map +1 -0
  13. package/dist/chunk-GRAGM4MC.js +72 -0
  14. package/dist/chunk-GRAGM4MC.js.map +1 -0
  15. package/dist/{chunk-K6WP7PYW.js → chunk-JTH2FPCK.js} +3231 -424
  16. package/dist/chunk-JTH2FPCK.js.map +1 -0
  17. package/dist/{chunk-P4QNEXFC.js → chunk-KJH62YEK.js} +2 -2
  18. package/dist/{chunk-HNUXAZIJ.js → chunk-P5OKDSLB.js} +2 -2
  19. package/dist/{chunk-GSUO5QS6.js → chunk-VLF5RHEQ.js} +2 -5
  20. package/dist/{chunk-GSUO5QS6.js.map → chunk-VLF5RHEQ.js.map} +1 -1
  21. package/dist/{chunk-JFIYKDXF.js → chunk-XRYEZPR6.js} +2 -1
  22. package/dist/chunk-XRYEZPR6.js.map +1 -0
  23. package/dist/{coder-LKm3Mczw.d.ts → coder-CEkUFv8h.d.ts} +1 -1
  24. package/dist/{types-CNDJCL_0.d.ts → coordination-BMzskrUR.d.ts} +268 -2
  25. package/dist/{kb-gate-CKfykcYQ.d.ts → delegates-BPLIl8EC.d.ts} +491 -566
  26. package/dist/index.d.ts +7 -7
  27. package/dist/index.js +23 -80
  28. package/dist/index.js.map +1 -1
  29. package/dist/intelligence.d.ts +2 -2
  30. package/dist/kb-gate-CuzMYGYM.d.ts +76 -0
  31. package/dist/{loop-runner-bin-D4Ir7b00.d.ts → loop-runner-bin-BGpVVyXp.d.ts} +6 -27
  32. package/dist/loop-runner-bin.d.ts +5 -5
  33. package/dist/loop-runner-bin.js +6 -5
  34. package/dist/loops.d.ts +8 -6
  35. package/dist/loops.js +32 -8
  36. package/dist/mcp/bin.js +94 -30
  37. package/dist/mcp/bin.js.map +1 -1
  38. package/dist/mcp/index.d.ts +9 -272
  39. package/dist/mcp/index.js +45 -365
  40. package/dist/mcp/index.js.map +1 -1
  41. package/dist/{openai-tools-CKfR3EMh.d.ts → openai-tools-CoeLQ7Uo.d.ts} +1 -1
  42. package/dist/profiles.d.ts +2 -2
  43. package/dist/profiles.js +4 -4
  44. package/dist/{router-client-B0Qi1NiN.d.ts → router-client-30Y_pca8.d.ts} +1 -1
  45. package/dist/{run-loop-DgVhucoR.d.ts → run-loop-DluzfJ2h.d.ts} +1 -1
  46. package/dist/runtime.d.ts +424 -23
  47. package/dist/runtime.js +32 -8
  48. package/dist/topology.d.ts +54 -1
  49. package/dist/topology.js +222 -0
  50. package/dist/topology.js.map +1 -1
  51. package/dist/{types-CklkW4Eh.d.ts → types-C8rNlxfV.d.ts} +4 -4
  52. package/dist/workflow.d.ts +2 -2
  53. package/dist/workflow.js +4 -2
  54. package/dist/workflow.js.map +1 -1
  55. package/package.json +5 -5
  56. package/skills/agent-runtime-adoption/SKILL.md +54 -46
  57. package/skills/build-with-agent-runtime/SKILL.md +12 -8
  58. package/skills/loop-writer/SKILL.md +9 -1
  59. package/skills/supervise/SKILL.md +24 -0
  60. package/dist/chunk-5M2WDWBI.js +0 -908
  61. package/dist/chunk-5M2WDWBI.js.map +0 -1
  62. package/dist/chunk-AYRQZRDV.js +0 -623
  63. package/dist/chunk-AYRQZRDV.js.map +0 -1
  64. package/dist/chunk-FO4DCM7R.js +0 -1356
  65. package/dist/chunk-FO4DCM7R.js.map +0 -1
  66. package/dist/chunk-JFIYKDXF.js.map +0 -1
  67. package/dist/chunk-K5M3SHEU.js.map +0 -1
  68. package/dist/chunk-K6WP7PYW.js.map +0 -1
  69. package/dist/driver-B2RKkVJW.d.ts +0 -221
  70. /package/dist/{chunk-P4QNEXFC.js.map → chunk-KJH62YEK.js.map} +0 -0
  71. /package/dist/{chunk-HNUXAZIJ.js.map → chunk-P5OKDSLB.js.map} +0 -0
package/dist/runtime.js CHANGED
@@ -8,17 +8,19 @@ import {
8
8
  acquireSandbox,
9
9
  adaptiveRefine,
10
10
  anytimeReport,
11
+ asAuthoredProfile,
11
12
  assertStrategyContract,
12
13
  assertTraceDerivedFindings,
13
14
  auditIntent,
14
15
  authorStrategy,
16
+ authoredWorker,
15
17
  breadthDriver,
16
18
  buildSteerContext,
17
19
  builtinShapes,
18
20
  completionAuthorizes,
19
21
  contentAddress,
22
+ coordinationDriverAgent,
20
23
  createBudgetPool,
21
- createDriver,
22
24
  createExecutor,
23
25
  createExecutorRegistry,
24
26
  createMcpEnvironment,
@@ -39,17 +41,23 @@ import {
39
41
  depthDriver,
40
42
  deterministicCompletion,
41
43
  discriminatingMeans,
44
+ driverChild,
45
+ driverExecutorFactory,
46
+ driverRuntime,
42
47
  equalKOnCost,
43
48
  fanout,
44
49
  flatWidenGate,
50
+ gateOnDeliverable,
45
51
  gitWorkspace,
46
52
  harvestCorpus,
47
53
  inlineSandboxClient,
54
+ isDriverSpec,
48
55
  jjWorkspace,
49
56
  localShell,
50
57
  loopDispatch,
51
58
  loopUntil,
52
59
  materializeTreeView,
60
+ nestedScopeSeamKey,
53
61
  observe,
54
62
  openSandboxRun,
55
63
  panel,
@@ -61,7 +69,6 @@ import {
61
69
  refine,
62
70
  registerShape,
63
71
  registryScopeAnalyst,
64
- renderAnalyses,
65
72
  renderAnytimeTable,
66
73
  renderCorpusToInstructions,
67
74
  renderReport,
@@ -72,6 +79,7 @@ import {
72
79
  routerToolLoop,
73
80
  runAgentic,
74
81
  runBenchmark,
82
+ runInWorkspace,
75
83
  runLoop,
76
84
  runPersonified,
77
85
  runStrategyEvolution,
@@ -79,18 +87,23 @@ import {
79
87
  sampleThenRefine,
80
88
  selectChampion,
81
89
  sentinelCompletion,
90
+ serveCoordinationMcp,
82
91
  settledToIteration,
83
92
  spendFromUsageEvents,
84
93
  stopSentinel,
85
94
  strategyAuthorContract,
95
+ supervisorSkill,
86
96
  trajectoryReport,
87
97
  verify,
88
- widen
89
- } from "./chunk-K6WP7PYW.js";
98
+ widen,
99
+ withDriverExecutor
100
+ } from "./chunk-JTH2FPCK.js";
101
+ import "./chunk-WIR4HOOJ.js";
90
102
  import {
91
103
  extractLlmCallEvent,
92
104
  mapSandboxEvent
93
- } from "./chunk-GSUO5QS6.js";
105
+ } from "./chunk-VLF5RHEQ.js";
106
+ import "./chunk-G3RGMA7C.js";
94
107
  import "./chunk-DGUM43GV.js";
95
108
  export {
96
109
  FileCorpus,
@@ -102,17 +115,19 @@ export {
102
115
  acquireSandbox,
103
116
  adaptiveRefine,
104
117
  anytimeReport,
118
+ asAuthoredProfile,
105
119
  assertStrategyContract,
106
120
  assertTraceDerivedFindings,
107
121
  auditIntent,
108
122
  authorStrategy,
123
+ authoredWorker,
109
124
  breadthDriver,
110
125
  buildSteerContext,
111
126
  builtinShapes,
112
127
  completionAuthorizes,
113
128
  contentAddress,
129
+ coordinationDriverAgent,
114
130
  createBudgetPool,
115
- createDriver,
116
131
  createExecutor,
117
132
  createExecutorRegistry,
118
133
  createMcpEnvironment,
@@ -133,19 +148,25 @@ export {
133
148
  depthDriver,
134
149
  deterministicCompletion,
135
150
  discriminatingMeans,
151
+ driverChild,
152
+ driverExecutorFactory,
153
+ driverRuntime,
136
154
  equalKOnCost,
137
155
  extractLlmCallEvent,
138
156
  fanout,
139
157
  flatWidenGate,
158
+ gateOnDeliverable,
140
159
  gitWorkspace,
141
160
  harvestCorpus,
142
161
  inlineSandboxClient,
162
+ isDriverSpec,
143
163
  jjWorkspace,
144
164
  localShell,
145
165
  loopDispatch,
146
166
  loopUntil,
147
167
  mapSandboxEvent,
148
168
  materializeTreeView,
169
+ nestedScopeSeamKey,
149
170
  observe,
150
171
  openSandboxRun,
151
172
  panel,
@@ -157,7 +178,6 @@ export {
157
178
  refine,
158
179
  registerShape,
159
180
  registryScopeAnalyst,
160
- renderAnalyses,
161
181
  renderAnytimeTable,
162
182
  renderCorpusToInstructions,
163
183
  renderReport,
@@ -168,6 +188,7 @@ export {
168
188
  routerToolLoop,
169
189
  runAgentic,
170
190
  runBenchmark,
191
+ runInWorkspace,
171
192
  runLoop,
172
193
  runPersonified,
173
194
  runStrategyEvolution,
@@ -175,12 +196,15 @@ export {
175
196
  sampleThenRefine,
176
197
  selectChampion,
177
198
  sentinelCompletion,
199
+ serveCoordinationMcp,
178
200
  settledToIteration,
179
201
  spendFromUsageEvents,
180
202
  stopSentinel,
181
203
  strategyAuthorContract,
204
+ supervisorSkill,
182
205
  trajectoryReport,
183
206
  verify,
184
- widen
207
+ widen,
208
+ withDriverExecutor
185
209
  };
186
210
  //# sourceMappingURL=runtime.js.map
@@ -1,5 +1,58 @@
1
1
  import { R as RuntimeHooks, a as RuntimeHookEvent } from './runtime-hooks-C7JwKb9E.js';
2
2
 
3
+ /**
4
+ * @experimental
5
+ *
6
+ * Run replay — the visual, animated record of a recursive agent run.
7
+ *
8
+ * The runtime emits ONE event stream (`agent.spawn`/`agent.child`/`agent.run`/`agent.turn`)
9
+ * through `RuntimeHooks`; the topology tree + waterfall already fold it into ASCII. This module
10
+ * folds the SAME stream into a normalized, timestamped `ReplayEvent[]` (the recorder) and renders
11
+ * a self-contained, animated HTML player (`renderReplayHtml`) — a timeline scrubber over the live
12
+ * recursive tree where every node colors by the completion-oracle: delivered (valid) green, ran-
13
+ * but-not-delivered amber, failed red. No server, no build, no external deps — one HTML file you
14
+ * open in a browser. The same `ReplayEvent[]` is the portable timeline a hosted plane viewer reads.
15
+ */
16
+
17
+ /** One normalized animation frame — a node appearing, settling, or stepping, at a wall-clock ms. */
18
+ interface ReplayEvent {
19
+ t: number;
20
+ kind: 'root' | 'spawn' | 'settle' | 'step';
21
+ id: string;
22
+ parentId?: string;
23
+ label?: string;
24
+ runtime?: string;
25
+ depth?: number;
26
+ status?: 'running' | 'done' | 'down';
27
+ /** The completion-oracle signal: delivered ⟺ a deployable check passed (not self-report). */
28
+ valid?: boolean;
29
+ score?: number;
30
+ reason?: string;
31
+ tokens?: number;
32
+ usd?: number;
33
+ }
34
+ interface ReplayTimeline {
35
+ runId: string;
36
+ events: ReplayEvent[];
37
+ /** Wall-clock window [t0, t1] the player scrubs over. */
38
+ t0: number;
39
+ t1: number;
40
+ }
41
+ /**
42
+ * A `RuntimeHooks` sink that records every lifecycle event in arrival order as `ReplayEvent`s.
43
+ * Attach it to `SupervisorOpts.hooks` (or merge with another hooks object) and read `timeline()`
44
+ * after the run. Pure capture — no I/O, no throwing; an unrecognized event is ignored.
45
+ */
46
+ declare function createReplayRecorder(): {
47
+ hooks: RuntimeHooks;
48
+ events: ReplayEvent[];
49
+ timeline(runId?: string): ReplayTimeline;
50
+ };
51
+ /** Render a self-contained animated HTML replay player for a timeline. Open the file in a browser. */
52
+ declare function renderReplayHtml(timeline: ReplayTimeline, opts?: {
53
+ title?: string;
54
+ }): string;
55
+
3
56
  /**
4
57
  * @experimental
5
58
  *
@@ -70,4 +123,4 @@ declare function renderTopologyTree(tree: {
70
123
  node: (id: string) => TopologyNode | undefined;
71
124
  }, opts?: RenderOptions): string;
72
125
 
73
- export { type RenderOptions, type TopologyNode, type TopologyStatus, type TopologyView, createTopologyView, renderTopologyTree };
126
+ export { type RenderOptions, type ReplayEvent, type ReplayTimeline, type TopologyNode, type TopologyStatus, type TopologyView, createReplayRecorder, createTopologyView, renderReplayHtml, renderTopologyTree };
package/dist/topology.js CHANGED
@@ -1,5 +1,225 @@
1
1
  import "./chunk-DGUM43GV.js";
2
2
 
3
+ // src/topology/replay.ts
4
+ function spendTokens(s) {
5
+ if (!s?.tokens) return 0;
6
+ return (s.tokens.input ?? 0) + (s.tokens.output ?? 0);
7
+ }
8
+ function createReplayRecorder() {
9
+ const events = [];
10
+ let runId = "run";
11
+ const onEvent = (e) => {
12
+ if (e.runId) runId = e.runId;
13
+ const t = e.timestamp;
14
+ const p = e.payload ?? {};
15
+ switch (e.target) {
16
+ case "agent.run": {
17
+ if (e.phase === "before") {
18
+ events.push({
19
+ t,
20
+ kind: "root",
21
+ id: e.runId,
22
+ label: String(p.driver ?? "root"),
23
+ depth: 0,
24
+ status: "running"
25
+ });
26
+ } else if (e.phase === "after" || e.phase === "error") {
27
+ events.push({
28
+ t,
29
+ kind: "settle",
30
+ id: e.runId,
31
+ status: e.phase === "error" ? "down" : "done"
32
+ });
33
+ }
34
+ break;
35
+ }
36
+ case "agent.spawn": {
37
+ events.push({
38
+ t,
39
+ kind: "spawn",
40
+ id: String(p.childId ?? e.id),
41
+ ...e.parentId ? { parentId: e.parentId } : {},
42
+ label: String(p.label ?? p.childId ?? "?"),
43
+ ...p.runtime ? { runtime: String(p.runtime) } : {},
44
+ ...typeof p.depth === "number" ? { depth: p.depth } : {},
45
+ status: "running"
46
+ });
47
+ break;
48
+ }
49
+ case "agent.child": {
50
+ const spent = p.spent;
51
+ events.push({
52
+ t,
53
+ kind: "settle",
54
+ id: String(p.childId ?? e.id),
55
+ status: p.status ?? "done",
56
+ ...typeof p.valid === "boolean" ? { valid: p.valid } : {},
57
+ ...typeof p.score === "number" ? { score: p.score } : {},
58
+ ...p.reason ? { reason: String(p.reason) } : {},
59
+ tokens: spendTokens(spent),
60
+ usd: spent?.usd ?? 0
61
+ });
62
+ break;
63
+ }
64
+ default: {
65
+ const owner = e.parentId ?? e.runId;
66
+ if (owner) events.push({ t, kind: "step", id: owner });
67
+ }
68
+ }
69
+ };
70
+ return {
71
+ hooks: { onEvent },
72
+ events,
73
+ timeline(rid) {
74
+ const ts = events.map((e) => e.t);
75
+ const t0 = ts.length ? Math.min(...ts) : 0;
76
+ const t1 = ts.length ? Math.max(...ts) : 0;
77
+ const defined = new Set(
78
+ events.filter((e) => e.kind === "spawn" || e.kind === "root").map((e) => e.id)
79
+ );
80
+ const synthetic = [];
81
+ for (const id of new Set(events.map((e) => e.parentId).filter((p) => !!p))) {
82
+ if (!defined.has(id))
83
+ synthetic.push({
84
+ t: t0,
85
+ kind: "root",
86
+ id,
87
+ label: shortRoot(id),
88
+ depth: 0,
89
+ status: "running"
90
+ });
91
+ }
92
+ return { runId: rid ?? runId, events: [...synthetic, ...events], t0, t1 };
93
+ }
94
+ };
95
+ }
96
+ function renderReplayHtml(timeline, opts) {
97
+ const title = opts?.title ?? `agent replay \xB7 ${timeline.runId}`;
98
+ const data = JSON.stringify(timeline);
99
+ return `<!doctype html>
100
+ <html lang="en"><head><meta charset="utf-8"/><meta name="viewport" content="width=device-width,initial-scale=1"/>
101
+ <title>${escapeHtml(title)}</title>
102
+ <style>
103
+ :root { --bg:#0b0e14; --panel:#11161f; --line:#2a3340; --txt:#c9d4e3; --dim:#6b7787;
104
+ --run:#e0a73a; --done:#3ad17a; --undeliv:#e0a73a; --down:#e25d5d; --edge:#33405200; }
105
+ * { box-sizing:border-box; }
106
+ body { margin:0; background:var(--bg); color:var(--txt); font:13px/1.5 ui-monospace,SFMono-Regular,Menlo,monospace; }
107
+ header { padding:10px 16px; border-bottom:1px solid var(--line); display:flex; gap:18px; align-items:center; flex-wrap:wrap; }
108
+ header b { color:#fff; font-weight:600; } .stat { color:var(--dim); } .stat span { color:var(--txt); }
109
+ #wrap { display:flex; height:calc(100vh - 112px); }
110
+ #stage { flex:1; overflow:auto; }
111
+ #side { width:280px; border-left:1px solid var(--line); padding:14px; overflow:auto; background:var(--panel); }
112
+ #side h3 { margin:0 0 8px; font-size:12px; color:var(--dim); text-transform:uppercase; letter-spacing:.08em; }
113
+ #side .k { color:var(--dim); } #side .row { margin:3px 0; }
114
+ svg { display:block; } .node circle { stroke:#0b0e14; stroke-width:2; cursor:pointer; }
115
+ .node text { fill:var(--txt); font-size:11px; } .node .sub { fill:var(--dim); font-size:9px; }
116
+ .edge { stroke:#2a3340; stroke-width:1.5; fill:none; }
117
+ .hidden { opacity:0; } .node, .edge { transition:opacity .25s ease, fill .25s, stroke .25s; }
118
+ footer { padding:10px 16px; border-top:1px solid var(--line); display:flex; gap:14px; align-items:center; }
119
+ button { background:#1b2433; color:var(--txt); border:1px solid var(--line); border-radius:6px; padding:6px 14px; cursor:pointer; font:inherit; }
120
+ button:hover { background:#243047; } input[type=range] { flex:1; accent-color:#3ad17a; }
121
+ select { background:#1b2433; color:var(--txt); border:1px solid var(--line); border-radius:6px; padding:5px; }
122
+ .legend i { display:inline-block; width:9px; height:9px; border-radius:50%; margin:0 4px 0 10px; vertical-align:middle; }
123
+ </style></head><body>
124
+ <header>
125
+ <b>${escapeHtml(title)}</b>
126
+ <span class="legend"><i style="background:var(--run)"></i>running<i style="background:var(--done)"></i>delivered<i style="background:var(--down)"></i>failed</span>
127
+ <span class="stat">t <span id="clock">0.0s</span></span>
128
+ <span class="stat">nodes <span id="cNodes">0</span></span>
129
+ <span class="stat">delivered <span id="cDeliv">0</span></span>
130
+ <span class="stat">tokens <span id="cTok">0</span></span>
131
+ <span class="stat">$<span id="cUsd">0.000</span></span>
132
+ </header>
133
+ <div id="wrap"><div id="stage"><svg id="svg"></svg></div>
134
+ <div id="side"><h3>node</h3><div id="detail" class="stat">hover a node\u2026</div></div></div>
135
+ <footer>
136
+ <button id="play">\u25B6 play</button>
137
+ <button id="restart">\u27F2</button>
138
+ <input type="range" id="scrub" min="0" max="1000" value="0"/>
139
+ <select id="speed"><option value="1">1\xD7</option><option value="2">2\xD7</option><option value="4" selected>4\xD7</option><option value="8">8\xD7</option><option value="20">20\xD7</option></select>
140
+ </footer>
141
+ <script>
142
+ const TL = ${data};
143
+ const EV = TL.events.slice().sort((a,b)=>a.t-b.t);
144
+ const span = Math.max(1, TL.t1 - TL.t0);
145
+ // \u2500\u2500 build the full tree (nodes known from spawn/root events) \u2500\u2500
146
+ const nodes = new Map();
147
+ for (const e of EV) {
148
+ if (e.kind==='root' || e.kind==='spawn') {
149
+ if (!nodes.has(e.id)) nodes.set(e.id, { id:e.id, parentId:e.parentId, label:e.label||e.id, runtime:e.runtime, depth:e.depth??0, tSpawn:e.t, children:[] });
150
+ }
151
+ }
152
+ for (const n of nodes.values()) { if (n.parentId && nodes.has(n.parentId)) nodes.get(n.parentId).children.push(n); }
153
+ const roots = [...nodes.values()].filter(n=>!n.parentId || !nodes.has(n.parentId));
154
+ // \u2500\u2500 tidy layout: post-order leaf slotting; x=avg(children), y=depth \u2500\u2500
155
+ let leaf=0; const VGAP=78, HGAP=120;
156
+ function layout(n, d){ n.y = 40 + d*VGAP; if(!n.children.length){ n.x = 40 + (leaf++)*HGAP; } else { n.children.forEach(c=>layout(c,d+1)); n.x = (n.children[0].x + n.children[n.children.length-1].x)/2; } }
157
+ roots.forEach(r=>layout(r,0));
158
+ const W = Math.max(900, 80 + leaf*HGAP), H = 80 + (Math.max(0,...[...nodes.values()].map(n=>n.depth||0))+1)*VGAP;
159
+ const svg = document.getElementById('svg'); svg.setAttribute('viewBox','0 0 '+W+' '+H); svg.setAttribute('width',W); svg.setAttribute('height',H);
160
+ const NS='http://www.w3.org/2000/svg';
161
+ function el(t,a){ const x=document.createElementNS(NS,t); for(const k in a) x.setAttribute(k,a[k]); return x; }
162
+ // edges
163
+ const edgeEls=new Map();
164
+ for (const n of nodes.values()) if (n.parentId && nodes.has(n.parentId)) { const p=nodes.get(n.parentId);
165
+ const path=el('path',{class:'edge hidden', d:'M'+p.x+','+(p.y)+' C'+p.x+','+((p.y+n.y)/2)+' '+n.x+','+((p.y+n.y)/2)+' '+n.x+','+n.y}); svg.appendChild(path); edgeEls.set(n.id,path); }
166
+ // nodes
167
+ const nodeEls=new Map();
168
+ for (const n of nodes.values()) { const g=el('g',{class:'node hidden'});
169
+ const c=el('circle',{cx:n.x,cy:n.y,r:14,fill:'var(--dim)'});
170
+ const t=el('text',{x:n.x,y:n.y-20,'text-anchor':'middle'}); t.textContent=n.label.slice(0,18);
171
+ const s=el('text',{class:'sub',x:n.x,y:n.y+26,'text-anchor':'middle'}); s.textContent=n.runtime||'';
172
+ g.appendChild(c); g.appendChild(t); g.appendChild(s); svg.appendChild(g); nodeEls.set(n.id,{g,c,s});
173
+ g.addEventListener('mouseenter',()=>showDetail(n)); }
174
+ function showDetail(n){ const st=state.get(n.id)||{}; document.getElementById('detail').innerHTML =
175
+ '<div class="row"><span class="k">id</span> '+n.id+'</div>'+
176
+ '<div class="row"><span class="k">label</span> '+esc(n.label)+'</div>'+
177
+ '<div class="row"><span class="k">runtime</span> '+(n.runtime||'\u2014')+'</div>'+
178
+ '<div class="row"><span class="k">depth</span> '+(n.depth??0)+'</div>'+
179
+ '<div class="row"><span class="k">status</span> '+(st.status||'\u2014')+(st.valid===false?' (not delivered)':st.valid?' (delivered)':'')+'</div>'+
180
+ '<div class="row"><span class="k">score</span> '+(st.score!=null?st.score.toFixed(2):'\u2014')+'</div>'+
181
+ (st.reason?'<div class="row"><span class="k">reason</span> '+esc(st.reason)+'</div>':'')+
182
+ '<div class="row"><span class="k">tokens</span> '+(st.tokens||0)+'</div>'; }
183
+ function esc(x){ return String(x).replace(/[<>&]/g,c=>({'<':'&lt;','>':'&gt;','&':'&amp;'}[c])); }
184
+ // \u2500\u2500 playback \u2500\u2500
185
+ const state=new Map();
186
+ function colorOf(st){ if(!st||st.status==='running'||!st.status) return 'var(--run)'; if(st.status==='down') return 'var(--down)'; return st.valid===false ? 'var(--undeliv)' : 'var(--done)'; }
187
+ function applyTo(ms){ const clk=TL.t0+ms; state.clear();
188
+ let deliv=0, tok=0, usd=0, shown=0;
189
+ // reset visibility
190
+ for (const [,v] of nodeEls){ v.g.classList.add('hidden'); }
191
+ for (const [,p] of edgeEls){ p.classList.add('hidden'); }
192
+ for (const e of EV){ if (e.t>clk) break;
193
+ if (e.kind==='spawn'||e.kind==='root'){ const ne=nodeEls.get(e.id); if(ne){ ne.g.classList.remove('hidden'); shown++; const ed=edgeEls.get(e.id); if(ed) ed.classList.remove('hidden'); } state.set(e.id, Object.assign(state.get(e.id)||{}, {status:'running'})); }
194
+ else if (e.kind==='settle'){ const st=Object.assign(state.get(e.id)||{}, {status:e.status, valid:e.valid, score:e.score, reason:e.reason, tokens:e.tokens, usd:e.usd}); state.set(e.id,st); }
195
+ }
196
+ for (const [id,st] of state){ const ne=nodeEls.get(id); if(ne) ne.c.setAttribute('fill', colorOf(st)); if(st.status&&st.status!=='running'){ if(st.status==='done'&&st.valid!==false) deliv++; tok+=st.tokens||0; usd+=st.usd||0; } }
197
+ document.getElementById('clock').textContent=(ms/1000).toFixed(1)+'s';
198
+ document.getElementById('cNodes').textContent=shown;
199
+ document.getElementById('cDeliv').textContent=deliv;
200
+ document.getElementById('cTok').textContent=tok;
201
+ document.getElementById('cUsd').textContent=usd.toFixed(3);
202
+ }
203
+ const scrub=document.getElementById('scrub'); let playing=false, raf=0, last=0;
204
+ function setMs(ms){ ms=Math.max(0,Math.min(span,ms)); scrub.value=(ms/span*1000)|0; applyTo(ms); }
205
+ scrub.addEventListener('input',()=>setMs(scrub.value/1000*span));
206
+ document.getElementById('play').addEventListener('click',()=>{ playing=!playing; document.getElementById('play').textContent=playing?'\u23F8 pause':'\u25B6 play'; if(playing){ last=performance.now(); if((scrub.value/1000*span)>=span) setMs(0); tick(); } });
207
+ document.getElementById('restart').addEventListener('click',()=>{ setMs(0); });
208
+ function tick(){ if(!playing) return; const now=performance.now(); const sp=+document.getElementById('speed').value; let ms=scrub.value/1000*span + (now-last)*sp; last=now; if(ms>=span){ ms=span; playing=false; document.getElementById('play').textContent='\u25B6 play'; } setMs(ms); if(playing) raf=requestAnimationFrame(tick); }
209
+ setMs(0);
210
+ </script></body></html>`;
211
+ }
212
+ function shortRoot(id) {
213
+ const seg = id.split("/").pop() ?? id;
214
+ return seg.length > 22 ? `\u2026${seg.slice(-21)}` : seg;
215
+ }
216
+ function escapeHtml(s) {
217
+ return s.replace(
218
+ /[<>&"]/g,
219
+ (c) => ({ "<": "&lt;", ">": "&gt;", "&": "&amp;", '"': "&quot;" })[c] ?? c
220
+ );
221
+ }
222
+
3
223
  // src/topology/tree.ts
4
224
  var stepTargets = /* @__PURE__ */ new Set(["agent.turn", "agent.tool_call", "agent.plan", "agent.decision"]);
5
225
  function createTopologyView() {
@@ -105,7 +325,9 @@ function renderTopologyTree(tree, opts = {}) {
105
325
  return lines.join("\n");
106
326
  }
107
327
  export {
328
+ createReplayRecorder,
108
329
  createTopologyView,
330
+ renderReplayHtml,
109
331
  renderTopologyTree
110
332
  };
111
333
  //# sourceMappingURL=topology.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/topology/tree.ts"],"sourcesContent":["/**\n * @experimental\n *\n * TOPOLOGY VIEW — the live recursive agent tree, folded from the ONE lifecycle stream\n * (`src/runtime-hooks.ts`). It is a pure projection: feed it the `RuntimeHookEvent`s that\n * `runLoop`, `toolLoop`, and the keystone `Scope` already emit, and it maintains the tree of\n * agents + renders it. Attach `view.hooks` to a `Supervisor`/`runLoop` and the tree updates live.\n *\n * Two node sources, ONE stream:\n * - an agent node is born from `agent.spawn` (id = `childId`, parent = the spawner) or the root\n * `agent.run` (id = `runId`); `agent.child`/`agent.run:after` settle it (status + score/reason).\n * - a STEP (`agent.{turn,tool_call,plan,decision}`) is not a node — it advances the step count of\n * the agent it belongs to (matched by `runId`/`parentId`).\n *\n * No I/O, no timers, no backend coupling — the same projection drives a CLI render, a TUI, or a\n * web tree. Rendering is deterministic given the event order (the stream is the source of truth).\n */\n\nimport type { RuntimeHookEvent, RuntimeHooks } from '../runtime-hooks'\n\nexport type TopologyStatus = 'running' | 'done' | 'down'\n\n/** One agent in the tree. A leaf never spawns; a driver's `childIds` is non-empty. */\nexport interface TopologyNode {\n readonly id: string\n /** Display label (spawn `label`, or the driver name on the root). */\n label: string\n /** Leaf runtime (`router`/`sandbox`/`cli`) when known. */\n runtime?: string\n /** Parent agent id; undefined ⇒ a root. */\n parentId?: string\n /** Recursion depth (root = 0). */\n depth: number\n status: TopologyStatus\n /** Count of in-agent steps (turns + tool calls + plan/decision rounds) folded so far. */\n steps: number\n /** Deployable score in [0,1] once settled `done`. */\n score?: number\n /** Failure reason once settled `down`. */\n reason?: string\n /** Children in spawn order. */\n readonly childIds: string[]\n}\n\nexport interface RenderOptions {\n /** Cap the rendered depth (deeper nodes collapse to a `… N more` line). Default: no cap. */\n readonly maxDepth?: number\n /** Drop the per-node detail suffix (steps/children/score) — labels only. Default: false. */\n readonly compact?: boolean\n}\n\nexport interface TopologyView {\n /** The `RuntimeHooks` sink — attach to `SupervisorOpts.hooks` / `runLoop` options. */\n readonly hooks: RuntimeHooks\n /** Fold one event into the tree (the same call `hooks.onEvent` makes — exposed for replay). */\n ingest(event: RuntimeHookEvent): void\n /** Every node, insertion order. */\n nodes(): TopologyNode[]\n /** Nodes with no in-tree parent (the run roots). */\n roots(): TopologyNode[]\n /** One node by id. */\n node(id: string): TopologyNode | undefined\n /** Render the tree as an aligned ASCII forest. */\n render(opts?: RenderOptions): string\n}\n\nconst stepTargets = new Set(['agent.turn', 'agent.tool_call', 'agent.plan', 'agent.decision'])\n\n/** Build a live topology view. Stateful — one per run (or per replay). */\nexport function createTopologyView(): TopologyView {\n // Insertion-ordered so render is stable and roots() reflects spawn order.\n const byId = new Map<string, TopologyNode>()\n\n const ensure = (id: string, seed: Partial<TopologyNode> = {}): TopologyNode => {\n const existing = byId.get(id)\n if (existing) return existing\n const node: TopologyNode = {\n id,\n label: seed.label ?? id,\n depth: seed.depth ?? 0,\n status: 'running',\n steps: 0,\n childIds: [],\n ...seed,\n }\n byId.set(id, node)\n return node\n }\n\n const str = (v: unknown): string | undefined => (typeof v === 'string' ? v : undefined)\n const num = (v: unknown): number | undefined => (typeof v === 'number' ? v : undefined)\n\n const ingest = (event: RuntimeHookEvent): void => {\n const p = (event.payload ?? {}) as Record<string, unknown>\n\n if (event.target === 'agent.spawn' && event.phase === 'after') {\n const id = str(p.childId)\n if (!id) return\n const parent = event.parentId ? ensure(event.parentId) : undefined\n const node = ensure(id, {\n label: str(p.label) ?? id,\n runtime: str(p.runtime),\n parentId: event.parentId,\n depth: num(p.depth) ?? (parent ? parent.depth + 1 : 0),\n })\n if (parent && !parent.childIds.includes(id)) parent.childIds.push(id)\n node.status = 'running'\n return\n }\n\n if (event.target === 'agent.child' && event.phase === 'after') {\n const id = str(p.childId)\n if (!id) return\n const node = ensure(id, { parentId: event.parentId })\n node.status = str(p.status) === 'down' ? 'down' : 'done'\n node.score = num(p.score)\n node.reason = str(p.reason)\n return\n }\n\n if (event.target === 'agent.run') {\n const node = ensure(event.runId, {\n label: str(p.driver) ?? event.runId,\n parentId: event.parentId,\n })\n if (event.phase === 'before') node.status = 'running'\n else if (event.phase === 'error') node.status = 'down'\n else if (event.phase === 'after' && node.status === 'running') node.status = 'done'\n return\n }\n\n // A step advances the agent it belongs to: tool-loop/run-loop key the agent by `runId`,\n // a within-agent sub-event names it via `parentId`. Only count `after`/`event` phases so a\n // before/after pair is one step, not two. An unknown owner is dropped (no phantom node).\n if (stepTargets.has(event.target) && (event.phase === 'after' || event.phase === 'event')) {\n const owner =\n (event.parentId && byId.get(event.parentId)) || byId.get(event.runId) || undefined\n if (owner) owner.steps += 1\n }\n }\n\n const nodes = (): TopologyNode[] => [...byId.values()]\n const roots = (): TopologyNode[] =>\n nodes().filter((n) => n.parentId === undefined || !byId.has(n.parentId))\n\n return {\n hooks: { onEvent: (e) => ingest(e) },\n ingest,\n nodes,\n roots,\n node: (id) => byId.get(id),\n render: (opts) => renderTopologyTree({ roots: roots(), node: (id) => byId.get(id) }, opts),\n }\n}\n\nconst glyph: Record<TopologyStatus, string> = { running: '◐', done: '✓', down: '✗' }\n\n/** Render a forest of `TopologyNode`s to an aligned ASCII tree. Pure — given the same roots +\n * node lookup it returns the same string. Exposed so a caller can render a tree it folded\n * itself (e.g. from a journal replay) without the live view. */\nexport function renderTopologyTree(\n tree: { roots: TopologyNode[]; node: (id: string) => TopologyNode | undefined },\n opts: RenderOptions = {},\n): string {\n const lines: string[] = []\n\n const detail = (n: TopologyNode): string => {\n if (opts.compact) return ''\n const parts: string[] = []\n if (n.runtime) parts.push(n.runtime)\n if (n.steps > 0) parts.push(`${n.steps} ${n.steps === 1 ? 'step' : 'steps'}`)\n if (n.childIds.length > 0) parts.push(`${n.childIds.length} children`)\n if (n.status === 'done' && n.score !== undefined) parts.push(`score ${n.score.toFixed(2)}`)\n if (n.status === 'down' && n.reason) parts.push(`down: ${n.reason}`)\n return parts.length ? ` (${parts.join(' · ')})` : ''\n }\n\n const walk = (n: TopologyNode, prefix: string, isLast: boolean, depth: number): void => {\n const branch = depth === 0 ? '' : isLast ? '└─ ' : '├─ '\n lines.push(`${prefix}${branch}${glyph[n.status]} ${n.label}${detail(n)}`)\n\n if (opts.maxDepth !== undefined && depth >= opts.maxDepth && n.childIds.length > 0) {\n const childPrefix = prefix + (depth === 0 ? '' : isLast ? ' ' : '│ ')\n lines.push(`${childPrefix}└─ … ${n.childIds.length} more`)\n return\n }\n const childPrefix = prefix + (depth === 0 ? '' : isLast ? ' ' : '│ ')\n const kids = n.childIds.map((id) => tree.node(id)).filter((c): c is TopologyNode => !!c)\n kids.forEach((c, i) => {\n walk(c, childPrefix, i === kids.length - 1, depth + 1)\n })\n }\n\n tree.roots.forEach((r, i) => {\n walk(r, '', i === tree.roots.length - 1, 0)\n })\n return lines.join('\\n')\n}\n"],"mappings":"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","names":["childPrefix"]}
1
+ {"version":3,"sources":["../src/topology/replay.ts","../src/topology/tree.ts"],"sourcesContent":["/**\n * @experimental\n *\n * Run replay — the visual, animated record of a recursive agent run.\n *\n * The runtime emits ONE event stream (`agent.spawn`/`agent.child`/`agent.run`/`agent.turn`)\n * through `RuntimeHooks`; the topology tree + waterfall already fold it into ASCII. This module\n * folds the SAME stream into a normalized, timestamped `ReplayEvent[]` (the recorder) and renders\n * a self-contained, animated HTML player (`renderReplayHtml`) — a timeline scrubber over the live\n * recursive tree where every node colors by the completion-oracle: delivered (valid) green, ran-\n * but-not-delivered amber, failed red. No server, no build, no external deps — one HTML file you\n * open in a browser. The same `ReplayEvent[]` is the portable timeline a hosted plane viewer reads.\n */\n\nimport type { RuntimeHookEvent, RuntimeHooks } from '../runtime-hooks'\n\n/** One normalized animation frame — a node appearing, settling, or stepping, at a wall-clock ms. */\nexport interface ReplayEvent {\n t: number\n kind: 'root' | 'spawn' | 'settle' | 'step'\n id: string\n parentId?: string\n label?: string\n runtime?: string\n depth?: number\n status?: 'running' | 'done' | 'down'\n /** The completion-oracle signal: delivered ⟺ a deployable check passed (not self-report). */\n valid?: boolean\n score?: number\n reason?: string\n tokens?: number\n usd?: number\n}\n\nexport interface ReplayTimeline {\n runId: string\n events: ReplayEvent[]\n /** Wall-clock window [t0, t1] the player scrubs over. */\n t0: number\n t1: number\n}\n\ninterface RecordedSpend {\n tokens?: { input?: number; output?: number }\n usd?: number\n}\n\nfunction spendTokens(s: RecordedSpend | undefined): number {\n if (!s?.tokens) return 0\n return (s.tokens.input ?? 0) + (s.tokens.output ?? 0)\n}\n\n/**\n * A `RuntimeHooks` sink that records every lifecycle event in arrival order as `ReplayEvent`s.\n * Attach it to `SupervisorOpts.hooks` (or merge with another hooks object) and read `timeline()`\n * after the run. Pure capture — no I/O, no throwing; an unrecognized event is ignored.\n */\nexport function createReplayRecorder(): {\n hooks: RuntimeHooks\n events: ReplayEvent[]\n timeline(runId?: string): ReplayTimeline\n} {\n const events: ReplayEvent[] = []\n let runId = 'run'\n\n const onEvent = (e: RuntimeHookEvent): void => {\n if (e.runId) runId = e.runId\n const t = e.timestamp\n const p = (e.payload ?? {}) as Record<string, unknown>\n switch (e.target) {\n case 'agent.run': {\n // The root driver's lifecycle. `before` plants the root node; `after`/`error` settle it.\n if (e.phase === 'before') {\n events.push({\n t,\n kind: 'root',\n id: e.runId,\n label: String(p.driver ?? 'root'),\n depth: 0,\n status: 'running',\n })\n } else if (e.phase === 'after' || e.phase === 'error') {\n events.push({\n t,\n kind: 'settle',\n id: e.runId,\n status: e.phase === 'error' ? 'down' : 'done',\n })\n }\n break\n }\n case 'agent.spawn': {\n events.push({\n t,\n kind: 'spawn',\n id: String(p.childId ?? e.id),\n ...(e.parentId ? { parentId: e.parentId } : {}),\n label: String(p.label ?? p.childId ?? '?'),\n ...(p.runtime ? { runtime: String(p.runtime) } : {}),\n ...(typeof p.depth === 'number' ? { depth: p.depth } : {}),\n status: 'running',\n })\n break\n }\n case 'agent.child': {\n const spent = p.spent as RecordedSpend | undefined\n events.push({\n t,\n kind: 'settle',\n id: String(p.childId ?? e.id),\n status: (p.status as 'done' | 'down') ?? 'done',\n ...(typeof p.valid === 'boolean' ? { valid: p.valid } : {}),\n ...(typeof p.score === 'number' ? { score: p.score } : {}),\n ...(p.reason ? { reason: String(p.reason) } : {}),\n tokens: spendTokens(spent),\n usd: spent?.usd ?? 0,\n })\n break\n }\n default: {\n // agent.turn / agent.plan / agent.decision / agent.tool_call → a step pulse on the owner.\n const owner = e.parentId ?? e.runId\n if (owner) events.push({ t, kind: 'step', id: owner })\n }\n }\n }\n\n return {\n hooks: { onEvent },\n events,\n timeline(rid?: string): ReplayTimeline {\n const ts = events.map((e) => e.t)\n const t0 = ts.length ? Math.min(...ts) : 0\n const t1 = ts.length ? Math.max(...ts) : 0\n // Synthesize any node referenced as a parent but never spawned (the supervisor's root\n // driver, and each nested driver's tree root, run via `act`, not `spawn`) so the player\n // renders the WHOLE recursion — driver → worker — not just the spawned leaves.\n const defined = new Set(\n events.filter((e) => e.kind === 'spawn' || e.kind === 'root').map((e) => e.id),\n )\n const synthetic: ReplayEvent[] = []\n for (const id of new Set(events.map((e) => e.parentId).filter((p): p is string => !!p))) {\n if (!defined.has(id))\n synthetic.push({\n t: t0,\n kind: 'root',\n id,\n label: shortRoot(id),\n depth: 0,\n status: 'running',\n })\n }\n return { runId: rid ?? runId, events: [...synthetic, ...events], t0, t1 }\n },\n }\n}\n\n/** Render a self-contained animated HTML replay player for a timeline. Open the file in a browser. */\nexport function renderReplayHtml(timeline: ReplayTimeline, opts?: { title?: string }): string {\n const title = opts?.title ?? `agent replay · ${timeline.runId}`\n const data = JSON.stringify(timeline)\n return `<!doctype html>\n<html lang=\"en\"><head><meta charset=\"utf-8\"/><meta name=\"viewport\" content=\"width=device-width,initial-scale=1\"/>\n<title>${escapeHtml(title)}</title>\n<style>\n :root { --bg:#0b0e14; --panel:#11161f; --line:#2a3340; --txt:#c9d4e3; --dim:#6b7787;\n --run:#e0a73a; --done:#3ad17a; --undeliv:#e0a73a; --down:#e25d5d; --edge:#33405200; }\n * { box-sizing:border-box; }\n body { margin:0; background:var(--bg); color:var(--txt); font:13px/1.5 ui-monospace,SFMono-Regular,Menlo,monospace; }\n header { padding:10px 16px; border-bottom:1px solid var(--line); display:flex; gap:18px; align-items:center; flex-wrap:wrap; }\n header b { color:#fff; font-weight:600; } .stat { color:var(--dim); } .stat span { color:var(--txt); }\n #wrap { display:flex; height:calc(100vh - 112px); }\n #stage { flex:1; overflow:auto; }\n #side { width:280px; border-left:1px solid var(--line); padding:14px; overflow:auto; background:var(--panel); }\n #side h3 { margin:0 0 8px; font-size:12px; color:var(--dim); text-transform:uppercase; letter-spacing:.08em; }\n #side .k { color:var(--dim); } #side .row { margin:3px 0; }\n svg { display:block; } .node circle { stroke:#0b0e14; stroke-width:2; cursor:pointer; }\n .node text { fill:var(--txt); font-size:11px; } .node .sub { fill:var(--dim); font-size:9px; }\n .edge { stroke:#2a3340; stroke-width:1.5; fill:none; }\n .hidden { opacity:0; } .node, .edge { transition:opacity .25s ease, fill .25s, stroke .25s; }\n footer { padding:10px 16px; border-top:1px solid var(--line); display:flex; gap:14px; align-items:center; }\n button { background:#1b2433; color:var(--txt); border:1px solid var(--line); border-radius:6px; padding:6px 14px; cursor:pointer; font:inherit; }\n button:hover { background:#243047; } input[type=range] { flex:1; accent-color:#3ad17a; }\n select { background:#1b2433; color:var(--txt); border:1px solid var(--line); border-radius:6px; padding:5px; }\n .legend i { display:inline-block; width:9px; height:9px; border-radius:50%; margin:0 4px 0 10px; vertical-align:middle; }\n</style></head><body>\n<header>\n <b>${escapeHtml(title)}</b>\n <span class=\"legend\"><i style=\"background:var(--run)\"></i>running<i style=\"background:var(--done)\"></i>delivered<i style=\"background:var(--down)\"></i>failed</span>\n <span class=\"stat\">t <span id=\"clock\">0.0s</span></span>\n <span class=\"stat\">nodes <span id=\"cNodes\">0</span></span>\n <span class=\"stat\">delivered <span id=\"cDeliv\">0</span></span>\n <span class=\"stat\">tokens <span id=\"cTok\">0</span></span>\n <span class=\"stat\">$<span id=\"cUsd\">0.000</span></span>\n</header>\n<div id=\"wrap\"><div id=\"stage\"><svg id=\"svg\"></svg></div>\n<div id=\"side\"><h3>node</h3><div id=\"detail\" class=\"stat\">hover a node…</div></div></div>\n<footer>\n <button id=\"play\">▶ play</button>\n <button id=\"restart\">⟲</button>\n <input type=\"range\" id=\"scrub\" min=\"0\" max=\"1000\" value=\"0\"/>\n <select id=\"speed\"><option value=\"1\">1×</option><option value=\"2\">2×</option><option value=\"4\" selected>4×</option><option value=\"8\">8×</option><option value=\"20\">20×</option></select>\n</footer>\n<script>\nconst TL = ${data};\nconst EV = TL.events.slice().sort((a,b)=>a.t-b.t);\nconst span = Math.max(1, TL.t1 - TL.t0);\n// ── build the full tree (nodes known from spawn/root events) ──\nconst nodes = new Map();\nfor (const e of EV) {\n if (e.kind==='root' || e.kind==='spawn') {\n if (!nodes.has(e.id)) nodes.set(e.id, { id:e.id, parentId:e.parentId, label:e.label||e.id, runtime:e.runtime, depth:e.depth??0, tSpawn:e.t, children:[] });\n }\n}\nfor (const n of nodes.values()) { if (n.parentId && nodes.has(n.parentId)) nodes.get(n.parentId).children.push(n); }\nconst roots = [...nodes.values()].filter(n=>!n.parentId || !nodes.has(n.parentId));\n// ── tidy layout: post-order leaf slotting; x=avg(children), y=depth ──\nlet leaf=0; const VGAP=78, HGAP=120;\nfunction layout(n, d){ n.y = 40 + d*VGAP; if(!n.children.length){ n.x = 40 + (leaf++)*HGAP; } else { n.children.forEach(c=>layout(c,d+1)); n.x = (n.children[0].x + n.children[n.children.length-1].x)/2; } }\nroots.forEach(r=>layout(r,0));\nconst W = Math.max(900, 80 + leaf*HGAP), H = 80 + (Math.max(0,...[...nodes.values()].map(n=>n.depth||0))+1)*VGAP;\nconst svg = document.getElementById('svg'); svg.setAttribute('viewBox','0 0 '+W+' '+H); svg.setAttribute('width',W); svg.setAttribute('height',H);\nconst NS='http://www.w3.org/2000/svg';\nfunction el(t,a){ const x=document.createElementNS(NS,t); for(const k in a) x.setAttribute(k,a[k]); return x; }\n// edges\nconst edgeEls=new Map();\nfor (const n of nodes.values()) if (n.parentId && nodes.has(n.parentId)) { const p=nodes.get(n.parentId);\n const path=el('path',{class:'edge hidden', d:'M'+p.x+','+(p.y)+' C'+p.x+','+((p.y+n.y)/2)+' '+n.x+','+((p.y+n.y)/2)+' '+n.x+','+n.y}); svg.appendChild(path); edgeEls.set(n.id,path); }\n// nodes\nconst nodeEls=new Map();\nfor (const n of nodes.values()) { const g=el('g',{class:'node hidden'});\n const c=el('circle',{cx:n.x,cy:n.y,r:14,fill:'var(--dim)'});\n const t=el('text',{x:n.x,y:n.y-20,'text-anchor':'middle'}); t.textContent=n.label.slice(0,18);\n const s=el('text',{class:'sub',x:n.x,y:n.y+26,'text-anchor':'middle'}); s.textContent=n.runtime||'';\n g.appendChild(c); g.appendChild(t); g.appendChild(s); svg.appendChild(g); nodeEls.set(n.id,{g,c,s});\n g.addEventListener('mouseenter',()=>showDetail(n)); }\nfunction showDetail(n){ const st=state.get(n.id)||{}; document.getElementById('detail').innerHTML =\n '<div class=\"row\"><span class=\"k\">id</span> '+n.id+'</div>'+\n '<div class=\"row\"><span class=\"k\">label</span> '+esc(n.label)+'</div>'+\n '<div class=\"row\"><span class=\"k\">runtime</span> '+(n.runtime||'—')+'</div>'+\n '<div class=\"row\"><span class=\"k\">depth</span> '+(n.depth??0)+'</div>'+\n '<div class=\"row\"><span class=\"k\">status</span> '+(st.status||'—')+(st.valid===false?' (not delivered)':st.valid?' (delivered)':'')+'</div>'+\n '<div class=\"row\"><span class=\"k\">score</span> '+(st.score!=null?st.score.toFixed(2):'—')+'</div>'+\n (st.reason?'<div class=\"row\"><span class=\"k\">reason</span> '+esc(st.reason)+'</div>':'')+\n '<div class=\"row\"><span class=\"k\">tokens</span> '+(st.tokens||0)+'</div>'; }\nfunction esc(x){ return String(x).replace(/[<>&]/g,c=>({'<':'&lt;','>':'&gt;','&':'&amp;'}[c])); }\n// ── playback ──\nconst state=new Map();\nfunction colorOf(st){ if(!st||st.status==='running'||!st.status) return 'var(--run)'; if(st.status==='down') return 'var(--down)'; return st.valid===false ? 'var(--undeliv)' : 'var(--done)'; }\nfunction applyTo(ms){ const clk=TL.t0+ms; state.clear();\n let deliv=0, tok=0, usd=0, shown=0;\n // reset visibility\n for (const [,v] of nodeEls){ v.g.classList.add('hidden'); }\n for (const [,p] of edgeEls){ p.classList.add('hidden'); }\n for (const e of EV){ if (e.t>clk) break;\n if (e.kind==='spawn'||e.kind==='root'){ const ne=nodeEls.get(e.id); if(ne){ ne.g.classList.remove('hidden'); shown++; const ed=edgeEls.get(e.id); if(ed) ed.classList.remove('hidden'); } state.set(e.id, Object.assign(state.get(e.id)||{}, {status:'running'})); }\n else if (e.kind==='settle'){ const st=Object.assign(state.get(e.id)||{}, {status:e.status, valid:e.valid, score:e.score, reason:e.reason, tokens:e.tokens, usd:e.usd}); state.set(e.id,st); }\n }\n for (const [id,st] of state){ const ne=nodeEls.get(id); if(ne) ne.c.setAttribute('fill', colorOf(st)); if(st.status&&st.status!=='running'){ if(st.status==='done'&&st.valid!==false) deliv++; tok+=st.tokens||0; usd+=st.usd||0; } }\n document.getElementById('clock').textContent=(ms/1000).toFixed(1)+'s';\n document.getElementById('cNodes').textContent=shown;\n document.getElementById('cDeliv').textContent=deliv;\n document.getElementById('cTok').textContent=tok;\n document.getElementById('cUsd').textContent=usd.toFixed(3);\n}\nconst scrub=document.getElementById('scrub'); let playing=false, raf=0, last=0;\nfunction setMs(ms){ ms=Math.max(0,Math.min(span,ms)); scrub.value=(ms/span*1000)|0; applyTo(ms); }\nscrub.addEventListener('input',()=>setMs(scrub.value/1000*span));\ndocument.getElementById('play').addEventListener('click',()=>{ playing=!playing; document.getElementById('play').textContent=playing?'⏸ pause':'▶ play'; if(playing){ last=performance.now(); if((scrub.value/1000*span)>=span) setMs(0); tick(); } });\ndocument.getElementById('restart').addEventListener('click',()=>{ setMs(0); });\nfunction tick(){ if(!playing) return; const now=performance.now(); const sp=+document.getElementById('speed').value; let ms=scrub.value/1000*span + (now-last)*sp; last=now; if(ms>=span){ ms=span; playing=false; document.getElementById('play').textContent='▶ play'; } setMs(ms); if(playing) raf=requestAnimationFrame(tick); }\nsetMs(0);\n</script></body></html>`\n}\n\n/** A readable label for a synthesized root node (the last path segment of a nested tree key). */\nfunction shortRoot(id: string): string {\n const seg = id.split('/').pop() ?? id\n return seg.length > 22 ? `…${seg.slice(-21)}` : seg\n}\n\nfunction escapeHtml(s: string): string {\n return s.replace(\n /[<>&\"]/g,\n (c) => ({ '<': '&lt;', '>': '&gt;', '&': '&amp;', '\"': '&quot;' })[c] ?? c,\n )\n}\n","/**\n * @experimental\n *\n * TOPOLOGY VIEW — the live recursive agent tree, folded from the ONE lifecycle stream\n * (`src/runtime-hooks.ts`). It is a pure projection: feed it the `RuntimeHookEvent`s that\n * `runLoop`, `toolLoop`, and the keystone `Scope` already emit, and it maintains the tree of\n * agents + renders it. Attach `view.hooks` to a `Supervisor`/`runLoop` and the tree updates live.\n *\n * Two node sources, ONE stream:\n * - an agent node is born from `agent.spawn` (id = `childId`, parent = the spawner) or the root\n * `agent.run` (id = `runId`); `agent.child`/`agent.run:after` settle it (status + score/reason).\n * - a STEP (`agent.{turn,tool_call,plan,decision}`) is not a node — it advances the step count of\n * the agent it belongs to (matched by `runId`/`parentId`).\n *\n * No I/O, no timers, no backend coupling — the same projection drives a CLI render, a TUI, or a\n * web tree. Rendering is deterministic given the event order (the stream is the source of truth).\n */\n\nimport type { RuntimeHookEvent, RuntimeHooks } from '../runtime-hooks'\n\nexport type TopologyStatus = 'running' | 'done' | 'down'\n\n/** One agent in the tree. A leaf never spawns; a driver's `childIds` is non-empty. */\nexport interface TopologyNode {\n readonly id: string\n /** Display label (spawn `label`, or the driver name on the root). */\n label: string\n /** Leaf runtime (`router`/`sandbox`/`cli`) when known. */\n runtime?: string\n /** Parent agent id; undefined ⇒ a root. */\n parentId?: string\n /** Recursion depth (root = 0). */\n depth: number\n status: TopologyStatus\n /** Count of in-agent steps (turns + tool calls + plan/decision rounds) folded so far. */\n steps: number\n /** Deployable score in [0,1] once settled `done`. */\n score?: number\n /** Failure reason once settled `down`. */\n reason?: string\n /** Children in spawn order. */\n readonly childIds: string[]\n}\n\nexport interface RenderOptions {\n /** Cap the rendered depth (deeper nodes collapse to a `… N more` line). Default: no cap. */\n readonly maxDepth?: number\n /** Drop the per-node detail suffix (steps/children/score) — labels only. Default: false. */\n readonly compact?: boolean\n}\n\nexport interface TopologyView {\n /** The `RuntimeHooks` sink — attach to `SupervisorOpts.hooks` / `runLoop` options. */\n readonly hooks: RuntimeHooks\n /** Fold one event into the tree (the same call `hooks.onEvent` makes — exposed for replay). */\n ingest(event: RuntimeHookEvent): void\n /** Every node, insertion order. */\n nodes(): TopologyNode[]\n /** Nodes with no in-tree parent (the run roots). */\n roots(): TopologyNode[]\n /** One node by id. */\n node(id: string): TopologyNode | undefined\n /** Render the tree as an aligned ASCII forest. */\n render(opts?: RenderOptions): string\n}\n\nconst stepTargets = new Set(['agent.turn', 'agent.tool_call', 'agent.plan', 'agent.decision'])\n\n/** Build a live topology view. Stateful — one per run (or per replay). */\nexport function createTopologyView(): TopologyView {\n // Insertion-ordered so render is stable and roots() reflects spawn order.\n const byId = new Map<string, TopologyNode>()\n\n const ensure = (id: string, seed: Partial<TopologyNode> = {}): TopologyNode => {\n const existing = byId.get(id)\n if (existing) return existing\n const node: TopologyNode = {\n id,\n label: seed.label ?? id,\n depth: seed.depth ?? 0,\n status: 'running',\n steps: 0,\n childIds: [],\n ...seed,\n }\n byId.set(id, node)\n return node\n }\n\n const str = (v: unknown): string | undefined => (typeof v === 'string' ? v : undefined)\n const num = (v: unknown): number | undefined => (typeof v === 'number' ? v : undefined)\n\n const ingest = (event: RuntimeHookEvent): void => {\n const p = (event.payload ?? {}) as Record<string, unknown>\n\n if (event.target === 'agent.spawn' && event.phase === 'after') {\n const id = str(p.childId)\n if (!id) return\n const parent = event.parentId ? ensure(event.parentId) : undefined\n const node = ensure(id, {\n label: str(p.label) ?? id,\n runtime: str(p.runtime),\n parentId: event.parentId,\n depth: num(p.depth) ?? (parent ? parent.depth + 1 : 0),\n })\n if (parent && !parent.childIds.includes(id)) parent.childIds.push(id)\n node.status = 'running'\n return\n }\n\n if (event.target === 'agent.child' && event.phase === 'after') {\n const id = str(p.childId)\n if (!id) return\n const node = ensure(id, { parentId: event.parentId })\n node.status = str(p.status) === 'down' ? 'down' : 'done'\n node.score = num(p.score)\n node.reason = str(p.reason)\n return\n }\n\n if (event.target === 'agent.run') {\n const node = ensure(event.runId, {\n label: str(p.driver) ?? event.runId,\n parentId: event.parentId,\n })\n if (event.phase === 'before') node.status = 'running'\n else if (event.phase === 'error') node.status = 'down'\n else if (event.phase === 'after' && node.status === 'running') node.status = 'done'\n return\n }\n\n // A step advances the agent it belongs to: tool-loop/run-loop key the agent by `runId`,\n // a within-agent sub-event names it via `parentId`. Only count `after`/`event` phases so a\n // before/after pair is one step, not two. An unknown owner is dropped (no phantom node).\n if (stepTargets.has(event.target) && (event.phase === 'after' || event.phase === 'event')) {\n const owner =\n (event.parentId && byId.get(event.parentId)) || byId.get(event.runId) || undefined\n if (owner) owner.steps += 1\n }\n }\n\n const nodes = (): TopologyNode[] => [...byId.values()]\n const roots = (): TopologyNode[] =>\n nodes().filter((n) => n.parentId === undefined || !byId.has(n.parentId))\n\n return {\n hooks: { onEvent: (e) => ingest(e) },\n ingest,\n nodes,\n roots,\n node: (id) => byId.get(id),\n render: (opts) => renderTopologyTree({ roots: roots(), node: (id) => byId.get(id) }, opts),\n }\n}\n\nconst glyph: Record<TopologyStatus, string> = { running: '◐', done: '✓', down: '✗' }\n\n/** Render a forest of `TopologyNode`s to an aligned ASCII tree. Pure — given the same roots +\n * node lookup it returns the same string. Exposed so a caller can render a tree it folded\n * itself (e.g. from a journal replay) without the live view. */\nexport function renderTopologyTree(\n tree: { roots: TopologyNode[]; node: (id: string) => TopologyNode | undefined },\n opts: RenderOptions = {},\n): string {\n const lines: string[] = []\n\n const detail = (n: TopologyNode): string => {\n if (opts.compact) return ''\n const parts: string[] = []\n if (n.runtime) parts.push(n.runtime)\n if (n.steps > 0) parts.push(`${n.steps} ${n.steps === 1 ? 'step' : 'steps'}`)\n if (n.childIds.length > 0) parts.push(`${n.childIds.length} children`)\n if (n.status === 'done' && n.score !== undefined) parts.push(`score ${n.score.toFixed(2)}`)\n if (n.status === 'down' && n.reason) parts.push(`down: ${n.reason}`)\n return parts.length ? ` (${parts.join(' · ')})` : ''\n }\n\n const walk = (n: TopologyNode, prefix: string, isLast: boolean, depth: number): void => {\n const branch = depth === 0 ? '' : isLast ? '└─ ' : '├─ '\n lines.push(`${prefix}${branch}${glyph[n.status]} ${n.label}${detail(n)}`)\n\n if (opts.maxDepth !== undefined && depth >= opts.maxDepth && n.childIds.length > 0) {\n const childPrefix = prefix + (depth === 0 ? '' : isLast ? ' ' : '│ ')\n lines.push(`${childPrefix}└─ … ${n.childIds.length} more`)\n return\n }\n const childPrefix = prefix + (depth === 0 ? '' : isLast ? ' ' : '│ ')\n const kids = n.childIds.map((id) => tree.node(id)).filter((c): c is TopologyNode => !!c)\n kids.forEach((c, i) => {\n walk(c, childPrefix, i === kids.length - 1, depth + 1)\n })\n }\n\n tree.roots.forEach((r, i) => {\n walk(r, '', i === tree.roots.length - 1, 0)\n })\n return lines.join('\\n')\n}\n"],"mappings":"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","names":["childPrefix"]}
@@ -762,8 +762,8 @@ interface Driver<Task, Output, Decision> {
762
762
  * the `loop.plan` event so a topology viewer can render the agent's chosen
763
763
  * move + rationale (not just the inferred fan-width). Drivers whose topology
764
764
  * is a pure function of count (refine/fanout-vote) omit it — the kernel
765
- * infers `moveKind` from the planned-task count. Agent-authored drivers
766
- * (`createDriver`) return their chosen move's kind + rationale.
765
+ * infers `moveKind` from the planned-task count. A driver that authors its
766
+ * own topology returns its chosen move's kind + rationale here.
767
767
  */
768
768
  describePlan?(): LoopPlanDescription | undefined;
769
769
  /**
@@ -857,8 +857,8 @@ interface SandboxClient {
857
857
  * loop end. When the driver's branch point is kernel-inferred (no
858
858
  * `describePlan` — refine, fanout-vote), the kernel prunes boxes no future
859
859
  * round can reach after each round, so the live set tracks the active frontier.
860
- * When the driver authors its own branch point (`describePlan().parentIndex`
861
- * `createDriver`), it may descend from any prior
860
+ * When the driver authors its own branch point (`describePlan().parentIndex`),
861
+ * it may descend from any prior
862
862
  * iteration, so no box is pruned and the live-box count rises to the total
863
863
  * iterations across all rounds. Size `forkFanout` runs accordingly (CRIU forks
864
864
  * are copy-on-write, but each is still a live box until loop end).
@@ -1,6 +1,6 @@
1
1
  import { AgentProfile, CreateSandboxOptions, PromptOptions, TaskOptions, SandboxEvent } from '@tangle-network/sandbox';
2
- import { S as SandboxClient, O as OutputAdapter, f as LoopSandboxPlacement, b as LoopResult } from './types-CklkW4Eh.js';
3
- import { R as RunLoopOptions } from './run-loop-DgVhucoR.js';
2
+ import { S as SandboxClient, O as OutputAdapter, f as LoopSandboxPlacement, b as LoopResult } from './types-C8rNlxfV.js';
3
+ import { R as RunLoopOptions } from './run-loop-DluzfJ2h.js';
4
4
  import '@tangle-network/agent-eval';
5
5
  import './runtime-hooks-C7JwKb9E.js';
6
6
 
package/dist/workflow.js CHANGED
@@ -2,11 +2,13 @@ import {
2
2
  createSandboxForSpec,
3
3
  describeSandboxPlacement,
4
4
  runLoop
5
- } from "./chunk-K6WP7PYW.js";
5
+ } from "./chunk-JTH2FPCK.js";
6
+ import "./chunk-WIR4HOOJ.js";
6
7
  import {
7
8
  ValidationError,
8
9
  extractLlmCallEvent
9
- } from "./chunk-GSUO5QS6.js";
10
+ } from "./chunk-VLF5RHEQ.js";
11
+ import "./chunk-G3RGMA7C.js";
10
12
  import "./chunk-DGUM43GV.js";
11
13
 
12
14
  // src/workflow/agent-delegate.ts