@tangle-network/agent-runtime 0.105.0 → 0.106.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 (218) hide show
  1. package/README.md +2 -2
  2. package/dist/activation-CRjVfOiy.js +203 -0
  3. package/dist/activation-CRjVfOiy.js.map +1 -0
  4. package/dist/activation-DyWB0K6E.d.ts +98 -0
  5. package/dist/agent.d.ts +216 -234
  6. package/dist/agent.js +615 -547
  7. package/dist/agent.js.map +1 -1
  8. package/dist/analyst-loop-DGdtVENg.js +517 -0
  9. package/dist/analyst-loop-DGdtVENg.js.map +1 -0
  10. package/dist/analyst-loop.d.ts +10 -43
  11. package/dist/analyst-loop.js +2 -11
  12. package/dist/backends-Dy8NujB4.js +713 -0
  13. package/dist/backends-Dy8NujB4.js.map +1 -0
  14. package/dist/candidate-execution/index.d.ts +4 -192
  15. package/dist/candidate-execution/index.js +5 -66
  16. package/dist/candidate-execution-DqMbv1rU.js +488 -0
  17. package/dist/candidate-execution-DqMbv1rU.js.map +1 -0
  18. package/dist/coder-yhVWbdWc.js +40 -0
  19. package/dist/coder-yhVWbdWc.js.map +1 -0
  20. package/dist/conversation-XucG_K3l.js +1265 -0
  21. package/dist/conversation-XucG_K3l.js.map +1 -0
  22. package/dist/conversation.d.ts +2 -804
  23. package/dist/conversation.js +2 -55
  24. package/dist/environment-provider-CfLUmKmf.js +1168 -0
  25. package/dist/environment-provider-CfLUmKmf.js.map +1 -0
  26. package/dist/environment-provider-IUGU3epE.d.ts +964 -0
  27. package/dist/environment-provider.d.ts +2 -78
  28. package/dist/environment-provider.js +2 -18
  29. package/dist/errors-DEAvWQPy.js +111 -0
  30. package/dist/errors-DEAvWQPy.js.map +1 -0
  31. package/dist/improvement-adapter-Cr5TBSji.d.ts +166 -0
  32. package/dist/improvement-cycle-C095kS_g.js +2624 -0
  33. package/dist/improvement-cycle-C095kS_g.js.map +1 -0
  34. package/dist/index-BGdxAwfO.d.ts +838 -0
  35. package/dist/index-C-FYUuFG.d.ts +193 -0
  36. package/dist/index-CbNM40xG.d.ts +140 -0
  37. package/dist/index-DruSKs3Z.d.ts +7224 -0
  38. package/dist/index-Efjb3nrQ.d.ts +671 -0
  39. package/dist/index.d.ts +14 -1058
  40. package/dist/index.js +2106 -2082
  41. package/dist/index.js.map +1 -1
  42. package/dist/intelligence.d.ts +768 -889
  43. package/dist/intelligence.js +1466 -1367
  44. package/dist/intelligence.js.map +1 -1
  45. package/dist/{kb-gate-CwHO0vz6.d.ts → kb-gate-C8z2juK8.d.ts} +33 -31
  46. package/dist/kb-gate-DpaSwXVx.js +80 -0
  47. package/dist/kb-gate-DpaSwXVx.js.map +1 -0
  48. package/dist/knowledge-Ku-mPP4f.js +433 -0
  49. package/dist/knowledge-Ku-mPP4f.js.map +1 -0
  50. package/dist/knowledge.d.ts +2 -150
  51. package/dist/knowledge.js +2 -40
  52. package/dist/local-harness-Dh8PJ0ot.d.ts +455 -0
  53. package/dist/loop-runner-bin-BXsFEUcf.d.ts +162 -0
  54. package/dist/loop-runner-bin-BwjQ1NfH.js +268 -0
  55. package/dist/loop-runner-bin-BwjQ1NfH.js.map +1 -0
  56. package/dist/loop-runner-bin.d.ts +2 -15
  57. package/dist/loop-runner-bin.js +2 -24
  58. package/dist/loops.d.ts +5 -4164
  59. package/dist/loops.js +8 -419
  60. package/dist/mcp/bin.d.ts +1 -1
  61. package/dist/mcp/bin.js +116 -146
  62. package/dist/mcp/bin.js.map +1 -1
  63. package/dist/mcp/index.d.ts +295 -436
  64. package/dist/mcp/index.js +1242 -1177
  65. package/dist/mcp/index.js.map +1 -1
  66. package/dist/mcp/memory-bin.d.ts +1 -1
  67. package/dist/mcp/memory-bin.js +37 -16
  68. package/dist/mcp/memory-bin.js.map +1 -1
  69. package/dist/memory-server-DL6cE2Ag.js +234 -0
  70. package/dist/memory-server-DL6cE2Ag.js.map +1 -0
  71. package/dist/model-resolution-Btd9iIKV.js +98 -0
  72. package/dist/model-resolution-Btd9iIKV.js.map +1 -0
  73. package/dist/openai-tools-B68JaOCx.d.ts +24 -0
  74. package/dist/openai-tools-CKLy1C7M.js +44 -0
  75. package/dist/openai-tools-CKLy1C7M.js.map +1 -0
  76. package/dist/otel-export-D34IQW1C.js +843 -0
  77. package/dist/otel-export-D34IQW1C.js.map +1 -0
  78. package/dist/platform.d.ts +188 -185
  79. package/dist/platform.js +185 -218
  80. package/dist/platform.js.map +1 -1
  81. package/dist/prepare-B53sPV7M.js +4292 -0
  82. package/dist/prepare-B53sPV7M.js.map +1 -0
  83. package/dist/primeintellect/index.d.ts +149 -145
  84. package/dist/primeintellect/index.js +626 -1003
  85. package/dist/primeintellect/index.js.map +1 -1
  86. package/dist/profiles.d.ts +296 -490
  87. package/dist/profiles.js +722 -749
  88. package/dist/profiles.js.map +1 -1
  89. package/dist/protected-model-port-BLPH5VVv.js +722 -0
  90. package/dist/protected-model-port-BLPH5VVv.js.map +1 -0
  91. package/dist/protected-model-port-BP6Z4eau.d.ts +283 -0
  92. package/dist/protected-redaction--F3v1oo8.js +200 -0
  93. package/dist/protected-redaction--F3v1oo8.js.map +1 -0
  94. package/dist/redact-D-u-rrcn.js +82 -0
  95. package/dist/redact-D-u-rrcn.js.map +1 -0
  96. package/dist/redact-kbIOjfjM.d.ts +452 -0
  97. package/dist/researcher-CoVqNhfI.js +433 -0
  98. package/dist/researcher-CoVqNhfI.js.map +1 -0
  99. package/dist/runtime-BG0BziaP.js +5408 -0
  100. package/dist/runtime-BG0BziaP.js.map +1 -0
  101. package/dist/sandbox-events-DeI5xX8P.js +316 -0
  102. package/dist/sandbox-events-DeI5xX8P.js.map +1 -0
  103. package/dist/spawn-journal-CwPvKUTa.js +456 -0
  104. package/dist/spawn-journal-CwPvKUTa.js.map +1 -0
  105. package/dist/structural-rollout-IXUEplky.js +1445 -0
  106. package/dist/structural-rollout-IXUEplky.js.map +1 -0
  107. package/dist/substrate-B0TYNrXn.js +27 -0
  108. package/dist/substrate-B0TYNrXn.js.map +1 -0
  109. package/dist/{substrate-Dyiqmd3o.d.ts → substrate-BcnuSHXm.d.ts} +57 -54
  110. package/dist/supervise-BiRutHS9.js +7321 -0
  111. package/dist/supervise-BiRutHS9.js.map +1 -0
  112. package/dist/supervisor-DTKhF-RV.js +3826 -0
  113. package/dist/supervisor-DTKhF-RV.js.map +1 -0
  114. package/dist/testing.d.ts +4 -3
  115. package/dist/testing.js +2478 -2637
  116. package/dist/testing.js.map +1 -1
  117. package/dist/tool-server-RcWgLIsL.js +141 -0
  118. package/dist/tool-server-RcWgLIsL.js.map +1 -0
  119. package/dist/trace-propagation-CJJC7SVB.js +57 -0
  120. package/dist/trace-propagation-CJJC7SVB.js.map +1 -0
  121. package/dist/types-BaIpb0XT.d.ts +492 -0
  122. package/dist/types-BevOjfTY.d.ts +765 -0
  123. package/dist/types-C9j4qg6l.d.ts +500 -0
  124. package/dist/types-Cja5OTGh.d.ts +190 -0
  125. package/dist/util-MVgdwuIS.js +145 -0
  126. package/dist/util-MVgdwuIS.js.map +1 -0
  127. package/dist/workspace-archive-BosQiotA.js +575 -0
  128. package/dist/workspace-archive-BosQiotA.js.map +1 -0
  129. package/package.json +43 -56
  130. package/skills/build-with-agent-runtime/SKILL.md +1 -0
  131. package/dist/activation-Cm-9MMX1.d.ts +0 -69
  132. package/dist/analyst-loop.js.map +0 -1
  133. package/dist/backends-BO4Jqoe2.d.ts +0 -134
  134. package/dist/candidate-execution/index.js.map +0 -1
  135. package/dist/chunk-2KGAN2HM.js +0 -1256
  136. package/dist/chunk-2KGAN2HM.js.map +0 -1
  137. package/dist/chunk-2VZYRB55.js +0 -834
  138. package/dist/chunk-2VZYRB55.js.map +0 -1
  139. package/dist/chunk-3MDZX7YU.js +0 -125
  140. package/dist/chunk-3MDZX7YU.js.map +0 -1
  141. package/dist/chunk-5IBQAPVB.js +0 -29
  142. package/dist/chunk-5IBQAPVB.js.map +0 -1
  143. package/dist/chunk-5QOB7H74.js +0 -387
  144. package/dist/chunk-5QOB7H74.js.map +0 -1
  145. package/dist/chunk-C5RTIBNZ.js +0 -48
  146. package/dist/chunk-C5RTIBNZ.js.map +0 -1
  147. package/dist/chunk-CTRA64LY.js +0 -680
  148. package/dist/chunk-CTRA64LY.js.map +0 -1
  149. package/dist/chunk-DPEUKJRO.js +0 -27
  150. package/dist/chunk-DPEUKJRO.js.map +0 -1
  151. package/dist/chunk-FD2MBMOH.js +0 -494
  152. package/dist/chunk-FD2MBMOH.js.map +0 -1
  153. package/dist/chunk-H5QPIZNX.js +0 -907
  154. package/dist/chunk-H5QPIZNX.js.map +0 -1
  155. package/dist/chunk-HLKC4UYB.js +0 -3514
  156. package/dist/chunk-HLKC4UYB.js.map +0 -1
  157. package/dist/chunk-HNP72PNU.js +0 -192
  158. package/dist/chunk-HNP72PNU.js.map +0 -1
  159. package/dist/chunk-ISPWRSEC.js +0 -408
  160. package/dist/chunk-ISPWRSEC.js.map +0 -1
  161. package/dist/chunk-KRBFHMV6.js +0 -1228
  162. package/dist/chunk-KRBFHMV6.js.map +0 -1
  163. package/dist/chunk-M22PL4GH.js +0 -171
  164. package/dist/chunk-M22PL4GH.js.map +0 -1
  165. package/dist/chunk-NURIQBJQ.js +0 -72
  166. package/dist/chunk-NURIQBJQ.js.map +0 -1
  167. package/dist/chunk-OPVWXJ2H.js +0 -75
  168. package/dist/chunk-OPVWXJ2H.js.map +0 -1
  169. package/dist/chunk-PZZKQVQV.js +0 -52
  170. package/dist/chunk-PZZKQVQV.js.map +0 -1
  171. package/dist/chunk-Q2JSAVQ3.js +0 -671
  172. package/dist/chunk-Q2JSAVQ3.js.map +0 -1
  173. package/dist/chunk-RDOAVVHY.js +0 -699
  174. package/dist/chunk-RDOAVVHY.js.map +0 -1
  175. package/dist/chunk-SGKPNBXE.js +0 -60
  176. package/dist/chunk-SGKPNBXE.js.map +0 -1
  177. package/dist/chunk-SMQXZGLZ.js +0 -351
  178. package/dist/chunk-SMQXZGLZ.js.map +0 -1
  179. package/dist/chunk-UHEZW5BU.js +0 -2918
  180. package/dist/chunk-UHEZW5BU.js.map +0 -1
  181. package/dist/chunk-UPWGXKXB.js +0 -254
  182. package/dist/chunk-UPWGXKXB.js.map +0 -1
  183. package/dist/chunk-VISA6CI3.js +0 -200
  184. package/dist/chunk-VISA6CI3.js.map +0 -1
  185. package/dist/chunk-WMTCUOQL.js +0 -474
  186. package/dist/chunk-WMTCUOQL.js.map +0 -1
  187. package/dist/chunk-WRTOVNN4.js +0 -4728
  188. package/dist/chunk-WRTOVNN4.js.map +0 -1
  189. package/dist/chunk-WSTRQZYQ.js +0 -5576
  190. package/dist/chunk-WSTRQZYQ.js.map +0 -1
  191. package/dist/chunk-XBG2W2VW.js +0 -6434
  192. package/dist/chunk-XBG2W2VW.js.map +0 -1
  193. package/dist/chunk-YEJR7IXO.js +0 -68
  194. package/dist/chunk-YEJR7IXO.js.map +0 -1
  195. package/dist/chunk-YJZA2BIK.js +0 -1338
  196. package/dist/chunk-YJZA2BIK.js.map +0 -1
  197. package/dist/completion-gate-BMy5LGoP.d.ts +0 -256
  198. package/dist/conversation.js.map +0 -1
  199. package/dist/coordination-BZZSVYpZ.d.ts +0 -2570
  200. package/dist/environment-provider.js.map +0 -1
  201. package/dist/improvement-adapter-HAZz-7vK.d.ts +0 -203
  202. package/dist/knowledge.js.map +0 -1
  203. package/dist/local-harness-BDNcl6jI.d.ts +0 -175
  204. package/dist/loop-runner-bin-68aoM9-7.d.ts +0 -199
  205. package/dist/loop-runner-bin.js.map +0 -1
  206. package/dist/loops.js.map +0 -1
  207. package/dist/openai-tools-fnj6SRVg.d.ts +0 -46
  208. package/dist/protected-model-port-B4t-OQjL.d.ts +0 -271
  209. package/dist/redact-BuFjxBUJ.d.ts +0 -481
  210. package/dist/sanitize-DEbPNtyI.d.ts +0 -328
  211. package/dist/structural-rollout-DEh6CEsa.d.ts +0 -446
  212. package/dist/supervise-4_48wwvy.d.ts +0 -379
  213. package/dist/types-BCoemcXU.d.ts +0 -802
  214. package/dist/types-BwoZWq-i.d.ts +0 -510
  215. package/dist/types-Cbp72qyu.d.ts +0 -491
  216. package/dist/types-CmYCMbFT.d.ts +0 -203
  217. package/dist/types-CvNwMzQt.d.ts +0 -957
  218. package/dist/worktree-fanout-DxhAWr5Z.d.ts +0 -999
@@ -1,2570 +0,0 @@
1
- import { R as Runtime, E as ExecutorFactory, U as UsageEvent, k as ExecutorProgress, T as TraceSource, g as Spend, i as ExecutorRegistry, A as Agent, S as Scope, b as ResultBlobStore, B as Budget } from './types-CvNwMzQt.js';
2
- import { StreamingDetector, DetectorSignal, ToolSpan, AgentEvalError } from '@tangle-network/agent-eval';
3
- import { AgentProfile as AgentProfile$1 } from '@tangle-network/agent-interface';
4
- import { a as UiLens, U as UiFinding, C as CoderTask } from './substrate-Dyiqmd3o.js';
5
- import { a as SandboxClient, E as ExecCtx, t as LoopTraceEmitter, c as LoopTraceEvent, A as AgentRunSpec } from './types-BCoemcXU.js';
6
- import { AgentProfile, BackendType, SandboxEvent, SandboxInstance } from '@tangle-network/sandbox';
7
- import { b as ToolSpec, c as RuntimeTelemetryOptions, R as RouterConfig } from './sanitize-DEbPNtyI.js';
8
- import { R as RuntimeStreamEvent } from './types-BwoZWq-i.js';
9
- import { G as GitRunner, a as WorktreeCheckRunner, D as DeliverableSpec } from './completion-gate-BMy5LGoP.js';
10
- import { L as LocalHarness } from './local-harness-BDNcl6jI.js';
11
- import { ProviderExecutorOptions, AgentEnvironmentProviderRegistry } from './environment-provider.js';
12
- import { AgentEnvironmentProvider } from '@tangle-network/agent-interface/environment-provider';
13
-
14
- /**
15
- *
16
- * `piExecutor` — pi wrapped behind `Executor`, NOT forked.
17
- *
18
- * pi already implements everything a steerable worker needs, and implements it well: a queued
19
- * steering channel delivered between turns, follow-ups, abort, compaction, session persistence
20
- * with fork/branch, and an out-of-process line-delimited JSON protocol over stdin/stdout
21
- * (`pi --mode rpc`). Reimplementing any of that here would mean owning a turn loop someone else
22
- * already maintains. So this module is a thin protocol adapter, and every capability maps onto a
23
- * verb pi already has:
24
- *
25
- * `execute` → `prompt`, draining pi's event stream until `agent_end`
26
- * `deliver` → `prompt` with `streamingBehavior` — pi owns the queue, we do not
27
- * `teardown` → `abort`, then close stdin and reap the process
28
- * `progress` → pi's `tool_execution_start`/`_end` + `turn_end` events, plus `get_state`'s
29
- * `pendingMessageCount` mirrored locally so the read stays synchronous
30
- * `traceSource` → the same tool events decoded into the shared `ToolSpan` currency
31
- * `resultArtifact` → the last assistant text collected off the stream
32
- *
33
- * It is registered through the DOCUMENTED extension point (`ExecutorRegistry.register('pi', …)`),
34
- * so nothing in the resolver switches on it and a consumer can replace it wholesale.
35
- *
36
- * agent-runtime does NOT take a dependency on pi. The wire shapes (`RpcCommand`, `AgentEvent`)
37
- * are read structurally off JSON lines, so a pi that adds commands stays compatible and a pi that
38
- * is not installed simply fails loud at spawn instead of at import.
39
- *
40
- * Usage accounting: pi reports token usage on its assistant messages when the provider supplies
41
- * it. Nothing is fabricated — a turn whose usage pi does not report contributes an `iteration`
42
- * event and zero tokens, exactly like the other honest executors.
43
- *
44
- * @experimental
45
- */
46
-
47
- /** The runtime name `piExecutor` registers under. */
48
- declare const PI_RUNTIME: Runtime;
49
- /** Seam key the registry threads a `PiSeam` through (`ExecutorContext.seams['pi']`). */
50
- declare const piSeamKey = "pi";
51
- /** How to launch pi in its out-of-process RPC mode, and how long to wait on it. */
52
- interface PiSeam {
53
- /** The pi executable (default `'pi'`). Anything on PATH or an absolute path. */
54
- bin?: string;
55
- /** Extra args appended after `--mode rpc`. `--provider` / `--model` are added from `model`. */
56
- args?: ReadonlyArray<string>;
57
- /** `provider/model` or just `model` — split on the first `/` into pi's two flags. */
58
- model?: string;
59
- cwd?: string;
60
- env?: Record<string, string>;
61
- /** Wall-clock ceiling for one `prompt` (the wait for `agent_end`). Omit = no timeout. */
62
- turnTimeoutMs?: number;
63
- /** Newest-last activity window `progress()` reports. Default 12. */
64
- activityWindow?: number;
65
- }
66
- /** Build the `Executor` for one pi worker. Registered as runtime `'pi'`. */
67
- declare const piExecutor: ExecutorFactory<unknown>;
68
-
69
- /**
70
- *
71
- * The worker-side receive end of the down-leg: a per-worker inbox an executor exposes as
72
- * `Executor.deliver`. The driver's `steer_agent` / `answer_question` land here,
73
- * and the worker's agent loop drains them at two points (Drew's two delivery modes):
74
- *
75
- * - QUEUED (default): the message accumulates and is FLUSHED at the next step boundary — folded
76
- * into the conversation before the next think. A worker is also forced to flush BEFORE it may
77
- * settle, so it can never finish while a steer/answer it never read is still pending.
78
- * - FORCEFUL (`interrupt: true`): trips `freshInterrupt()`'s signal so the loop can abort its
79
- * in-flight turn immediately, then re-plan with the message folded in — breaking the worker out
80
- * of a wrong path mid-task instead of waiting for it to finish the step.
81
- *
82
- * `deliver` never throws — a malformed message is ignored, per the `Executor.deliver` contract.
83
- *
84
- * @experimental
85
- */
86
- interface InboxMessage {
87
- readonly kind: 'steer' | 'answer';
88
- readonly text: string;
89
- /** Forceful messages abort the in-flight turn; queued ones wait for the boundary flush. */
90
- readonly interrupt: boolean;
91
- /** Present for an `answer` — the question id it resolves. */
92
- readonly questionId?: string;
93
- }
94
- interface Inbox {
95
- /** The `Executor.deliver` implementation — accept a raw down-message from `Scope.send`. */
96
- deliver(msg: unknown): void;
97
- /** Remove and return all pending messages (the flush). */
98
- drain(): InboxMessage[];
99
- pending(): number;
100
- /** Open a fresh per-turn interrupt signal; a later forceful `deliver` aborts it. The loop links
101
- * this into the signal it passes to its inference call, then re-plans when it fires. */
102
- freshInterrupt(): AbortSignal;
103
- /** Render drained messages as ONE operator turn to fold into the worker's conversation. */
104
- fold(messages: ReadonlyArray<InboxMessage>): string;
105
- }
106
- /** Create the worker-side inbox for the down-leg: the driver's `steer_agent` / `answer_question` messages queue here and the worker's loop drains them at step boundaries and before settle. */
107
- declare function createInbox(): Inbox;
108
-
109
- /**
110
- *
111
- * The STEERABLE sandbox worker: one box, one server-side session, MANY turns — so a message
112
- * from the driver has a boundary to be folded into.
113
- *
114
- * The default cloud worker was built on `singleShotDriver`: one `runAgentRounds` shot, no turn
115
- * boundary, and no `Executor.deliver`. `Scope.send` therefore returned `false` for it and
116
- * `steer_agent` reported `delivered:false` on every call — steering the DEFAULT worker was
117
- * mechanically impossible, not merely unused. This module is the missing continuation loop.
118
- *
119
- * Structure deliberately mirrors `streamBridgeSession` (the cli-bridge equivalent that already
120
- * works) so both steerable runtimes have ONE shape:
121
- *
122
- * - turn 0 sends the task through `SandboxLineage.start` (fresh box, minted session id);
123
- * - each later turn fires ONLY when the inbox has a steer/answer to fold, and goes through
124
- * `SandboxLineage.continue` — the SAME box and the SAME server-side session, so the prompt
125
- * carries only the new instruction and the worker keeps everything it had already learned;
126
- * - a worker may not settle while a delivered steer is unread: the loop drains once more
127
- * before breaking, which is what makes `steer_agent` a promise rather than a hint;
128
- * - a forceful (`interrupt:true`) steer aborts the in-flight turn so the worker re-plans
129
- * immediately instead of finishing a path the supervisor already rejected.
130
- *
131
- * It also produces the LIVE signal a supervisor steers FROM: every sandbox event stamps
132
- * activity, tool parts are decoded into the shared `ToolSpan` currency for the online detector
133
- * panel, and `progress()` answers turn / tool / idle / pending-steer questions synchronously.
134
- *
135
- * Reports REAL usage only — token and cost numbers come from the sandbox events themselves.
136
- *
137
- * @experimental
138
- */
139
-
140
- /** Ceiling on continuation turns. Turn 0 is the task; every later turn is a folded steer, so
141
- * this bounds how many times a supervisor may redirect ONE worker before it must respawn. */
142
- declare const DEFAULT_SANDBOX_STEERING_MAX_TURNS = 24;
143
- /** Opt-in configuration for the steerable sandbox worker (`SandboxSeam.steering`). Absent, the
144
- * sandbox executor keeps its historical single-shot `runAgentRounds` composition verbatim. */
145
- interface SandboxSteeringOptions {
146
- /** Max turns for one worker (turn 0 + folded steers). Default {@link DEFAULT_SANDBOX_STEERING_MAX_TURNS}. */
147
- readonly maxTurns?: number;
148
- /** How many recent tool/turn notes `progress()` reports. Default 12. */
149
- readonly activityWindow?: number;
150
- /** Per-turn wall-clock ceiling; the turn's stream is aborted when it elapses. */
151
- readonly turnTimeoutMs?: number;
152
- }
153
- /** What the steerable session exposes to its executor: the usage stream plus the live reads. */
154
- interface SteerableSandboxSession {
155
- /** Drive the worker to settlement. `signal` is the spawn-scoped abort handed to `execute`. */
156
- stream(task: unknown, signal: AbortSignal): AsyncIterable<UsageEvent>;
157
- progress(): ExecutorProgress;
158
- traceSource(): TraceSource;
159
- artifact(): {
160
- outRef: string;
161
- out: unknown;
162
- spent: Spend;
163
- } | undefined;
164
- teardown(): Promise<void>;
165
- }
166
- interface SteerableSandboxArgs {
167
- readonly controller: AbortController;
168
- readonly profile: AgentProfile;
169
- readonly harness: BackendType;
170
- readonly sandboxClient: SandboxClient;
171
- readonly inbox: Inbox;
172
- readonly taskToPrompt: (task: unknown) => string;
173
- readonly options?: SandboxSteeringOptions;
174
- readonly loopCtx?: Partial<Omit<ExecCtx, 'sandboxClient' | 'signal'>>;
175
- readonly contentRef: (prefix: string, value: unknown) => string;
176
- readonly now?: () => number;
177
- }
178
- /** One steerable sandbox worker. The returned session is inert until `stream()` is drained. */
179
- declare function createSteerableSandboxSession(args: SteerableSandboxArgs): SteerableSandboxSession;
180
-
181
- /**
182
- *
183
- * The leaf runtime — the built-in `Executor` IMPLEMENTATIONS behind the ONE
184
- * open interface frozen in `./types`, plus the open resolver/registry that maps
185
- * an `AgentSpec` to one of them OR accepts a bring-your-own executor verbatim.
186
- *
187
- * The interface is the extension point, not a closed `inline|sandbox|cli` union:
188
- * - router/inline : a direct OpenAI-compatible Router call, no box (one-shot).
189
- * - sandbox : COMPOSES the existing `runAgentRounds` kernel as a single-task
190
- * leaf and surfaces its token/cost usage as `UsageEvent`s;
191
- * forwards PR #150's optional `lineage` passthrough WITHOUT
192
- * reinventing checkpoint/fork (streaming).
193
- * - cli : a Halo/RLM subprocess; `budgetExempt` (no token accounting),
194
- * excluded from the equal-k arms by construction (streaming).
195
- * Every metered runtime reports through the SAME normalized `UsageEvent` channel
196
- * so the conserved budget pool meters them identically. A user's own agent is
197
- * first-class the moment it implements `Executor` — register it by name or
198
- * pass it as `AgentSpec.executor`.
199
- *
200
- * Layering: `estimateCost`/`isModelPriced` are substrate primitives from
201
- * `@tangle-network/agent-eval`; `runAgentRounds`/`acquireSandbox` are runtime kernels
202
- * from this package. No per-vendor adapters live here.
203
- *
204
- * @experimental
205
- */
206
-
207
- /**
208
- * Router/inline connection seam. A direct OpenAI-compatible Router endpoint —
209
- * the cheapest leaf, no box, no tools. `model` overrides the profile's model
210
- * hint when present; otherwise the profile's `model.default` is required.
211
- */
212
- interface RouterSeam {
213
- routerBaseUrl: string;
214
- routerKey: string;
215
- model?: string;
216
- }
217
- /**
218
- * Sandbox executor seam. The `sandboxClient` the composed `runAgentRounds` creates
219
- * boxes through, plus the optional trace/run/lineage wiring forwarded into the
220
- * loop. `lineage` is opaque here (PR #150's `RunAgentRoundsOptions.lineage`): forwarded
221
- * forward-compatibly, never inspected — this executor does NOT reinvent
222
- * checkpoint/fork.
223
- */
224
- interface SandboxSeam {
225
- sandboxClient: SandboxClient;
226
- /** Forwarded into the composed `runAgentRounds`'s `ctx` (trace emitter, run handle, etc.). */
227
- loopCtx?: Partial<Omit<ExecCtx, 'sandboxClient' | 'signal'>>;
228
- /** PR #150 `RunAgentRoundsOptions.lineage` passthrough — opaque; forwarded, not parsed. */
229
- lineage?: unknown;
230
- /** Hard cap on the composed loop's iterations. The budget pool reserves against
231
- * the spawn `Budget.maxIterations`; this is the leaf's own ceiling. Default 1. */
232
- maxIterations?: number;
233
- /**
234
- * OPT-IN: run this worker as a multi-turn, STEERABLE session instead of the historical
235
- * single-shot `runAgentRounds` composition. Setting it gives the sandbox worker an `Executor.deliver`
236
- * inbox (so `Scope.send` / `steer_agent` actually reach it), a live tool-activity trace, and a
237
- * `progress()` read — turning the default cloud worker from something a supervisor can only
238
- * wait on into something it can watch and correct.
239
- *
240
- * Absent, nothing changes: the same `runAgentRounds` leaf, no inbox, `steer_agent` still reports
241
- * `delivered:false`. Opt-in because a steerable worker holds ONE box across several turns,
242
- * which is a different resource profile from a fire-and-forget shot.
243
- */
244
- steering?: SandboxSteeringOptions;
245
- }
246
- /** CLI subprocess seam. `bin` + `args` describe the Halo/RLM process to spawn. */
247
- interface CliSeam {
248
- bin: string;
249
- args?: string[];
250
- /** Extra environment for the subprocess (merged over `process.env`). */
251
- env?: Record<string, string>;
252
- /** Working directory for the subprocess. */
253
- cwd?: string;
254
- }
255
- /**
256
- * cli-worktree seam. A supervisor-authored `AgentProfile` driving a local coding-harness CLI
257
- * (claude / codex / opencode) on its own git worktree — the leaf `createWorktreeCliExecutor`
258
- * named as data. `harness` + `repoRoot` + `taskPrompt` are required; the authored
259
- * `profile.prompt.systemPrompt` + `profile.model.default` reach the harness via the §1.5
260
- * `harnessInvocation` mapper. Everything else mirrors `WorktreeCliExecutorOptions`.
261
- */
262
- interface CliWorktreeSeam {
263
- repoRoot: string;
264
- /** Local CLI harness transport. Omit when `bridge` is set. */
265
- harness?: LocalHarness;
266
- taskPrompt: string;
267
- runId?: string;
268
- baseRef?: string;
269
- harnessTimeoutMs?: number;
270
- /** Isolated, network-off Codex execution with terminal JSONL usage capture. */
271
- codexReproducible?: boolean;
272
- /** Absolute host paths denied to reproducible Codex. */
273
- codexReadDeniedPaths?: ReadonlyArray<string>;
274
- testCmd?: string;
275
- typecheckCmd?: string;
276
- checkTimeoutMs?: number;
277
- checkOutputCap?: number;
278
- budgetExempt?: boolean;
279
- /** Live cli-bridge transport inside the worktree. When set, the worktree leaf accepts
280
- * `deliver()` messages and resumes the same bridge session in this worktree cwd. */
281
- bridge?: CliWorktreeBridgeSeam;
282
- /** Test seam — forwarded to worktree helpers. */
283
- runGit?: GitRunner;
284
- /** Test seam — forwarded to verification checks. */
285
- runCommand?: WorktreeCheckRunner;
286
- }
287
- interface CliWorktreeBridgeSeam {
288
- bridgeUrl: string;
289
- bridgeBearer: string;
290
- /** Bridge model/harness id. Defaults to the profile's model hint when omitted. */
291
- model?: string;
292
- agentProfile?: Record<string, unknown>;
293
- timeoutMs?: number;
294
- /** Stable cli-bridge session id. Defaults to `bridge-worktree-${runId}`. */
295
- sessionId?: string;
296
- maxTurns?: number;
297
- }
298
- /**
299
- * cli-bridge seam. A local OpenAI-compatible bridge that fronts harness CLIs
300
- * (claude-code / opencode / kimi / pi) behind one HTTP surface; `model` doubles
301
- * as the harness selector (e.g. `claude-code/sonnet`, `opencode/<provider>/<model>`).
302
- * `agentProfile` is the bridge-dialect profile (metadata.disallowedTools, mcp)
303
- * forwarded verbatim per request — how an arm disables native tools or injects
304
- * a provider search MCP.
305
- *
306
- * The executor opens a RESUMABLE cli-bridge session — structurally identical to the
307
- * sandbox executor's persistent box, just local. `sessionId` is the stable
308
- * caller-owned id cli-bridge maps to the harness's internal conversation id; a
309
- * follow-up steer/resume on the SAME id continues the SAME harness session (opencode
310
- * `-s`, claude `--resume`, …). Omit it and the executor mints a stable one per spawn.
311
- */
312
- interface BridgeSeam {
313
- bridgeUrl: string;
314
- bridgeBearer: string;
315
- model: string;
316
- /** Optional working directory forwarded to cli-bridge and persisted with the session. */
317
- cwd?: string;
318
- agentProfile?: Record<string, unknown>;
319
- timeoutMs?: number;
320
- /** Stable, caller-owned cli-bridge session id for harness-side resume. Defaults
321
- * to a freshly minted per-spawn id so each worker is its own resumable session. */
322
- sessionId?: string;
323
- /** Per-resume-turn inference cap before the worker settles on its last output.
324
- * Mirrors `routerToolsInlineExecutor.maxTurns`; default 200 (runaway backstop). */
325
- maxTurns?: number;
326
- }
327
- /** Generic environment provider executor config. External packages implement
328
- * `AgentEnvironmentProvider`; this built-in wrapper lets `createExecutor`
329
- * consume them as backend data while preserving the existing usage channel. */
330
- interface ProviderSeam extends ProviderExecutorOptions {
331
- provider: AgentEnvironmentProvider | string;
332
- registry?: AgentEnvironmentProviderRegistry;
333
- }
334
- /**
335
- * Router seam WITH tool use — the tool-using router backend. Same direct
336
- * OpenAI-compatible endpoint as `RouterSeam`, but each turn passes `tools`; when
337
- * the model emits tool_calls they run via `executeToolCall` ON THIS HOST and the
338
- * results fold back as `tool` messages, repeating until the model answers without
339
- * a tool or `maxTurns` is hit. A real agentic loop, OFF-BOX — no sandbox, so it
340
- * is unaffected by a box's egress allowlist. One turn = one completion = the
341
- * equal-compute unit. `executeToolCall` receives the task so per-task tool
342
- * surfaces (e.g. a gym keyed by task) can dispatch correctly.
343
- */
344
- interface RouterToolsSeam {
345
- routerBaseUrl: string;
346
- routerKey: string;
347
- model?: string;
348
- tools: ReadonlyArray<ToolSpec>;
349
- executeToolCall: (name: string, args: Record<string, unknown>, task: unknown) => Promise<string>;
350
- /** Online observer of each tool step — the seam a `DetectorMonitor` taps to watch the live pipe
351
- * (raise a `finding` when the worker loops/errors). Called after every tool call resolves, with
352
- * real per-call wall-clock (`startedAt`/`endedAt`/`durationMs`) so a push `TraceSource` can carry
353
- * non-zero span durations onto the unified timeline. */
354
- onToolStep?: (step: {
355
- toolName: string;
356
- args: Record<string, unknown>;
357
- status: 'ok' | 'error';
358
- startedAt?: number;
359
- endedAt?: number;
360
- durationMs?: number;
361
- }) => void;
362
- /** Max inference turns. Default 200 (runaway backstop — set far above any
363
- * legitimate workflow). For tighter per-workflow limits use a cost budget
364
- * or wall-clock deadline at the call site. */
365
- maxTurns?: number;
366
- }
367
- /**
368
- * The leaf `createWorktreeCliExecutor` as a backend-as-data factory: a supervisor-authored
369
- * `AgentProfile` driving claude / codex / opencode on its own worktree. `budgetExempt` like
370
- * the other CLI leaves; the authored systemPrompt + model reach the harness via §1.5.
371
- */
372
- declare const cliWorktreeExecutor: ExecutorFactory<unknown>;
373
- /**
374
- * Config for {@link createExecutor}: the backend is DATA — the cost dial a profile,
375
- * an experiment config, or a replay journal can name — not an import choice. Each
376
- * variant carries its backend's seam (router/router-tools/bridge/cli/cli-worktree/sandbox).
377
- */
378
- type ExecutorConfig = ({
379
- backend: 'router';
380
- } & RouterSeam) | ({
381
- backend: 'router-tools';
382
- } & RouterToolsSeam) | ({
383
- backend: 'bridge';
384
- } & BridgeSeam) | ({
385
- backend: 'cli';
386
- } & CliSeam) | ({
387
- backend: 'cli-worktree';
388
- } & CliWorktreeSeam) | ({
389
- backend: 'provider';
390
- } & ProviderSeam) | ({
391
- backend: 'pi';
392
- } & PiSeam) | ({
393
- backend: 'sandbox';
394
- harness?: BackendType;
395
- } & SandboxSeam);
396
- /**
397
- * The single built-in executor factory. Picks a leaf backend by data (`config.backend`),
398
- * injects the matching seam, and delegates to that backend's built-in implementation.
399
- * The `Executor` port stays OPEN: bring-your-own agents implement `Executor` directly
400
- * and never pass through here. Use this (or `createExecutorRegistry`) instead of a
401
- * per-vendor adapter or a closed `inline|sandbox|cli` switch — those bypass the
402
- * `UsageEvent` reporting channel.
403
- */
404
- declare function createExecutor(config: ExecutorConfig): ExecutorFactory<unknown>;
405
- /**
406
- * The open resolver/registry. Pre-registers the three built-ins under their
407
- * runtime tags (`'router'`, `'sandbox'`, `'cli'`) and accepts `register(name,
408
- * factory)` for any additional runtime — and a BYO `AgentSpec.executor` resolves
409
- * without touching the registry at all. NOT a closed switch; registration + BYO
410
- * ARE the extension points.
411
- *
412
- * `resolve` precedence (frozen in `ExecutorRegistry`): a BYO `spec.executor` →
413
- * `harness === null` → the `'router'` factory; else a registered factory for the
414
- * harness-derived runtime (`'sandbox'` for any `BackendType`); else fail loud.
415
- */
416
- declare function createExecutorRegistry(): ExecutorRegistry;
417
-
418
- /**
419
- *
420
- * The ONLINE analyst: watch a `TraceSource` and fold each tool span through agent-eval's published
421
- * streaming detector kernel (`repeatedActionDetector`/`errorStreakDetector` — the SAME kernel the
422
- * control loop folds), firing `onSignal` the moment a worker loops or error-storms. Substrate-
423
- * agnostic: it consumes spans from any source (owned router/bridge loop OR a sandbox box session),
424
- * never the raw tool seam. Detection logic + the failure taxonomy live in agent-eval; not reimplemented.
425
- *
426
- * @experimental
427
- */
428
-
429
- interface WatchTraceOptions {
430
- /** The detectors to run online. Defaults to a stuck-loop + error-streak panel. */
431
- readonly detectors?: ReadonlyArray<StreamingDetector>;
432
- /** Fired for each signal a detector raises — the seam that raises a `finding` on the bus. */
433
- readonly onSignal?: (signal: DetectorSignal, span: ToolSpan) => void | Promise<void>;
434
- }
435
- /** The default online panel for a tool-call pipe: a worker repeating the same call, or hammering
436
- * consecutive errors. (No-progress needs a domain progress-probe, so it is opt-in, not default.)
437
- *
438
- * Coverage note: `repeated-action` works for EVERY harness (it needs only tool name + args, which
439
- * every adapter provides). `error-streak` needs per-call status — opencode carries it inline
440
- * (`state.status`, VALIDATED live), but claude-code/codex tool-call parts do NOT (their errors live
441
- * in separate result blocks not yet decoded), so error-streak is silent for those until result-block
442
- * decoding is added + live-validated. It is in the panel because it is correct where status exists. */
443
- declare function defaultToolDetectors(): StreamingDetector[];
444
- /** Subscribe to a `TraceSource` and run the streaming detectors over its live spans. Returns an
445
- * unsubscribe. A defensive `argHash` failure (circular args) never throws out of the side-channel. */
446
- declare function watchTrace(source: TraceSource, opts?: WatchTraceOptions): () => void;
447
-
448
- /**
449
- *
450
- * The child→parent message bus: the ONE pipe carrying every message a worker, sub-driver, or
451
- * analyst sends up to the driver — settled outputs, questions, and trace-analyst findings. It
452
- * unifies channels that were ad-hoc before (the settled-worker cursor, the ask-parent question
453
- * channel, and analyst results) into a single typed primitive with two lanes:
454
- *
455
- * - PASS-THROUGH (`subscribe`): every published event reaches subscribers immediately — the
456
- * express lane for online steering and live observation (a UI, a hook, the parent's box).
457
- * - STANDBY (`pull`): events also queue so the driver consumes them on its own cadence. The queue
458
- * is PRIORITY-ordered: a higher-`priority` event (a blocking question) is bumped ahead of
459
- * queued settles/findings so the driver sees it first; ties resolve FIFO by publish order.
460
- *
461
- * Observability is first-class (A++): every event is stamped with a monotonic `seq` and wall-clock
462
- * `at`, the full ordered `history()` is retained as an audit/replay trail, and `stats()` exposes
463
- * published/pulled counts by kind. Subscribers receive the stamped record, not a bare event.
464
- *
465
- * The interface is transport-agnostic on purpose. Same box → this in-process queue. Cross box →
466
- * the SAME publish/pull/subscribe surface backed by a durable mailbox on the parent's box (children
467
- * POST events with at-least-once retry; payloads are blob refs so the event stays small). Consumers
468
- * depend only on this interface, so distribution is a transport swap, never an architecture change.
469
- *
470
- * @experimental
471
- */
472
- /** Every bus event is a discriminated union member keyed by `type`. */
473
- interface BusEvent {
474
- readonly type: string;
475
- }
476
- /** A published event stamped for ordering and observability. `seq` is the monotonic publish index;
477
- * `priority` drives pull order (higher = bumped ahead); `at` is the wall-clock publish time (ms). */
478
- interface BusRecord<E extends BusEvent> {
479
- readonly seq: number;
480
- readonly at: number;
481
- readonly priority: number;
482
- readonly event: E;
483
- }
484
- interface PublishOptions {
485
- /** Higher = pulled ahead of lower-priority queued events (default 0). A blocking question sets
486
- * this so it bumps to the front of the driver's inbox. */
487
- readonly priority?: number;
488
- /** Whether the event enters the pull queue (default true). Set `false` for record-only events —
489
- * the parent→child down-leg (steer / answer / resume): they belong in `history()` and reach
490
- * `subscribe` observers, but the parent must never `pull` its own outbound message back. */
491
- readonly queue?: boolean;
492
- }
493
- interface BusStats {
494
- readonly published: number;
495
- readonly pulled: number;
496
- /** Count published per event `type`. */
497
- readonly byKind: Readonly<Record<string, number>>;
498
- }
499
- interface EventBus<E extends BusEvent> {
500
- /** Stamp + queue the event, then deliver the stamped record to every subscriber in order.
501
- * Returns the stamped record. */
502
- publish(event: E, opts?: PublishOptions): Promise<BusRecord<E>>;
503
- /** Remove and return the highest-priority QUEUED event whose type is in `kinds` (any if omitted),
504
- * ties broken FIFO by `seq`; `undefined` when nothing matches. */
505
- pull(kinds?: ReadonlyArray<E['type']>): E | undefined;
506
- /** Register a pass-through handler; it receives the stamped record of every event published after
507
- * registration. Returns an unsubscribe fn. */
508
- subscribe(handler: (record: BusRecord<E>) => void | Promise<void>): () => void;
509
- /** Count of queued, not-yet-pulled events (filtered by `kinds` when given). */
510
- pending(kinds?: ReadonlyArray<E['type']>): number;
511
- /** The full ordered log of every event ever published (the audit/replay trail). */
512
- history(): ReadonlyArray<BusRecord<E>>;
513
- /** Throughput counters for observability dashboards. */
514
- stats(): BusStats;
515
- }
516
- /** Create the child→parent coordination bus: one typed pipe for settled outputs, questions, and analyst findings, with a priority-ordered pull queue and a pass-through subscribe lane. */
517
- declare function createEventBus<E extends BusEvent>(now?: () => number): EventBus<E>;
518
-
519
- /**
520
- *
521
- * Sandbox-session coder decode layer. The sandbox-session delegate (`./delegates`) and the
522
- * cross-restart resume driver run the in-box harness over a `SandboxClient` and need to
523
- * (a) build an `AgentRunSpec` from the caller-authored (or minimal model-only default) worker
524
- * profile, (b) decode the harness event stream into a structured `CoderOutput`, and (c) gate it with
525
- * the shared mechanical checks. This sandbox-session path is kept separate from the generic recursive
526
- * path: `worktreeFanout` instead settles the raw `WorktreePatchArtifact` and gates via
527
- * `patchDelivered`. Prefer `worktreeFanout` / `worktreeLoopRunner` for NEW local-repo coding.
528
- *
529
- * The decode tolerates two `result`-event shapes:
530
- * 1. the in-process executor's raw worktree-harness result (`{ branch, patch, stats, checks }`),
531
- * projected onto `CoderOutput`; and
532
- * 2. an LLM-emitted JSON block (`{ branch, patch, testResult, typecheckResult, diffStats }`),
533
- * lifted onto `data.result` or scanned out of the assistant transcript (any harness shape).
534
- *
535
- * @experimental
536
- */
537
-
538
- /** @experimental The structured coder result the sandbox-session path decodes + gates. */
539
- interface CoderOutput {
540
- /** Branch the agent wrote the patch on. */
541
- branch: string;
542
- /** Unified diff (`git diff <base>..HEAD`). */
543
- patch: string;
544
- testResult: {
545
- passed: boolean;
546
- output: string;
547
- };
548
- typecheckResult: {
549
- passed: boolean;
550
- output: string;
551
- };
552
- diffStats: {
553
- filesChanged: number;
554
- insertions: number;
555
- deletions: number;
556
- };
557
- /** Optional reviewer commentary surfaced by the agent. */
558
- reviewerNotes?: string;
559
- }
560
-
561
- /**
562
- *
563
- * Persistence port for the MCP delegation queue.
564
- *
565
- * `DelegationTaskQueue` keeps its working set in memory (status/history
566
- * reads stay synchronous) and journals every record mutation through a
567
- * `DelegationStore`. `DelegationTaskQueue.restore({ store })` is the load
568
- * path: it reads the full record set once at construction and rehydrates
569
- * the queue from it. After that the store only sees writes.
570
- *
571
- * Records MUST be JSON-safe — `FileDelegationStore` round-trips them
572
- * through `JSON.stringify`/`JSON.parse`, so a `Date`, `Map`, or function
573
- * smuggled into `args`/`result` would corrupt the journal.
574
- *
575
- * @experimental
576
- */
577
-
578
- /** @experimental */
579
- interface DelegationStore {
580
- /**
581
- * Read every persisted record. Called once, by
582
- * `DelegationTaskQueue.restore`, before any write. A missing backing
583
- * file is an empty store; an unparseable one throws
584
- * `DelegationStateCorruptError`.
585
- */
586
- loadAll(): Promise<DelegationRecord[]>;
587
- /** Insert or replace the record keyed by `record.taskId`. */
588
- upsert(record: DelegationRecord): Promise<void>;
589
- /**
590
- * Resolve an idempotency key to the taskId that claimed it, if any.
591
- * The queue serves submit-time dedupe from its rehydrated in-memory
592
- * index; this read exists for consumers that share a store across
593
- * processes without holding the full record set.
594
- */
595
- lookupIdempotencyKey(key: string): Promise<string | undefined>;
596
- /** Delete the named records — the retention-cap eviction path. */
597
- remove(taskIds: readonly string[]): Promise<void>;
598
- }
599
- /**
600
- * The persisted delegation state exists but cannot be parsed into
601
- * records. Fail loud: silently starting empty over a corrupt journal
602
- * would erase delegation history and re-run idempotent work. Opt into
603
- * recovery explicitly via `FileDelegationStoreOptions.recoverCorrupt`
604
- * (the bin maps `AGENT_RUNTIME_DELEGATION_STATE_RECOVER=1` onto it),
605
- * which archives the corrupt file and starts fresh.
606
- *
607
- * @experimental
608
- */
609
- declare class DelegationStateCorruptError extends AgentEvalError {
610
- constructor(message: string, options?: {
611
- cause?: unknown;
612
- });
613
- }
614
- /**
615
- * A delegation-store read or write failed (filesystem error, store
616
- * called before `loadAll`, ...). Once the queue observes one, it stops
617
- * accepting new submissions — accepting work it cannot journal would
618
- * silently demote durable mode to in-memory mode.
619
- *
620
- * @experimental
621
- */
622
- declare class DelegationPersistenceError extends AgentEvalError {
623
- constructor(message: string, options?: {
624
- cause?: unknown;
625
- });
626
- }
627
- /** In-memory `DelegationStore` — suitable for single-process use and tests. @experimental */
628
- declare class InMemoryDelegationStore implements DelegationStore {
629
- private readonly records;
630
- loadAll(): Promise<DelegationRecord[]>;
631
- upsert(record: DelegationRecord): Promise<void>;
632
- lookupIdempotencyKey(key: string): Promise<string | undefined>;
633
- remove(taskIds: readonly string[]): Promise<void>;
634
- }
635
- /** @experimental */
636
- interface FileDelegationStoreOptions {
637
- /** Absolute path of the JSON state file. Parent directories are created on first write. */
638
- filePath: string;
639
- /**
640
- * When the state file exists but cannot be parsed, archive it to
641
- * `<filePath>.corrupt-<timestamp>` and start empty instead of
642
- * throwing `DelegationStateCorruptError`. Default false.
643
- */
644
- recoverCorrupt?: boolean;
645
- }
646
- /**
647
- * JSON-file persistence for the delegation queue. Each write serializes
648
- * the full record set and lands it atomically (write to a sibling tmp
649
- * file, then `rename`), so readers never observe a torn file — a crash
650
- * mid-write leaves the previous snapshot intact. Writes are serialized
651
- * internally; concurrent `upsert`/`remove` calls cannot interleave.
652
- *
653
- * Built for the MCP server's scale (one stdio process, hundreds of
654
- * records): full-snapshot writes keep the format trivially inspectable
655
- * and corruption-detectable without a database dependency.
656
- *
657
- * @experimental
658
- */
659
- declare class FileDelegationStore implements DelegationStore {
660
- private readonly filePath;
661
- private readonly recoverCorrupt;
662
- private readonly records;
663
- private loaded;
664
- private writeTail;
665
- private tmpSeq;
666
- constructor(options: FileDelegationStoreOptions);
667
- loadAll(): Promise<DelegationRecord[]>;
668
- upsert(record: DelegationRecord): Promise<void>;
669
- lookupIdempotencyKey(key: string): Promise<string | undefined>;
670
- remove(taskIds: readonly string[]): Promise<void>;
671
- private assertLoaded;
672
- private enqueueWrite;
673
- private writeSnapshot;
674
- }
675
-
676
- /**
677
- *
678
- * Compact loop-trace tee for the delegation journal.
679
- *
680
- * The OTEL exporter ({@link createPropagatingTraceEmitter}) is a no-op
681
- * without `OTEL_EXPORTER_OTLP_ENDPOINT`, which leaves delegated work streams
682
- * dark in practice. This module derives the same loop → round → branch span
683
- * tree (via the shared {@link buildLoopSpanNodes} builder) into a small,
684
- * JSON-safe shape persisted directly on the `DelegationRecord` — observable
685
- * through `delegation_status` with no collector infrastructure. Both sinks
686
- * coexist: the OTEL export path is unchanged.
687
- *
688
- * Payload discipline: a record's trace is hard-capped (spans + serialized
689
- * bytes). Past the cap the OLDEST spans are dropped and the record carries a
690
- * `traceTruncated: true` marker — truncation is never silent.
691
- *
692
- * @experimental
693
- */
694
-
695
- /**
696
- * One span of a delegation's compact trace. Flat (parent linkage by id), all
697
- * values JSON-safe scalars — `FileDelegationStore` round-trips records
698
- * through `JSON.stringify`. `meta` carries the span's attributes (GenAI
699
- * semconv keys + `tangle.loop.*` extensions) exactly as the OTEL sink emits
700
- * them, so a consumer can re-export journal traces losslessly.
701
- *
702
- * @experimental
703
- */
704
- interface DelegationTraceSpan {
705
- spanId: string;
706
- /** Absent on the tree root. */
707
- parentSpanId?: string;
708
- /** `'loop'` | `'loop.round'` | `'loop.iteration'` (or a sink-specific name). */
709
- name: string;
710
- /** Topology level: loop root, plan round, or iteration branch. */
711
- kind: 'loop' | 'round' | 'branch';
712
- startMs: number;
713
- endMs: number;
714
- meta?: Record<string, string | number | boolean>;
715
- }
716
- /** Default cap on spans retained per delegation record. @experimental */
717
- declare const DELEGATION_TRACE_MAX_SPANS = 512;
718
- /** Default cap on the serialized trace payload per record, in bytes. @experimental */
719
- declare const DELEGATION_TRACE_MAX_BYTES: number;
720
- /** @experimental */
721
- interface DelegationTraceCaps {
722
- /** Default {@link DELEGATION_TRACE_MAX_SPANS}. */
723
- maxSpans?: number;
724
- /** Default {@link DELEGATION_TRACE_MAX_BYTES}. Approximate — measured as the
725
- * sum of per-span `JSON.stringify` lengths. */
726
- maxBytes?: number;
727
- }
728
- /** @experimental */
729
- interface CappedDelegationTrace {
730
- trace: DelegationTraceSpan[];
731
- /** True when oldest spans were dropped to honor the caps. */
732
- truncated: boolean;
733
- }
734
- /**
735
- * Derive the compact span tree for ONE loop run from its buffered
736
- * `LoopTraceEvent` stream. Same reconstruction as the OTEL exporter
737
- * ({@link buildLoopSpanNodes}); tolerates partial streams.
738
- *
739
- * @experimental
740
- */
741
- declare function buildDelegationTraceSpans(events: ReadonlyArray<LoopTraceEvent>): DelegationTraceSpan[];
742
- /**
743
- * Enforce the trace caps over an ordered (oldest-first) span list. Drops the
744
- * OLDEST spans first and reports `truncated: true` when anything was dropped;
745
- * the newest span always survives, so a non-empty input never caps to empty.
746
- * Dropping a parent may orphan surviving children's `parentSpanId` references
747
- * — acceptable for the flat journal shape; consumers treat unresolved parents
748
- * as roots.
749
- *
750
- * @experimental
751
- */
752
- declare function capDelegationTrace(spans: ReadonlyArray<DelegationTraceSpan>, caps?: DelegationTraceCaps): CappedDelegationTrace;
753
- /**
754
- * Per-delegation trace collector. Buffers `LoopTraceEvent`s per runId
755
- * (mirroring the OTEL emitter's buffering) and hands the derived compact
756
- * spans to `onSpans` when a run reaches `loop.ended`. `settle()` drains runs
757
- * that never ended — a hard-aborted loop still leaves its partial tree in the
758
- * journal, unlike the OTEL path which drops it.
759
- *
760
- * @experimental
761
- */
762
- interface DelegationTraceCollector {
763
- emitter: LoopTraceEmitter;
764
- /** Flush buffered events of runs that never reached `loop.ended`. */
765
- settle(): void;
766
- }
767
- /** Build a `DelegationTraceCollector` that buffers loop-trace events and converts them to spans on settle. @experimental */
768
- declare function createDelegationTraceCollector(onSpans: (spans: DelegationTraceSpan[]) => void): DelegationTraceCollector;
769
- /**
770
- * Fan one `LoopTraceEvent` stream into several emitters — e.g. the
771
- * process-wide OTEL exporter AND the per-delegation journal collector.
772
- * `undefined` entries are skipped; returns `undefined` when nothing is left
773
- * so callers keep the kernel's "no emitter, no events" fast path.
774
- *
775
- * @experimental
776
- */
777
- declare function composeLoopTraceEmitters(...emitters: ReadonlyArray<LoopTraceEmitter | undefined>): LoopTraceEmitter | undefined;
778
-
779
- /**
780
- * OTEL span exporter — streams LoopTraceEvents to an OTLP/HTTP collector.
781
- *
782
- * Reads OTEL_EXPORTER_OTLP_ENDPOINT + OTEL_EXPORTER_OTLP_HEADERS from env
783
- * when no explicit config is given. Keeps the runtime dep-free from
784
- * @opentelemetry/sdk-trace-base — minimal OTLP/JSON serializer.
785
- *
786
- * The exporter accepts both raw OtelSpan objects and LoopTraceEvents
787
- * (which get converted to OTLP spans automatically).
788
- */
789
-
790
- interface OtelExportConfig {
791
- /** OTLP endpoint. Reads OTEL_EXPORTER_OTLP_ENDPOINT env by default. */
792
- endpoint?: string;
793
- /** OTLP headers. Reads OTEL_EXPORTER_OTLP_HEADERS env by default. */
794
- headers?: Record<string, string>;
795
- /** Batch size before flush. Default 64. */
796
- batchSize?: number;
797
- /** Flush interval ms. Default 5000. */
798
- flushIntervalMs?: number;
799
- /** Resource attributes stamped on every export. */
800
- resourceAttributes?: Record<string, string | number | boolean>;
801
- /** Service name. Default 'agent-runtime'. */
802
- serviceName?: string;
803
- }
804
- interface OtelExporter {
805
- /** Export a span. */
806
- exportSpan(span: OtelSpan): void;
807
- /** Force flush pending spans. */
808
- flush(): Promise<void>;
809
- /** Shutdown cleanly. */
810
- shutdown(): Promise<void>;
811
- }
812
- interface OtelSpan {
813
- traceId: string;
814
- spanId: string;
815
- parentSpanId?: string;
816
- name: string;
817
- kind?: number;
818
- startTimeUnixNano: string;
819
- endTimeUnixNano: string;
820
- attributes?: OtelAttribute[];
821
- status?: {
822
- code: number;
823
- message?: string;
824
- };
825
- }
826
- interface OtelAttribute {
827
- key: string;
828
- value: {
829
- stringValue?: string;
830
- intValue?: string;
831
- doubleValue?: number;
832
- boolValue?: boolean;
833
- };
834
- }
835
- /**
836
- * Create an OTEL exporter. Returns undefined when no endpoint is configured.
837
- */
838
- declare function createOtelExporter(config?: OtelExportConfig): OtelExporter | undefined;
839
- /**
840
- * Convert a LoopTraceEvent into an OtelSpan for export.
841
- */
842
- declare function loopEventToOtelSpan(event: {
843
- kind: string;
844
- runId: string;
845
- timestamp: number;
846
- payload: object;
847
- }, traceId: string, parentSpanId?: string): OtelSpan;
848
- interface RuntimeEventOtelOptions extends RuntimeTelemetryOptions {
849
- /** Final customer redactor applied after the schema-aware runtime sanitizer. */
850
- redact?: (value: unknown) => unknown;
851
- }
852
- /** Convert normalized runtime events into lossless, redacted child spans. */
853
- declare function buildRuntimeEventOtelSpans(events: ReadonlyArray<RuntimeStreamEvent>, traceId: string, parentSpanId?: string, options?: RuntimeEventOtelOptions): OtelSpan[];
854
- /**
855
- * Sink-neutral node in a reconstructed loop span tree. The root node's
856
- * `parentSpanId` is `undefined` — sinks decide how to parent it (the OTEL
857
- * mapper attaches the inherited delegation span; the delegation journal
858
- * leaves it as the tree root).
859
- */
860
- interface LoopSpanNode {
861
- spanId: string;
862
- parentSpanId?: string;
863
- /** `'loop'` | `'loop.round'` | `'loop.iteration'`. */
864
- name: string;
865
- /** Topology level: loop root, plan round, or iteration branch. */
866
- kind: 'loop' | 'round' | 'branch';
867
- startMs: number;
868
- endMs: number;
869
- attrs: Record<string, string | number | boolean>;
870
- /** True when the iteration carried an error — maps to OTEL status code 2. */
871
- error: boolean;
872
- }
873
- /**
874
- * Build a nested, real-duration OTLP span tree for ONE loop run from its full
875
- * ordered `LoopTraceEvent` stream. Unlike `loopEventToOtelSpan` (one flat,
876
- * zero-duration span per event), this reconstructs the topology hierarchy a
877
- * GenAI trace viewer renders natively:
878
- *
879
- * loop (invoke_workflow)
880
- * └─ loop.round[k] (invoke_workflow) ← tangle.loop.move.{kind,width,rationale}
881
- * ├─ loop.iteration[i] (invoke_agent) ← gen_ai.agent.name + usage + verdict + placement
882
- * └─ …
883
- *
884
- * Attributes follow the current GenAI semconv (`gen_ai.*`) where they apply and
885
- * a namespaced `tangle.loop.*` / `tangle.cost.usd` extension for topology /
886
- * verdict / placement / cost (not yet standardized). Pure: feed it a buffered
887
- * per-runId event array (e.g. flushed on `loop.ended`) and export the result.
888
- */
889
- declare function buildLoopOtelSpans(events: ReadonlyArray<{
890
- kind: string;
891
- runId: string;
892
- timestamp: number;
893
- payload: object;
894
- }>, traceId: string, rootParentSpanId?: string): OtelSpan[];
895
- /**
896
- * Sink-neutral core behind {@link buildLoopOtelSpans}: reconstruct the
897
- * loop → round → branch span tree from one run's ordered `LoopTraceEvent`
898
- * stream. Consumed by the OTEL mapper above and by the MCP delegation
899
- * journal's compact trace tee — one topology reconstruction, two sinks.
900
- * Tolerates partial streams (a run that never reached `loop.ended` closes
901
- * at the last observed event's timestamp).
902
- */
903
- declare function buildLoopSpanNodes(events: ReadonlyArray<{
904
- kind: string;
905
- runId: string;
906
- timestamp: number;
907
- payload: object;
908
- }>): LoopSpanNode[];
909
- /** Wire version the eval-runs ingest enforces (X-Tangle-Wire-Version + body). */
910
- declare const INTELLIGENCE_WIRE_VERSION = "2026-05-26.v1";
911
- interface EvalRunGeneration {
912
- /** 0-based ordinal of this generation within the run (required by ingest). */
913
- index: number;
914
- /** Identity of the proposed surface change (content-addressed hash). */
915
- surfaceHash: string;
916
- /** Arbitrary provenance for this generation (rationale, evidence, source). */
917
- surface?: unknown;
918
- /** Per-scenario results; empty until the generation is measured. */
919
- cells?: unknown[];
920
- /** Mean composite score (0 when unmeasured — pair with labels.measured). */
921
- compositeMean: number;
922
- costUsd: number;
923
- durationMs: number;
924
- }
925
- interface EvalRunEvent {
926
- runId: string;
927
- runDir: string;
928
- /** ISO timestamp. */
929
- timestamp: string;
930
- status: 'started' | 'baseline-complete' | 'generation-complete' | 'gate-decided' | 'finished' | 'errored';
931
- labels?: Record<string, string>;
932
- baseline?: EvalRunGeneration;
933
- generations?: EvalRunGeneration[];
934
- gateDecision?: 'ship' | 'hold' | 'need_more_work' | 'model_ceiling' | 'arch_ceiling';
935
- holdoutLift?: number;
936
- totalCostUsd: number;
937
- totalDurationMs: number;
938
- errorMessage?: string;
939
- }
940
- interface EvalRunsExportConfig {
941
- /** Bearer key — tenant is resolved server-side from it. Reads TANGLE_API_KEY. */
942
- apiKey?: string;
943
- /** Intelligence base. Reads TANGLE_INTELLIGENCE_URL env, else prod. */
944
- base?: string;
945
- /** Idempotency-Key header (e.g. the runId) — safe retries + upsert. */
946
- idempotencyKey?: string;
947
- }
948
- interface EvalRunsExportResult {
949
- ok: boolean;
950
- status: number;
951
- accepted: number;
952
- rejected: Array<{
953
- index: number;
954
- reason: string;
955
- }>;
956
- }
957
- /**
958
- * Ship self-improvement eval-run events to Tangle Intelligence. Unlike the
959
- * best-effort span exporter, this RESOLVES with the ingest verdict (accepted /
960
- * rejected per event) so a consumer's loop can assert its provenance landed.
961
- * Throws only on a missing key or network failure.
962
- */
963
- declare function exportEvalRuns(events: EvalRunEvent[], config?: EvalRunsExportConfig): Promise<EvalRunsExportResult>;
964
-
965
- /**
966
- *
967
- * Trace context propagation for MCP subprocess.
968
- *
969
- * When the MCP server is launched as a child process by a sandbox harness,
970
- * the parent passes trace context via environment variables:
971
- *
972
- * TRACE_ID=<current-run-trace-id>
973
- * PARENT_SPAN_ID=<span-that-dispatched-the-delegation>
974
- *
975
- * The MCP server reads these at startup and uses them as the root of its
976
- * internal trace tree. All spans emitted by `runAgentRounds` invocations inside
977
- * the MCP are children of the parent's delegation span.
978
- *
979
- * When these env vars are absent, the MCP generates a fresh trace root —
980
- * the server operates standalone without trace joining.
981
- *
982
- * @experimental
983
- */
984
-
985
- interface TraceContext {
986
- /** Trace id inherited from the parent process, or a fresh one. */
987
- traceId: string;
988
- /** Parent span id from the delegation that launched this MCP server. */
989
- parentSpanId?: string;
990
- }
991
- /**
992
- * Read trace context from the process environment.
993
- * Returns a context with inherited ids or a freshly generated root.
994
- */
995
- declare function readTraceContextFromEnv(): TraceContext;
996
- /**
997
- * Create a LoopTraceEmitter that:
998
- * 1. Parents all spans under the inherited PARENT_SPAN_ID.
999
- * 2. Exports spans to OTEL when OTEL_EXPORTER_OTLP_ENDPOINT is set.
1000
- *
1001
- * Returns both the emitter and the optional exporter handle for shutdown.
1002
- */
1003
- declare function createPropagatingTraceEmitter(ctx: TraceContext): {
1004
- emitter: LoopTraceEmitter;
1005
- exporter: OtelExporter | undefined;
1006
- context: TraceContext;
1007
- };
1008
- /**
1009
- * Build env vars to pass to a child MCP subprocess so it inherits the
1010
- * current trace context.
1011
- */
1012
- declare function traceContextToEnv(ctx: TraceContext): Record<string, string>;
1013
-
1014
- /**
1015
- *
1016
- * MCP delegation tool surface — the typed inputs/outputs the product agent
1017
- * sees over the wire. These types are the contract; the JSON schemas under
1018
- * `tools/*` mirror them for the MCP `tools/list` advertisement.
1019
- *
1020
- * Async semantics: `delegate_ui_audit` returns a `taskId` immediately. The
1021
- * product agent polls `delegation_status` until the task transitions to
1022
- * `completed` | `failed` | `cancelled`. `delegate_feedback` +
1023
- * `delegation_history` are synchronous reads / writes against the local
1024
- * task queue + feedback store.
1025
- *
1026
- * @experimental
1027
- */
1028
-
1029
- /** @experimental */
1030
- type DelegationProfile = 'coder' | 'researcher' | 'ui-auditor';
1031
- /** @experimental */
1032
- type DelegationStatus = 'pending' | 'running' | 'completed' | 'failed' | 'cancelled';
1033
- /**
1034
- * Minimal `CoderTask` overrides exposed over the MCP wire. The full
1035
- * `CoderTask` carries fields the kernel synthesizes from `goal` +
1036
- * `repoRoot` — the agent only edits the few that materially gate
1037
- * validator behavior.
1038
- *
1039
- * @experimental
1040
- */
1041
- interface DelegateCodeConfig {
1042
- testCmd?: string;
1043
- typecheckCmd?: string;
1044
- forbiddenPaths?: string[];
1045
- maxDiffLines?: number;
1046
- }
1047
- /** @experimental */
1048
- interface DelegateCodeArgs {
1049
- /** Natural-language description of what the coder must accomplish. */
1050
- goal: string;
1051
- /** Absolute path inside the sandbox where the repo lives. */
1052
- repoRoot: string;
1053
- /** Optional free-form context the agent surfaces in the prompt prelude. */
1054
- contextHint?: string;
1055
- /**
1056
- * When > 1, dispatches `multiHarnessCoderFanout` across N harnesses
1057
- * (claude-code, codex, opencode-glm) and picks the highest-scoring
1058
- * passing patch. Default 1.
1059
- */
1060
- variants?: number;
1061
- /** Validator + prompt overrides the agent knows for this repo. */
1062
- config?: DelegateCodeConfig;
1063
- /** Multi-tenant scope (customer-id, workspace-id). */
1064
- namespace?: string;
1065
- }
1066
- /** @experimental */
1067
- interface DelegateCodeResult {
1068
- taskId: string;
1069
- /** Best-effort hint — coder loops can take minutes-to-hours. */
1070
- estimatedDurationMs?: number;
1071
- }
1072
- /** @experimental */
1073
- type ResearchSource = 'web' | 'corpus' | 'twitter' | 'github' | 'docs';
1074
- /** @experimental */
1075
- interface DelegateResearchConfig {
1076
- recencyWindow?: {
1077
- since?: string;
1078
- until?: string;
1079
- };
1080
- maxItems?: number;
1081
- minConfidence?: number;
1082
- }
1083
- /** @experimental */
1084
- interface DelegateResearchArgs {
1085
- question: string;
1086
- namespace: string;
1087
- scope?: string;
1088
- sources?: ResearchSource[];
1089
- variants?: number;
1090
- config?: DelegateResearchConfig;
1091
- }
1092
- /** @experimental */
1093
- interface DelegateResearchResult {
1094
- taskId: string;
1095
- estimatedDurationMs?: number;
1096
- }
1097
- /** @experimental */
1098
- interface FeedbackRefersTo {
1099
- kind: 'delegation' | 'artifact' | 'outcome';
1100
- /** For `'delegation'`, this is the taskId. */
1101
- ref: string;
1102
- }
1103
- /** @experimental */
1104
- interface FeedbackRating {
1105
- /** [0, 1]. */
1106
- score: number;
1107
- label?: 'good' | 'bad' | 'neutral' | 'mixed';
1108
- notes: string;
1109
- }
1110
- /** @experimental */
1111
- interface DelegateFeedbackArgs {
1112
- refersTo: FeedbackRefersTo;
1113
- rating: FeedbackRating;
1114
- by: 'agent' | 'user' | 'downstream-judge';
1115
- /** ISO timestamp; defaults to server clock when omitted. */
1116
- capturedAt?: string;
1117
- namespace?: string;
1118
- }
1119
- /** @experimental */
1120
- interface DelegateFeedbackResult {
1121
- recorded: true;
1122
- id: string;
1123
- }
1124
- /** @experimental */
1125
- interface DelegationStatusArgs {
1126
- taskId: string;
1127
- /**
1128
- * Return the delegation's compact loop-trace span tree alongside the
1129
- * status. Default false — status polls stay light; opt in when you need
1130
- * the topology (which iterations ran, where they were placed, what each
1131
- * cost) rather than just the state machine.
1132
- */
1133
- includeTrace?: boolean;
1134
- }
1135
- /** @experimental */
1136
- interface DelegationProgress {
1137
- iteration: number;
1138
- phase: string;
1139
- }
1140
- /** @experimental */
1141
- interface DelegationError {
1142
- message: string;
1143
- kind: string;
1144
- }
1145
- /**
1146
- * Polymorphic `result` field: `CoderOutput` when the underlying profile
1147
- * is `'coder'`, a structurally-typed research output when `'researcher'`.
1148
- * The MCP wire carries it as JSON either way.
1149
- *
1150
- * @experimental
1151
- */
1152
- type DelegationResultPayload = {
1153
- profile: 'coder';
1154
- output: CoderOutput;
1155
- } | {
1156
- profile: 'researcher';
1157
- output: ResearchOutputShape;
1158
- } | {
1159
- profile: 'ui-auditor';
1160
- output: UiAuditorDelegationOutput;
1161
- };
1162
- /**
1163
- * Wire-shape of a completed UI-audit delegation. The `findings` array
1164
- * contains every finding persisted to the workspace during the run,
1165
- * already enriched with `id` and `createdAt` by the writer. `workspaceDir`
1166
- * is the absolute path to the workspace; `indexFile` is the workspace-
1167
- * relative path to the regenerated index.md.
1168
- *
1169
- * @experimental
1170
- */
1171
- interface UiAuditorDelegationOutput {
1172
- workspaceDir: string;
1173
- indexFile: string;
1174
- findings: UiFinding[];
1175
- /** Total iterations the loop ran for this delegation. */
1176
- iterations: number;
1177
- }
1178
- /** @experimental */
1179
- type UiAuditLensFilter = readonly UiLens[];
1180
- /** Optional per-route capture spec the agent surfaces over the wire. */
1181
- interface DelegateUiAuditRoute {
1182
- /** Stable route name (used in screenshot filenames + finding metadata). */
1183
- name: string;
1184
- /** Fully-qualified URL. */
1185
- url: string;
1186
- /** Viewports to capture at. Defaults to `[{ width: 1280, height: 800 }]`. */
1187
- viewports?: readonly {
1188
- width: number;
1189
- height: number;
1190
- }[];
1191
- /** Default false. Full-page captures for the broad lenses. */
1192
- fullPage?: boolean;
1193
- /** Selector to wait for before capture. */
1194
- waitFor?: string;
1195
- }
1196
- /** @experimental */
1197
- interface DelegateUiAuditConfig {
1198
- /**
1199
- * Lenses to iterate. Default: every lens except `'other'`. Order is
1200
- * preserved — the driver iterates lens-by-lens.
1201
- */
1202
- lenses?: UiAuditLensFilter;
1203
- /** Maximum total iterations across all (lens × route) pairs. Default 33 (11 lenses × 3 routes). */
1204
- maxIterations?: number;
1205
- /** Maximum concurrent iterations within a single plan() round. Default 2. */
1206
- maxConcurrency?: number;
1207
- /** Free-form product context surfaced to the judge. */
1208
- productContext?: string;
1209
- }
1210
- /** @experimental */
1211
- interface DelegateUiAuditArgs {
1212
- /** Workspace root for the audit (absolute path). */
1213
- workspaceDir: string;
1214
- /** Routes to audit. Must be non-empty. */
1215
- routes: readonly DelegateUiAuditRoute[];
1216
- /** Multi-tenant scope. */
1217
- namespace?: string;
1218
- config?: DelegateUiAuditConfig;
1219
- }
1220
- /** @experimental */
1221
- interface DelegateUiAuditResult {
1222
- taskId: string;
1223
- estimatedDurationMs?: number;
1224
- }
1225
- /**
1226
- * Provider-neutral research output carried over the MCP boundary. The MCP
1227
- * layer accepts this structural shape instead of coupling its wire contract to
1228
- * one research implementation.
1229
- *
1230
- * @experimental
1231
- */
1232
- interface ResearchOutputShape {
1233
- items: unknown[];
1234
- citations: unknown[];
1235
- proposedWrites: unknown[];
1236
- gaps?: string[];
1237
- notes?: string;
1238
- [key: string]: unknown;
1239
- }
1240
- /** @experimental */
1241
- interface DelegationStatusResult {
1242
- taskId: string;
1243
- profile: DelegationProfile;
1244
- status: DelegationStatus;
1245
- progress?: DelegationProgress;
1246
- result?: DelegationResultPayload;
1247
- error?: DelegationError;
1248
- costUsd?: number;
1249
- startedAt: string;
1250
- completedAt?: string;
1251
- /** Compact loop-trace span tree; present only when `includeTrace: true` was passed and spans were recorded. */
1252
- trace?: DelegationTraceSpan[];
1253
- /** Present when oldest trace spans were dropped to honor the trace caps. */
1254
- traceTruncated?: true;
1255
- /** Inherited trace identity recorded at submit — join key into the caller's trace. */
1256
- traceId?: string;
1257
- /** Caller span that dispatched the delegation, when one was inherited. */
1258
- parentSpanId?: string;
1259
- }
1260
- /** @experimental */
1261
- interface DelegationHistoryArgs {
1262
- namespace?: string;
1263
- profile?: DelegationProfile;
1264
- /** ISO date — only delegations started at-or-after `since` are returned. */
1265
- since?: string;
1266
- /** Default 50. Hard cap 500. */
1267
- limit?: number;
1268
- }
1269
- /** @experimental */
1270
- interface DelegationFeedbackSnapshot {
1271
- id: string;
1272
- score: number;
1273
- label?: FeedbackRating['label'];
1274
- by: DelegateFeedbackArgs['by'];
1275
- notes: string;
1276
- capturedAt: string;
1277
- }
1278
- /** @experimental */
1279
- interface DelegationHistoryEntry {
1280
- taskId: string;
1281
- profile: DelegationProfile;
1282
- namespace?: string;
1283
- args: DelegateCodeArgs | DelegateResearchArgs | DelegateUiAuditArgs;
1284
- status: DelegationStatus;
1285
- feedback?: DelegationFeedbackSnapshot[];
1286
- costUsd?: number;
1287
- startedAt: string;
1288
- completedAt?: string;
1289
- /**
1290
- * True when the record carries a journaled loop trace. History stays
1291
- * light by design — fetch the spans via
1292
- * `delegation_status { taskId, includeTrace: true }`.
1293
- */
1294
- hasTrace: boolean;
1295
- /** Inherited trace identity recorded at submit — join key into the caller's trace. */
1296
- traceId?: string;
1297
- }
1298
- /** @experimental */
1299
- interface DelegationHistoryResult {
1300
- delegations: DelegationHistoryEntry[];
1301
- }
1302
-
1303
- /**
1304
- *
1305
- * State machine for async MCP delegations:
1306
- *
1307
- * pending → running → completed | failed
1308
- * ↘ cancelled (from any non-terminal state via cancel())
1309
- *
1310
- * Each `submit` returns a `taskId` immediately and kicks the work off in the
1311
- * background. The work function receives an `AbortSignal` the queue fires
1312
- * when `cancel(taskId)` is called. The queue does NOT supervise runtime
1313
- * timeouts — the underlying `runAgentRounds` driver / sandbox imposes those.
1314
- *
1315
- * Idempotency: callers may supply an `idempotencyKey` (hash of the input).
1316
- * A duplicate `submit` with a known key returns the existing task instead of
1317
- * starting a new one. Mutated input → different key → different task.
1318
- *
1319
- * Durability: the working set lives in memory (reads stay synchronous) and
1320
- * every record mutation is journaled through a `DelegationStore`. The default
1321
- * `InMemoryDelegationStore` keeps today's semantics — a process restart drops
1322
- * all state. Construct via `DelegationTaskQueue.restore({ store })` with a
1323
- * `FileDelegationStore` to reload prior records on startup: terminal records
1324
- * stay queryable, in-flight records either re-attach through the
1325
- * `resumeDelegate` seam (when they carry a `detachedSessionRef`) or fail
1326
- * loud with a driver-restart error so `delegation_status` tells the truth.
1327
- *
1328
- * @experimental
1329
- */
1330
-
1331
- type AnyDelegateArgs = DelegateCodeArgs | DelegateResearchArgs | DelegateUiAuditArgs;
1332
- /**
1333
- * Must be JSON-safe end to end (`args`, `result`, `error`, `feedback`) —
1334
- * persistent stores round-trip records through `JSON.stringify`.
1335
- *
1336
- * @experimental
1337
- */
1338
- interface DelegationRecord {
1339
- taskId: string;
1340
- profile: DelegationProfile;
1341
- namespace?: string;
1342
- args: AnyDelegateArgs;
1343
- status: DelegationStatus;
1344
- progress?: DelegationProgress;
1345
- result?: DelegationResultPayload;
1346
- error?: DelegationError;
1347
- costUsd?: number;
1348
- startedAt: string;
1349
- completedAt?: string;
1350
- /** Sha-prefix hash of the canonical input — used for idempotency lookup. */
1351
- idempotencyKey?: string;
1352
- /**
1353
- * Caller-generated deterministic id of a detached run (e.g. the sandbox
1354
- * session id a single-tick driver resumes by). Presence is what makes a
1355
- * restored in-flight record resumable via `resumeDelegate`; without it a
1356
- * restart settles the record as failed.
1357
- */
1358
- detachedSessionRef?: string;
1359
- /** Feedback events keyed by this delegation's taskId. */
1360
- feedback: DelegationFeedbackSnapshot[];
1361
- /**
1362
- * Compact loop-trace span tree teed from the delegation's run, oldest
1363
- * spans first. Appended when a delegated loop reaches `loop.ended` and
1364
- * settled (partial buffers included) at the terminal transition. Capped
1365
- * via `capDelegationTrace` — see `traceTruncated`.
1366
- */
1367
- trace?: DelegationTraceSpan[];
1368
- /** Present when oldest trace spans were dropped to honor the trace caps. */
1369
- traceTruncated?: true;
1370
- /**
1371
- * Inherited trace identity (the queue's `traceContext` at submit time —
1372
- * typically `readTraceContextFromEnv()`), distinct from the span payload:
1373
- * a journal consumer joins records into the parent trace by these ids
1374
- * without parsing spans. Restored records keep their persisted identity.
1375
- */
1376
- traceId?: string;
1377
- /** Caller span that dispatched the delegation, when one was inherited. */
1378
- parentSpanId?: string;
1379
- }
1380
- /** @experimental */
1381
- interface SubmitInput<Args extends AnyDelegateArgs> {
1382
- profile: DelegationProfile;
1383
- args: Args;
1384
- namespace?: string;
1385
- idempotencyKey?: string;
1386
- /**
1387
- * Records the detached-run resume key on the new record. The submitted
1388
- * `run` function still executes in-process exactly as without it — the
1389
- * ref only matters after a restart, when `DelegationTaskQueue.restore`
1390
- * hands it to the `resumeDelegate` seam instead of failing the record.
1391
- */
1392
- detachedSessionRef?: string;
1393
- /**
1394
- * Runs the underlying delegation. The queue passes a fresh `AbortSignal`
1395
- * and a `report` channel for incremental progress updates. The function
1396
- * MUST resolve with the typed `DelegationResultPayload['output']`; the
1397
- * queue wraps it with the profile tag.
1398
- */
1399
- run: (ctx: DelegationRunContext) => Promise<DelegationResultPayload['output']>;
1400
- }
1401
- /** @experimental Context handed to a `SubmitInput.run` function. */
1402
- interface DelegationRunContext {
1403
- signal: AbortSignal;
1404
- report(progress: DelegationProgress): void;
1405
- /** The `detachedSessionRef` recorded at submit, when one was supplied. */
1406
- detachedSessionRef?: string;
1407
- /**
1408
- * Replace the record's detached-run resume key — the detached dispatch path
1409
- * calls this once the sandbox id is known so the persisted ref names a
1410
- * resolvable box. Ignored after the record settles (a cancel racing the
1411
- * rebind is legitimate; the ref no longer matters then). Throws on an empty
1412
- * ref — erasing the resume key would silently make the record unresumable.
1413
- */
1414
- updateDetachedSessionRef(ref: string): void;
1415
- /**
1416
- * Per-delegation loop-trace sink, always provided by the queue. Events
1417
- * emitted here are journaled onto the record as a compact span tree
1418
- * (`record.trace`) when each loop run ends and at the delegation's
1419
- * terminal transition. Delegates forward it into their `runAgentRounds` ctx,
1420
- * composed with any process-wide OTEL emitter
1421
- * (`composeLoopTraceEmitters`). Optional in the type so consumer-built
1422
- * contexts stay source-compatible.
1423
- */
1424
- traceEmitter?: LoopTraceEmitter;
1425
- }
1426
- /** @experimental */
1427
- interface SubmitOutput {
1428
- taskId: string;
1429
- /** True when a prior matching `idempotencyKey` returned an existing record. */
1430
- reused: boolean;
1431
- }
1432
- /**
1433
- * One observation of a detached run, mapped 1:1 from a single-tick driver
1434
- * (e.g. the sandbox SDK's `driveTurn`, which reports
1435
- * completed | running | failed per pass). `running` schedules another tick
1436
- * after `intervalMs`; `completed` / `failed` settle the record.
1437
- *
1438
- * @experimental
1439
- */
1440
- type DelegationResumeTick = {
1441
- state: 'running';
1442
- } | {
1443
- state: 'completed';
1444
- output: DelegationResultPayload['output'];
1445
- costUsd?: number;
1446
- } | {
1447
- state: 'failed';
1448
- error: DelegationError;
1449
- };
1450
- /** @experimental */
1451
- interface DelegationResumeContext {
1452
- /** Fired by `cancel(taskId)`; the driver should stop the remote run when it can. */
1453
- signal: AbortSignal;
1454
- report(progress: DelegationProgress): void;
1455
- }
1456
- /**
1457
- * Re-attaches restored in-flight records to their detached runs. The queue
1458
- * calls `tick` repeatedly — it never awaits a whole run — so the driver can
1459
- * be a thin wrapper over a one-pass primitive: resolve the run named by
1460
- * `detachedSessionRef`, advance/poll it once, report where it stands. A
1461
- * thrown error settles the record as failed; `failed` ticks are treated as
1462
- * terminal and are not retried.
1463
- *
1464
- * @experimental
1465
- */
1466
- interface DelegationResumeDriver {
1467
- tick(task: {
1468
- record: DelegationRecord;
1469
- detachedSessionRef: string;
1470
- }, ctx: DelegationResumeContext): Promise<DelegationResumeTick>;
1471
- /** Delay between `running` ticks, in milliseconds. Default 5000. */
1472
- intervalMs?: number;
1473
- }
1474
- /** @experimental */
1475
- interface DelegationTaskQueueOptions {
1476
- /** ID generator override; default `randomTaskId`. */
1477
- generateId?: () => string;
1478
- /** Clock override; default `() => new Date().toISOString()`. */
1479
- now?: () => string;
1480
- /**
1481
- * Journal for record mutations and the `restore()` load source. Default
1482
- * `InMemoryDelegationStore` — observably identical to an unjournaled
1483
- * queue. Pass a `FileDelegationStore` through
1484
- * `DelegationTaskQueue.restore` for state that survives a restart;
1485
- * constructing with `new` never loads prior state.
1486
- */
1487
- store?: DelegationStore;
1488
- /** Resume seam for restored in-flight records that carry a `detachedSessionRef`. */
1489
- resumeDelegate?: DelegationResumeDriver;
1490
- /**
1491
- * Maximum number of terminal (completed | failed | cancelled) records
1492
- * retained; the oldest (by `completedAt`) are evicted from memory and
1493
- * store once the cap is exceeded. Default unbounded.
1494
- */
1495
- maxTerminalRecords?: number;
1496
- /**
1497
- * Observes the first store failure. After it fires, the queue refuses
1498
- * new submissions and `flush()` rejects with the same error. Default:
1499
- * rethrow on a microtask — an unhandled crash — because silently
1500
- * degrading durable mode to memory-only would lie to the caller.
1501
- */
1502
- onPersistError?: (error: DelegationPersistenceError) => void;
1503
- /**
1504
- * Inherited trace identity stamped on every submitted record
1505
- * (`traceId` / `parentSpanId`). The bin passes
1506
- * `readTraceContextFromEnv()` so journal consumers can join delegation
1507
- * records into the caller's trace. Restored records keep the identity
1508
- * they were persisted with.
1509
- */
1510
- traceContext?: TraceContext;
1511
- }
1512
- /** In-process queue for async delegation tasks — submit, cancel, poll status, and read history. @experimental */
1513
- declare class DelegationTaskQueue {
1514
- private readonly records;
1515
- private readonly controllers;
1516
- private readonly byIdempotencyKey;
1517
- private readonly generateId;
1518
- private readonly now;
1519
- private readonly store;
1520
- private readonly resumeDelegate?;
1521
- private readonly maxTerminalRecords;
1522
- private readonly onPersistError;
1523
- private readonly traceContext;
1524
- private persistTail;
1525
- private persistFailure;
1526
- constructor(options?: DelegationTaskQueueOptions);
1527
- /**
1528
- * Construct a queue from previously-persisted state. Loads every record
1529
- * from `options.store`, rebuilds the idempotency index (so a re-submitted
1530
- * identical task returns the prior taskId and its terminal state), then:
1531
- *
1532
- * - terminal records stay queryable via `status()` / `history()`
1533
- * - in-flight records with a `detachedSessionRef` re-attach through
1534
- * `options.resumeDelegate` and report `running`
1535
- * - other in-flight records settle as failed — their driver died with
1536
- * the previous process and the result is unrecoverable
1537
- *
1538
- * The retention cap applies to the loaded set as well.
1539
- */
1540
- static restore(options?: DelegationTaskQueueOptions): Promise<DelegationTaskQueue>;
1541
- /**
1542
- * Kick off a delegation in the background. Returns immediately. The
1543
- * `taskId` is queryable via `status` once this method returns. Throws
1544
- * the recorded `DelegationPersistenceError` once the store has failed —
1545
- * the queue does not accept work it cannot journal.
1546
- */
1547
- submit<Args extends AnyDelegateArgs>(input: SubmitInput<Args>): SubmitOutput;
1548
- /**
1549
- * Snapshot the current state of a delegation. Returns `undefined` for
1550
- * unknown ids so callers can distinguish missing from terminal.
1551
- * `includeTrace` attaches the journaled loop-trace span tree — off by
1552
- * default so status polls stay light.
1553
- */
1554
- status(taskId: string, opts?: {
1555
- includeTrace?: boolean;
1556
- }): DelegationStatusResult | undefined;
1557
- /**
1558
- * Abort an in-flight delegation. Returns `false` if the task is unknown
1559
- * or already terminal. The underlying `run` function MUST honor the
1560
- * abort signal for the cancel to take effect; the queue marks the
1561
- * record `cancelled` regardless so a misbehaving runner cannot pin the
1562
- * UI on `running` forever.
1563
- */
1564
- cancel(taskId: string): boolean;
1565
- /**
1566
- * Append a feedback event to the matching delegation. Returns `false`
1567
- * when `ref` does not name a known taskId — the caller should still
1568
- * record the feedback through a different surface (artifact/outcome
1569
- * kinds are not queue-bound).
1570
- */
1571
- attachFeedback(taskId: string, snapshot: DelegationFeedbackSnapshot): boolean;
1572
- /**
1573
- * Query the recorded delegations. Returns entries newest-first (by
1574
- * `startedAt`), truncated to `limit`.
1575
- */
1576
- history(args?: DelegationHistoryArgs): DelegationHistoryEntry[];
1577
- /**
1578
- * Await every journal write issued so far. Rejects with the recorded
1579
- * `DelegationPersistenceError` when any of them failed. Call before
1580
- * handing the store's backing file to another process.
1581
- */
1582
- flush(): Promise<void>;
1583
- /** Test-only — number of in-flight (non-terminal) records. */
1584
- inflightCount(): number;
1585
- private execute;
1586
- private appendTrace;
1587
- private rehydrate;
1588
- private startResume;
1589
- private driveResume;
1590
- /**
1591
- * Journal the resumed segment of a detached run as one compact span. The
1592
- * resume driver re-attaches after a process restart, so the original
1593
- * process's loop events are gone — this span records the post-restart
1594
- * observation window (re-attach → terminal tick) under the
1595
- * `'detached-resume'` driver tag, keeping restored delegations observable
1596
- * in the journal alongside trace-carrying live runs.
1597
- */
1598
- private appendResumeSpan;
1599
- private persist;
1600
- private persistRemoval;
1601
- private failPersistence;
1602
- private enforceRetention;
1603
- }
1604
- /**
1605
- * Best-effort stable hash for use as `idempotencyKey`. Not cryptographic;
1606
- * collisions only affect dedupe, never correctness.
1607
- *
1608
- * @experimental
1609
- */
1610
- declare function hashIdempotencyInput(value: unknown): string;
1611
-
1612
- /**
1613
- *
1614
- * Detached delegation turns over the sandbox SDK's `driveTurn` primitive.
1615
- *
1616
- * Two halves of one story:
1617
- *
1618
- * - {@link runDetachedTurn} — the dispatch side. A single-session delegate
1619
- * (single-variant coder / researcher) acquires a box, binds the sandbox id
1620
- * into the record's `detachedSessionRef`, then advances the turn with
1621
- * repeated `driveTurn` ticks instead of holding a live SSE stream. The
1622
- * session id is deterministic and supplied at submit time, so a process
1623
- * crash between ticks loses nothing — the turn keeps running in the box.
1624
- *
1625
- * - {@link createDetachedTurnResumeDriver} — the resume side. A
1626
- * `DelegationResumeDriver` that re-attaches restored in-flight records to
1627
- * their detached runs: parse the record's ref, resolve the box, advance the
1628
- * turn one `driveTurn` pass per `tick()`, and map the SDK's three states
1629
- * (`completed | running | failed`) onto `DelegationResumeTick`.
1630
- *
1631
- * Both sides type the box structurally ({@link DriveTurnCapableBox}) so tests
1632
- * inject fakes and the module never requires the sandbox SDK at runtime — the
1633
- * SDK stays an optional peer, exactly like the executors' `SandboxClient` seam.
1634
- *
1635
- * Tradeoffs of detached mode (why it is opt-in, not the default): a detached
1636
- * turn yields one terminal payload instead of a live event stream, so kernel
1637
- * token/cost aggregation is not produced for that turn. The trace sinks still
1638
- * observe detached work — `runDetachedTurn` synthesizes a single-iteration
1639
- * loop event stream (see `RunDetachedTurnOptions.traceEmitter`) so the span
1640
- * topology joins the inherited trace context, with cost/tokens reported as 0
1641
- * under the `'detached-turn'` driver tag. Multi-variant fanout stays on the
1642
- * streaming `runAgentRounds` path — N concurrent sessions cannot be expressed as one
1643
- * resume key, and winner selection needs every candidate.
1644
- *
1645
- * @experimental
1646
- */
1647
-
1648
- /**
1649
- * Structural mirror of the sandbox SDK's `TurnDriveResult` (>= 0.6).
1650
- * Discriminated on `state`; `failed` is terminal and deterministic per the
1651
- * SDK contract — re-invoking with the same ids returns the same outcome.
1652
- *
1653
- * @experimental
1654
- */
1655
- type DriveTurnTick = {
1656
- state: 'completed';
1657
- text: string;
1658
- result: Record<string, unknown>;
1659
- } | {
1660
- state: 'running';
1661
- startedAt?: Date;
1662
- elapsedMs?: number;
1663
- } | {
1664
- state: 'failed';
1665
- error: string;
1666
- };
1667
- /**
1668
- * The box surface detached turns need. `SandboxInstance`
1669
- * (`@tangle-network/sandbox` >= 0.6) satisfies it structurally; tests pass
1670
- * in-memory fakes. `_sessionCancel` is the SDK's remote-cancellation surface —
1671
- * optional here because older SDKs / fakes may not expose it; when present it
1672
- * is invoked on abort so the remote run actually stops.
1673
- *
1674
- * @experimental
1675
- */
1676
- interface DriveTurnCapableBox {
1677
- driveTurn(message: string, opts: {
1678
- sessionId: string;
1679
- turnId?: string;
1680
- wallCapMs?: number;
1681
- }): Promise<DriveTurnTick>;
1682
- _sessionCancel?(id: string): Promise<void>;
1683
- }
1684
- /**
1685
- * Decoded `DelegationRecord.detachedSessionRef`. `sandboxId` is absent between
1686
- * submit and box acquisition — a record restored in that window is not
1687
- * resumable (there is no box to resume on) and the resume driver fails it
1688
- * loud rather than dispatching onto a guessed box.
1689
- *
1690
- * @experimental
1691
- */
1692
- interface DetachedSessionRefParts {
1693
- sessionId: string;
1694
- sandboxId?: string;
1695
- }
1696
- /**
1697
- * Encode ref parts into the JSON-safe string stored on the record:
1698
- * `session=<id>` before the box exists, `sandbox=<id>;session=<id>` once
1699
- * bound. Ids must not contain the `;`/`=` delimiters.
1700
- *
1701
- * @experimental
1702
- */
1703
- declare function formatDetachedSessionRef(parts: DetachedSessionRefParts): string;
1704
- /** Parse a `detachedSessionRef` string back to parts; throws `ValidationError` on malformed input. @experimental */
1705
- declare function parseDetachedSessionRef(raw: string): DetachedSessionRefParts;
1706
- /** @experimental The terminal payload of a finished detached turn. */
1707
- interface DetachedTurn {
1708
- /** Final assistant text. */
1709
- text: string;
1710
- /** The SDK's cached AgentExecutionResult-shape record for the turn. */
1711
- result: Record<string, unknown>;
1712
- }
1713
- /**
1714
- * Synthesize the terminal event array a detached turn settles through. Shaped
1715
- * so the existing event-stream output adapters (coder, researcher) parse it:
1716
- * `data.result` for adapters that read a structured terminal record, `data.text`
1717
- * for adapters that scan assistant text for the fenced result block.
1718
- *
1719
- * @experimental
1720
- */
1721
- declare function detachedTurnEvents(sessionId: string, turn: DetachedTurn): SandboxEvent[];
1722
- /** @experimental */
1723
- interface RunDetachedTurnOptions {
1724
- /** Sandbox client used to acquire the box (the delegate's executor client). */
1725
- client: SandboxClient;
1726
- /** Profile + overrides for box acquisition — same spec the streaming path uses. */
1727
- spec: AgentRunSpec<unknown>;
1728
- /** The full turn prompt; consumed by `driveTurn`'s dispatch leg. */
1729
- prompt: string;
1730
- /** Deterministic resume key, minted at submit time (`parseDetachedSessionRef(ref).sessionId`). */
1731
- sessionId: string;
1732
- /**
1733
- * Called once the box exists, with its sandbox id. Callers persist
1734
- * `formatDetachedSessionRef({ sandboxId, sessionId })` onto the record here so
1735
- * a restart can resolve the box again.
1736
- */
1737
- bindSandbox(sandboxId: string): void;
1738
- signal: AbortSignal;
1739
- report(progress: DelegationProgress): void;
1740
- /** Delay between `running` ticks (ms). Default 5000. */
1741
- tickIntervalMs?: number;
1742
- /** Wall-clock cap forwarded to `driveTurn` — the SDK cancels and fails a session past it. */
1743
- wallCapMs?: number;
1744
- /**
1745
- * Loop-trace sink. When set, the detached turn synthesizes a
1746
- * single-iteration loop span tree (`runId` = `sessionId`, driver
1747
- * `'detached-turn'`) so trace-context inheritance survives the detached
1748
- * path — the same events the streaming `runAgentRounds` path would emit, minus
1749
- * per-token telemetry: `driveTurn` yields one terminal payload, so token
1750
- * and cost figures are structurally unavailable and reported as 0 under
1751
- * this driver tag.
1752
- */
1753
- traceEmitter?: LoopTraceEmitter;
1754
- /** Physical placement stamped on the synthesized dispatch event. Default `'sibling'`. */
1755
- placement?: 'sibling' | 'fleet';
1756
- }
1757
- /**
1758
- * Dispatch one detached turn and advance it to a terminal state with
1759
- * `driveTurn` ticks. The first tick dispatches (idempotent on `sessionId`);
1760
- * subsequent ticks poll. On abort the remote session is cancelled via
1761
- * `_sessionCancel` when the box exposes it. The box is torn down on every
1762
- * in-process exit path (success, failure, abort) — only a process death skips
1763
- * teardown, which is exactly the case the resume driver re-attaches to.
1764
- *
1765
- * @experimental
1766
- */
1767
- declare function runDetachedTurn(options: RunDetachedTurnOptions): Promise<DetachedTurn>;
1768
- /** @experimental */
1769
- interface DetachedTurnResumeDriverOptions {
1770
- /**
1771
- * Resolve the live box owning a detached session. The bin wires this to the
1772
- * sandbox client's `get(sandboxId)`; throw when the box no longer exists —
1773
- * a thrown tick settles the record as failed, which is the truth.
1774
- */
1775
- resolveSandbox(sandboxId: string): Promise<DriveTurnCapableBox>;
1776
- /**
1777
- * Rebuild the turn prompt from the persisted record. Only consumed by
1778
- * `driveTurn`'s dispatch leg — i.e. when the previous process died after
1779
- * binding the box but before the session was dispatched. Must reproduce the
1780
- * prompt the delegate would have sent.
1781
- */
1782
- buildMessage(record: DelegationRecord): string;
1783
- /**
1784
- * Map a completed turn onto the delegation's typed output payload (parse +
1785
- * validate per profile). Throw when the resumed result does not pass the
1786
- * profile's gate — the queue settles the record as failed with that error.
1787
- */
1788
- settleOutput(turn: DetachedTurn, record: DelegationRecord, ctx: {
1789
- signal: AbortSignal;
1790
- }): Promise<DelegationResultPayload['output']> | DelegationResultPayload['output'];
1791
- /** Delay between `running` ticks (ms). Default 5000. */
1792
- intervalMs?: number;
1793
- /** Wall-clock cap forwarded to `driveTurn` on every tick. */
1794
- wallCapMs?: number;
1795
- }
1796
- /**
1797
- * Build the `driveTurn`-backed {@link DelegationResumeDriver}. Each `tick()`
1798
- * is one settle/poll/dispatch pass:
1799
- *
1800
- * - ref without a sandbox binding → `failed` (`DetachedSessionUnboundError`):
1801
- * the previous process died before a box existed; there is nothing to resume.
1802
- * - `driveTurn` `completed` → `settleOutput` → `completed` tick.
1803
- * - `running` → progress via `ctx.report`, `running` tick (queue re-ticks
1804
- * after `intervalMs`).
1805
- * - `failed` → `failed` tick (`DetachedTurnFailedError`) — terminal per the
1806
- * SDK's deterministic-failure contract.
1807
- *
1808
- * Abort: the queue stops ticking once `cancel()` flips the record, so remote
1809
- * cancellation is hooked onto `ctx.signal` (once per task) and fires
1810
- * `_sessionCancel` when the SDK surface exposes it. The driver never deletes
1811
- * boxes — it cannot know whether `sandboxId` is a disposable sibling or a
1812
- * fleet machine, and destroying a fleet machine would be unrecoverable.
1813
- *
1814
- * @experimental
1815
- */
1816
- declare function createDetachedTurnResumeDriver(options: DetachedTurnResumeDriverOptions): DelegationResumeDriver;
1817
-
1818
- /**
1819
- *
1820
- * Delegation executors — the layer between MCP delegates and the sandbox
1821
- * substrate. Each executor exposes a {@link SandboxClient} the kernel
1822
- * consumes plus a placement tag so the trace pipeline can correlate workers
1823
- * with their physical placement.
1824
- *
1825
- * Two implementations ship in-box:
1826
- *
1827
- * - {@link createSiblingSandboxExecutor} — every delegation spawns a fresh
1828
- * sandbox sibling to the caller. Default when the MCP server runs as a
1829
- * standalone CLI mounted outside a fleet.
1830
- *
1831
- * - {@link createFleetWorkspaceExecutor} — delegations dispatch onto machines
1832
- * in the caller's existing fleet so worker diffs land directly on the
1833
- * caller's filesystem (the fleet's shared workspace). Selected when the
1834
- * parent sandbox passes `TANGLE_FLEET_ID` into the MCP server's env.
1835
- *
1836
- * @experimental
1837
- */
1838
-
1839
- /** @experimental */
1840
- interface DelegationExecutor {
1841
- /** Sandbox client the kernel calls. Returned with `describePlacement` set. */
1842
- readonly client: SandboxClient;
1843
- /** Best-effort one-liner used in stderr boot logs and diagnostics. */
1844
- describe(): string;
1845
- /**
1846
- * Where delegated work physically runs. `sibling` and `fleet` placements are
1847
- * session-backed (boxes expose `driveTurn`, so detached dispatch + resume
1848
- * apply); `in-process` spawns local harness CLIs with no sandbox session to
1849
- * detach. Optional so consumer-implemented executors stay source-compatible;
1850
- * absent means "unknown" and detached dispatch is not enabled for it.
1851
- */
1852
- readonly placement?: 'sibling' | 'fleet' | 'in-process';
1853
- }
1854
- /** @experimental */
1855
- interface SiblingSandboxExecutorOptions {
1856
- client: SandboxClient;
1857
- }
1858
- /**
1859
- * Wrap a raw sandbox SDK client so the kernel emits
1860
- * `loop.iteration.dispatch` events with `{ placement: 'sibling', sandboxId }`.
1861
- *
1862
- * The returned client `.create()` delegates to the underlying client; the
1863
- * only added behavior is a `describePlacement` tag the kernel reads.
1864
- *
1865
- * @experimental
1866
- */
1867
- declare function createSiblingSandboxExecutor(options: SiblingSandboxExecutorOptions): DelegationExecutor;
1868
- /**
1869
- * Minimal `SandboxFleet` surface the fleet executor calls. Declared
1870
- * structurally so tests can pass an in-memory stub without instantiating the
1871
- * sandbox SDK.
1872
- *
1873
- * @experimental
1874
- */
1875
- interface FleetHandle {
1876
- readonly fleetId: string;
1877
- /** Machine ids in dispatch-eligible order. The executor round-robins. */
1878
- readonly ids: ReadonlyArray<string>;
1879
- /** Resolve a machine id to its `SandboxInstance` — that machine is mounted
1880
- * on the fleet's shared workspace, so any diff the worker writes lands on
1881
- * every other fleet machine's filesystem too. */
1882
- sandbox(machineId: string): Promise<SandboxInstance>;
1883
- }
1884
- /** @experimental */
1885
- interface FleetWorkspaceExecutorOptions {
1886
- fleet: FleetHandle;
1887
- /**
1888
- * Override the machine-selection policy. Default = round-robin across
1889
- * `fleet.ids`, skipping the optional `excludeMachineIds` set (typically the
1890
- * coordinator machine the MCP server is running on).
1891
- */
1892
- selectMachine?: (call: {
1893
- callIndex: number;
1894
- ids: ReadonlyArray<string>;
1895
- }) => string;
1896
- /**
1897
- * Machine ids to skip during default round-robin. Set to the caller's own
1898
- * machineId so workers don't compete with the orchestrator on the same VM.
1899
- */
1900
- excludeMachineIds?: ReadonlyArray<string>;
1901
- }
1902
- /**
1903
- * Build an executor that resolves each delegated iteration to an existing
1904
- * machine in `fleet`. The fleet's shared-workspace policy means the worker
1905
- * machine sees the caller's filesystem — diffs land in-place with no
1906
- * cross-sandbox copy step.
1907
- *
1908
- * @experimental
1909
- */
1910
- declare function createFleetWorkspaceExecutor(options: FleetWorkspaceExecutorOptions): DelegationExecutor;
1911
-
1912
- /**
1913
- *
1914
- * `detachedSessionDelegate` — the sandbox-session coder delegate: a closure that drives `runAgentRounds`
1915
- * against a `SandboxClient` + a caller-supplied (or minimal model-only default) worker profile, to a
1916
- * mechanically-validated `CoderOutput`. The caller invokes the returned delegate directly with its
1917
- * coder args; when wired into a durable queue it also settles cross-restart-resumed records.
1918
- *
1919
- * Delegation vs COORDINATION (`../runtime/supervise/coordination-mcp.ts`): this delegate runs a
1920
- * coding task INSIDE the agent's OWN sandbox environment — a sibling box on its own `SandboxClient`,
1921
- * fresh branch on its repo. It is NOT backend-pluggable. To instead SPAWN + live-drive workers in a
1922
- * CHOSEN backend (sandbox OR cli-bridge, via `createExecutor({ backend })`) with observe/steer/resume
1923
- * + recursion, use `delegate()` / the coordination MCP.
1924
- *
1925
- * The worker profile is a parameter the caller supplies (§1.5: the system authors profiles). When
1926
- * none is passed, a minimal model-only default profile is materialized in `./detached-coder` — no
1927
- * hardcoded skills or tools. For NEW local-repo coding use `worktreeFanout` / `worktreeLoopRunner`
1928
- * (author one `AgentProfile` per harness → `createWorktreeCliExecutor` leaves → `gateOnDeliverable`).
1929
- *
1930
- * @experimental
1931
- */
1932
-
1933
- /** @experimental */
1934
- interface DelegateRunCtx {
1935
- signal: AbortSignal;
1936
- report(progress: DelegationProgress): void;
1937
- /**
1938
- * Detached-run resume key recorded on the queue record at submit time
1939
- * (`formatDetachedSessionRef`). Present only when the submit path requested
1940
- * detached dispatch — its presence is what routes a session-backed delegate
1941
- * onto the `driveTurn` tick path instead of holding a stream.
1942
- */
1943
- detachedSessionRef?: string;
1944
- /** Rebind the record's resume key (e.g. once the sandbox id is known). */
1945
- updateDetachedSessionRef?(ref: string): void;
1946
- /**
1947
- * Per-delegation trace sink supplied by the queue — loop events emitted
1948
- * here land on the delegation record as a compact span tree. Delegates
1949
- * compose it with their configured OTEL emitter so both sinks observe
1950
- * the same stream.
1951
- */
1952
- traceEmitter?: LoopTraceEmitter;
1953
- }
1954
- /** @experimental The coder delegate closure — given the coder args + run context, drives the
1955
- * sandbox-session coder path to a validated `CoderOutput`. `detachedSessionDelegate` is the
1956
- * built-in implementation; the queue invokes one of these per coder delegation. */
1957
- type CoderDelegate = (args: DelegateCodeArgs, ctx: DelegateRunCtx) => Promise<CoderOutput>;
1958
- /**
1959
- * UI-auditor delegate — fully consumer-injected. agent-runtime ships no
1960
- * default factory because the inputs are workspace path + judge function
1961
- * + (optionally) a `SandboxClient`, and the judge is the consumer's
1962
- * model seam. See `createInProcessUiAuditClient` + `uiAuditorProfile` in
1963
- * `@tangle-network/agent-runtime/profiles` for the canonical wiring.
1964
- *
1965
- * @experimental
1966
- */
1967
- type UiAuditorDelegate = (args: DelegateUiAuditArgs, ctx: DelegateRunCtx) => Promise<UiAuditorDelegationOutput>;
1968
- /** @experimental Structured review verdict over a coder candidate. */
1969
- interface CoderReview {
1970
- /** Gate: only approved candidates are eligible to win. */
1971
- approved: boolean;
1972
- /** Reviewer's recommendation — surfaced in traces. */
1973
- recommendation: 'ship' | 'approve-with-nits' | 'changes-requested' | 'reject';
1974
- /** Readiness 0..1, used by the `highest-readiness` winner-selection strategy. */
1975
- readiness: number;
1976
- notes?: string;
1977
- }
1978
- /**
1979
- *
1980
- * Optional adversarial reviewer over a coder candidate that already passed
1981
- * mechanical validation (tests/typecheck/forbidden/diff/no-op/secrets). Folded
1982
- * from the ai-trading-blueprint delegation MCP: a candidate is only eligible to
1983
- * win if the reviewer approves it. The reviewer is the consumer's seam — an LLM
1984
- * judge, a `pnpm review` command, anything returning a `CoderReview`.
1985
- *
1986
- * @experimental
1987
- */
1988
- type CoderReviewer = (output: CoderOutput, task: CoderTask, ctx: {
1989
- signal: AbortSignal;
1990
- }) => Promise<CoderReview> | CoderReview;
1991
- /**
1992
- * @experimental Winner-selection strategy among validated (+ reviewed) candidates on the
1993
- * sandbox-session path. The base strategies (`highest-score` / `smallest-diff` /
1994
- * `first-approved`) delegate to the shared `selectValidWinner`; `highest-readiness` is the
1995
- * reviewer-only strategy this path keeps that the generic selector does not express. Default
1996
- * `highest-score`.
1997
- */
1998
- type DetachedWinnerSelection = 'highest-score' | 'smallest-diff' | 'highest-readiness' | 'first-approved';
1999
- /** @experimental */
2000
- interface DetachedSessionDelegateOptions {
2001
- /**
2002
- * Execution placement. Pass a {@link DelegationExecutor} (sibling or fleet)
2003
- * to control where worker iterations land. `sandboxClient` is a
2004
- * convenience shorthand that wraps the client in a sibling executor — pass
2005
- * one or the other, not both.
2006
- */
2007
- executor?: DelegationExecutor;
2008
- /**
2009
- * Convenience shorthand for sibling placement. Equivalent to
2010
- * `executor: createSiblingSandboxExecutor({ client: sandboxClient })`.
2011
- */
2012
- sandboxClient?: SandboxClient;
2013
- /**
2014
- * The worker's authored `AgentProfile` (§1.5: the system authors profiles). Spread onto the
2015
- * sandbox-session run spec → `runAgentRounds` → the executor's `harnessInvocation`, so the harness runs
2016
- * under the caller's stance. Omit to use a minimal model-only default (no hardcoded skills/tools);
2017
- * `harness` / `model` / `systemPrompt` below are convenience overrides layered onto whichever
2018
- * profile is used.
2019
- */
2020
- workerProfile?: AgentProfile$1;
2021
- /** Backend harness for the single-coder path (sets `metadata.backendType`). Default `claude-code`. */
2022
- harness?: string;
2023
- /** Model override for the single-coder path. */
2024
- model?: string;
2025
- /**
2026
- * The worker's authored system prompt (§1.5). Flows onto the run spec's
2027
- * `profile.prompt.systemPrompt` → through `runAgentRounds` → the executor's `harnessInvocation`, so the
2028
- * harness runs under this stance. Omit to keep the profile's own prompt.
2029
- */
2030
- systemPrompt?: string;
2031
- /** Default `['claude-code', 'codex', 'opencode/zai-coding-plan/glm-5.1']` when variants > 1. */
2032
- fanoutHarnesses?: string[];
2033
- /** Optional per-harness model override for `variants > 1`. */
2034
- fanoutModels?: (string | undefined)[];
2035
- /** Hard cap on the kernel's per-batch concurrency. Default 4. */
2036
- maxConcurrency?: number;
2037
- /**
2038
- * Optional adversarial reviewer. When set, a candidate must pass mechanical
2039
- * validation AND `reviewer.approved` to be eligible to win — empty/secret/
2040
- * test-failing patches are already gone; this catches the "compiles + passes
2041
- * but wrong/unsafe" class the deterministic validator can't see.
2042
- */
2043
- reviewer?: CoderReviewer;
2044
- /** Winner-selection strategy among eligible candidates. Default `highest-score`. */
2045
- winnerSelection?: DetachedWinnerSelection;
2046
- /**
2047
- * Loop trace emitter forwarded into every delegated `runAgentRounds`. Wire
2048
- * `createPropagatingTraceEmitter(readTraceContextFromEnv())` here (the bin
2049
- * does) so delegated build-loops export their topology spans to the OTLP /
2050
- * Tangle Intelligence sink when `OTEL_EXPORTER_OTLP_ENDPOINT` is set — and
2051
- * are a cheap no-op when it isn't. Configurable by construction.
2052
- *
2053
- * Detached single-variant turns (taken when `ctx.detachedSessionRef` is set)
2054
- * bypass `runAgentRounds`; `runDetachedTurn` synthesizes a single-iteration loop
2055
- * event stream for them so this emitter observes detached work too.
2056
- */
2057
- traceEmitter?: LoopTraceEmitter;
2058
- /** Tick cadence (ms) for the detached single-variant path. Default 5000. */
2059
- detachedTickIntervalMs?: number;
2060
- /** Wall-clock cap (ms) forwarded to `driveTurn` for detached turns. */
2061
- detachedWallCapMs?: number;
2062
- }
2063
- /**
2064
- * Build the sandbox-session coder delegate. It drives `runAgentRounds` against the project's
2065
- * sandbox client + coder profile; when `args.variants > 1` it switches to the multi-harness fanout
2066
- * topology.
2067
- *
2068
- * This is the SANDBOX-SESSION coder path: workers run the in-box harness via the
2069
- * `SandboxClient`'s `streamPrompt`, and single-variant turns can dispatch DETACHED
2070
- * (driveTurn ticks) so a durable queue resumes them across an MCP restart — a substrate
2071
- * the recursive worktree-CLI leaf does not yet have a journal-replay equivalent for.
2072
- *
2073
- * For NEW local-repo coding use `worktreeFanout` / `worktreeLoopRunner` (author an `AgentProfile`
2074
- * per harness → `createWorktreeCliExecutor` leaves → `gateOnDeliverable`). This delegate runs
2075
- * held-stream by default and only its OPTIONAL cross-restart resume (the `driveTurn` tick) is opt-in
2076
- * behind `MCP_ENABLE_DETACHED_RESUME`.
2077
- *
2078
- * @experimental
2079
- */
2080
- declare function detachedSessionDelegate(options: DetachedSessionDelegateOptions): CoderDelegate;
2081
- /**
2082
- * Canonical `DelegateCodeArgs` → `CoderTask` mapping — the single source for
2083
- * the delegate's live dispatch AND the resume driver's settle/message
2084
- * rebuilding, so a resumed record reproduces exactly the task the original
2085
- * process dispatched.
2086
- *
2087
- * @experimental
2088
- */
2089
- declare function coderTaskFromArgs(args: DelegateCodeArgs): CoderTask;
2090
- /** @experimental */
2091
- interface SettleDetachedCoderTurnOptions {
2092
- task: CoderTask;
2093
- /** Session id of the detached turn — used as the synthesized event id. */
2094
- sessionId: string;
2095
- signal: AbortSignal;
2096
- harness?: string;
2097
- model?: string;
2098
- /** Same gate as the streaming path: an unapproved candidate cannot win. */
2099
- reviewer?: CoderReviewer;
2100
- }
2101
- /**
2102
- * Settle a completed detached coder turn through the same gate the streaming
2103
- * path applies: parse the terminal payload with the coder output adapter,
2104
- * run the mechanical validator (tests/typecheck/forbidden/diff/no-op/secrets),
2105
- * then the optional reviewer. Throws when nothing survives — a resumed or
2106
- * detached run must not return an unvalidated patch.
2107
- *
2108
- * SCOPE NOTE (detached/resume): the detached `driveTurn`-tick + cross-restart resume path is
2109
- * bound to the `runAgentRounds` + sandbox-session substrate. The recursive `Scope`/worktree-CLI leaf has
2110
- * journal→replay but no driveTurn-over-a-detached-sandbox-session equivalent yet, so resume is NOT
2111
- * advertised on the generic `worktreeFanout` path. This helper (with `coderTaskFromArgs` and
2112
- * `createDetachedTurnResumeDriver`) stays as the resume seam `bin.ts` wires for in-flight records.
2113
- *
2114
- * @experimental
2115
- */
2116
- declare function settleDetachedCoderTurn(turn: DetachedTurn, options: SettleDetachedCoderTurnOptions): Promise<CoderOutput>;
2117
-
2118
- /**
2119
- *
2120
- * Feedback persistence surface for the MCP layer.
2121
- *
2122
- * Feedback storage is product policy, so the MCP layer depends on this narrow
2123
- * interface instead of choosing a knowledge store. The default implementation
2124
- * is in-memory; consumers wire their own durable adapter via
2125
- * `createMcpServer({ feedbackStore })`.
2126
- *
2127
- * Feedback events are append-only: every rating is a new event with a
2128
- * fresh id, even when the same delegation is rated multiple times. The
2129
- * caller decides how to roll up scores downstream.
2130
- *
2131
- * @experimental
2132
- */
2133
-
2134
- /** @experimental */
2135
- interface FeedbackEvent {
2136
- id: string;
2137
- refersTo: DelegateFeedbackArgs['refersTo'];
2138
- rating: DelegateFeedbackArgs['rating'];
2139
- by: DelegateFeedbackArgs['by'];
2140
- capturedAt: string;
2141
- namespace?: string;
2142
- }
2143
- /** @experimental */
2144
- interface FeedbackStore {
2145
- /** Append a new event. Never dedupes — every rating is its own event. */
2146
- put(event: FeedbackEvent): Promise<void>;
2147
- /**
2148
- * List events filtered by `namespace`. When `namespace` is omitted, list
2149
- * across all namespaces. Returns events in insertion order.
2150
- */
2151
- list(filter?: {
2152
- namespace?: string;
2153
- refersToRef?: string;
2154
- }): Promise<FeedbackEvent[]>;
2155
- }
2156
- /** In-memory `FeedbackStore` — suitable for single-process use and tests. @experimental */
2157
- declare class InMemoryFeedbackStore implements FeedbackStore {
2158
- private readonly events;
2159
- put(event: FeedbackEvent): Promise<void>;
2160
- list(filter?: {
2161
- namespace?: string;
2162
- refersToRef?: string;
2163
- }): Promise<FeedbackEvent[]>;
2164
- }
2165
- /**
2166
- * Project a `FeedbackEvent` down to the snapshot shape carried on
2167
- * `delegation_history` entries.
2168
- *
2169
- * @experimental
2170
- */
2171
- declare function eventToSnapshot(event: FeedbackEvent): DelegationFeedbackSnapshot;
2172
-
2173
- /**
2174
- *
2175
- * `delegate` MCP tool — the ONE generic delegation verb, the agent-facing front door to
2176
- * `delegate()` / `supervise()`. The agent hands it an INTENT (what it wants done); a default
2177
- * authoring supervisor decomposes the intent and AUTHORS the worker profile it needs — there is no
2178
- * hardcoded coder/researcher profile, so one verb covers code, research, and anything else.
2179
- *
2180
- * `delegate` is SYNCHRONOUS: it awaits the full supervised run and returns the delivered output
2181
- * TOGETHER WITH `spentTotal` — the conserved cost of the whole delegation (`iterations` / `tokens` /
2182
- * `usd` / `ms`), so the caller always learns what the delegation actually spent.
2183
- *
2184
- * The supervisor's substrate (its brain `router`, the worker `backend`, the completion `deliverable`)
2185
- * is INJECTED at server construction — never an agent-supplied arg. The agent supplies only the
2186
- * intent (+ an optional per-call `model` / `runId`).
2187
- *
2188
- * @experimental
2189
- */
2190
-
2191
- /** MCP tool name for the `delegate` generic-delegation tool. @experimental */
2192
- declare const DELEGATE_TOOL_NAME = "delegate";
2193
- /** Human-readable description of the `delegate` MCP tool, injected into the tool manifest. @experimental */
2194
- declare const DELEGATE_DESCRIPTION: string;
2195
- /** JSON Schema for `delegate` tool arguments (`intent` + optional `model` and `runId`). @experimental */
2196
- declare const DELEGATE_INPUT_SCHEMA: {
2197
- readonly type: "object";
2198
- readonly properties: {
2199
- readonly intent: {
2200
- readonly type: "string";
2201
- readonly description: "What you want accomplished, as an outcome. The supervisor authors the worker.";
2202
- };
2203
- readonly model: {
2204
- readonly type: "string";
2205
- readonly description: "Optional per-call override for the supervisor brain model.";
2206
- };
2207
- readonly runId: {
2208
- readonly type: "string";
2209
- readonly description: "Optional trace-correlation id for this delegation.";
2210
- };
2211
- };
2212
- readonly required: readonly ["intent"];
2213
- readonly additionalProperties: false;
2214
- };
2215
- /** Parsed `delegate` tool arguments. */
2216
- interface DelegateArgs {
2217
- intent: string;
2218
- model?: string;
2219
- runId?: string;
2220
- }
2221
- /** Parse and validate raw MCP tool input into typed `DelegateArgs`; throws `TypeError` on bad input. @experimental */
2222
- declare function validateDelegateArgs(raw: unknown): DelegateArgs;
2223
- /** The synchronous result the `delegate` tool returns to the calling agent: the delivered output (or
2224
- * the no-winner reason) PLUS the conserved spend of the whole delegation. */
2225
- type DelegateResult = {
2226
- status: 'winner';
2227
- out: unknown;
2228
- outRef: string;
2229
- spentTotal: Spend;
2230
- } | {
2231
- status: 'no-winner';
2232
- reason: string;
2233
- spentTotal: Spend;
2234
- };
2235
- /** @experimental */
2236
- interface DelegateHandlerOptions {
2237
- /** The supervisor brain's router substrate (REQUIRED — the default supervisor is router-brained). */
2238
- router: RouterConfig;
2239
- /** WHERE the authored workers run. Required for `supervise()` to spawn anything. */
2240
- backend: ExecutorConfig;
2241
- /** The completion oracle the authored workers settle against (settled ⟺ delivered). */
2242
- deliverable?: DeliverableSpec;
2243
- /** Default supervisor brain model when a call omits `model`. */
2244
- model?: string;
2245
- /** Restrict the run to this subset of models. */
2246
- allowedModels?: readonly string[];
2247
- }
2248
- /**
2249
- * Build the `delegate` tool handler. Closes over the injected supervisor substrate (`router` /
2250
- * `backend` / `deliverable`); each call routes the agent's intent to `delegate()` and returns the
2251
- * delivered output with its conserved cost.
2252
- */
2253
- declare function createDelegateHandler(options: DelegateHandlerOptions): (raw: unknown) => Promise<DelegateResult>;
2254
-
2255
- /**
2256
- *
2257
- * Stdio JSON-RPC MCP server exposing the delegation tools to sandbox
2258
- * coding-harness agents (claude-code, codex, opencode, ...): the generic
2259
- * `delegate` verb plus the queue-bound `delegate_feedback`,
2260
- * `delegation_status`, and `delegation_history`. `delegate_ui_audit` is served
2261
- * when a `uiAuditorDelegate` is wired.
2262
- *
2263
- * The server is transport-bound but topology-free: tool execution is
2264
- * delegated to handler functions composed from a queue, a feedback
2265
- * store, and the wired run delegates. Consumers wire those at
2266
- * construction time. The `agent-runtime-mcp` bin serves the generic
2267
- * `delegate` verb over a real sandbox client when `MCP_ENABLE_DELEGATE=1`.
2268
- *
2269
- * Wire protocol: line-delimited JSON-RPC 2.0 over stdio. Each line is
2270
- * one request; each response is one line. `tools/list` and `tools/call`
2271
- * mirror the MCP 2024-11-05 spec; we do not pull in
2272
- * `@modelcontextprotocol/sdk` to keep the dependency footprint zero.
2273
- *
2274
- * @experimental
2275
- */
2276
-
2277
- /** @experimental */
2278
- interface McpServerOptions {
2279
- /**
2280
- * Required to enable `delegate` — the ONE generic delegation verb. Inject the supervisor
2281
- * substrate: its brain `router`, the worker `backend`, and the completion `deliverable`. The
2282
- * supervisor AUTHORS its own worker from the agent's intent, so there is no worker profile to
2283
- * wire here.
2284
- */
2285
- delegateSupervisor?: DelegateHandlerOptions;
2286
- /**
2287
- * Required to enable delegate_ui_audit. Wire one that closes over your
2288
- * `runAgentRounds` + `uiAuditorProfile` + a `SandboxClient` (the
2289
- * canonical in-process choice is `createInProcessUiAuditClient` from
2290
- * `@tangle-network/agent-runtime/profiles`) + your vision judge.
2291
- */
2292
- uiAuditorDelegate?: UiAuditorDelegate;
2293
- /** Override the default in-memory feedback store. */
2294
- feedbackStore?: FeedbackStore;
2295
- /** Override the default in-memory task queue. */
2296
- queue?: DelegationTaskQueue;
2297
- /**
2298
- * Extra tools to serve alongside the delegation tools, for example
2299
- * `createCoordinationTools(...).tools`. Registered after the built-ins; a
2300
- * duplicate name throws so delegation tools cannot be shadowed silently.
2301
- */
2302
- extraTools?: McpToolDescriptor[];
2303
- /**
2304
- * Inherited trace identity (`readTraceContextFromEnv()`) stamped on every
2305
- * record the DEFAULT queue creates. Ignored when `queue` is supplied —
2306
- * pass `traceContext` to that queue's constructor instead.
2307
- */
2308
- traceContext?: TraceContext;
2309
- /** Server display name surfaced via `initialize`. Default `'agent-runtime-mcp'`. */
2310
- serverName?: string;
2311
- /** Server version surfaced via `initialize`. Default = the package version baked at build time. */
2312
- serverVersion?: string;
2313
- }
2314
- /** @experimental */
2315
- interface McpToolDescriptor {
2316
- name: string;
2317
- description: string;
2318
- inputSchema: Record<string, unknown>;
2319
- handler: (raw: unknown) => Promise<unknown>;
2320
- }
2321
- /** @experimental */
2322
- interface McpServer {
2323
- /** Tools currently registered (depend on which delegates were wired). */
2324
- readonly tools: ReadonlyMap<string, McpToolDescriptor>;
2325
- /** The underlying queue — exposed so tests can introspect it. */
2326
- readonly queue: DelegationTaskQueue;
2327
- /** The feedback store — exposed for the same reason. */
2328
- readonly feedbackStore: FeedbackStore;
2329
- /** Handle a single parsed JSON-RPC message. Returns the response object (or `null` for notifications). */
2330
- handle(message: JsonRpcMessage): Promise<JsonRpcResponse | null>;
2331
- /** Drive the server on a stdio-shaped transport until `stop()` is called. */
2332
- serve(transport?: McpTransport): Promise<void>;
2333
- /** Stop a `serve` call. Subsequent requests are rejected. */
2334
- stop(): void;
2335
- }
2336
- /** @experimental */
2337
- interface McpTransport {
2338
- input: NodeJS.ReadableStream;
2339
- output: NodeJS.WritableStream;
2340
- }
2341
- /** @experimental */
2342
- interface JsonRpcMessage {
2343
- jsonrpc: '2.0';
2344
- id?: number | string | null;
2345
- method: string;
2346
- params?: unknown;
2347
- }
2348
- /** @experimental */
2349
- interface JsonRpcResponse {
2350
- jsonrpc: '2.0';
2351
- id: number | string | null;
2352
- result?: unknown;
2353
- error?: {
2354
- code: number;
2355
- message: string;
2356
- data?: unknown;
2357
- };
2358
- }
2359
- /**
2360
- * Stdio JSON-RPC MCP server exposing the delegation tools (`delegate`, `delegate_feedback`, `delegation_status`, `delegation_history`, optional `delegate_ui_audit`) to sandbox coding-harness agents.
2361
- *
2362
- * @experimental
2363
- */
2364
- declare function createMcpServer(options?: McpServerOptions): McpServer;
2365
- /**
2366
- * In-process pair of `Readable` + `Writable` streams suitable for driving
2367
- * `server.serve(...)` from a test. Returns the agent-side stream (the
2368
- * client writes to it) and the server-side stream (the test reads from it).
2369
- *
2370
- * @experimental
2371
- */
2372
- declare function createInProcessTransport(): {
2373
- transport: McpTransport;
2374
- clientWrite(line: string): void;
2375
- clientClose(): void;
2376
- readServer(): Promise<JsonRpcResponse[]>;
2377
- };
2378
-
2379
- /**
2380
- *
2381
- * MCP binding for a live `Scope`. A sandbox driver gets the same small verbs
2382
- * the in-process driver has: spawn, observe, await, steer, ask/answer, analyze,
2383
- * and stop. Settled outputs remain Scope artifacts; product code can project
2384
- * them into any UI/report envelope it needs.
2385
- *
2386
- * @experimental
2387
- */
2388
-
2389
- /** A worker the driver has drained via `await_event`. */
2390
- interface SettledWorker {
2391
- readonly id: string;
2392
- readonly status: 'done' | 'down';
2393
- readonly score?: number;
2394
- readonly valid?: boolean;
2395
- readonly outRef?: string;
2396
- readonly reason?: string;
2397
- /** Epoch ms the ledger recorded this settlement — the resolution a progress-based stop rule
2398
- * needs to answer "how long since anything landed?" without inventing a timestamp at read
2399
- * time. Stamped when the cursor yields the settlement, not when a reader first looks. */
2400
- readonly settledAt?: number;
2401
- }
2402
- type QuestionLevel = 'worker' | 'driver' | 'loop';
2403
- type QuestionUrgency = 'continue-without' | 'blocks-step' | 'blocks-run';
2404
- interface QuestionOption {
2405
- readonly label: string;
2406
- readonly tradeoff: string;
2407
- }
2408
- interface Question {
2409
- readonly id: string;
2410
- readonly from: string;
2411
- readonly level: QuestionLevel;
2412
- readonly question: string;
2413
- readonly reason: string;
2414
- readonly urgency: QuestionUrgency;
2415
- readonly options?: ReadonlyArray<QuestionOption>;
2416
- }
2417
- type QuestionDecision = {
2418
- readonly kind: 'answer';
2419
- readonly answer: string;
2420
- readonly by: string;
2421
- } | {
2422
- readonly kind: 'defer';
2423
- readonly reason: string;
2424
- } | {
2425
- readonly kind: 'escalate';
2426
- readonly to: 'parent' | 'user' | string;
2427
- readonly reason: string;
2428
- };
2429
- interface QuestionRecord extends Question {
2430
- readonly status: 'open' | 'answered' | 'deferred' | 'escalated';
2431
- readonly decision?: QuestionDecision;
2432
- readonly openedAt: number;
2433
- }
2434
- type QuestionPolicy = 'auto' | 'mustDecide' | 'bubble' | 'failClosed';
2435
- interface AnalystRegistry {
2436
- readonly kinds: ReadonlyArray<{
2437
- id: string;
2438
- description: string;
2439
- area: string;
2440
- }>;
2441
- readonly run: (kindId: string, trace: unknown) => Promise<unknown>;
2442
- }
2443
- /** A trace-analyst result re-entered as a message on the bus (the `finding` event kind). */
2444
- interface AnalystFindingEvent {
2445
- readonly fromWorker: string;
2446
- readonly analyst: string;
2447
- readonly findings: unknown;
2448
- }
2449
- /** A parent→child message (the down-leg): recorded for observability, delivered via the child inbox,
2450
- * never pulled back by the parent. `delivered` mirrors whether the live child accepted it. */
2451
- interface DownMessageEvent {
2452
- readonly toWorker: string;
2453
- readonly instruction: string;
2454
- readonly delivered: boolean;
2455
- }
2456
- /** Every message on the one typed pipe. UP (child→parent): question / settled / finding — queued for
2457
- * the driver to `pull`. DOWN (parent→child): steer / answer — record-only (history + subscribers),
2458
- * routed to the child inbox. New kinds are additive. */
2459
- type CoordinationEvent = {
2460
- readonly type: 'question';
2461
- readonly question: QuestionRecord;
2462
- } | {
2463
- readonly type: 'settled';
2464
- readonly worker: SettledWorker;
2465
- } | {
2466
- readonly type: 'finding';
2467
- readonly finding: AnalystFindingEvent;
2468
- } | {
2469
- readonly type: 'steer';
2470
- readonly down: DownMessageEvent;
2471
- } | {
2472
- readonly type: 'answer';
2473
- readonly down: DownMessageEvent;
2474
- readonly questionId: string;
2475
- };
2476
- type MakeWorkerAgent = (profile: unknown) => Agent<unknown, unknown>;
2477
- interface CoordinationToolsOptions {
2478
- readonly scope: Scope<unknown>;
2479
- readonly blobs: ResultBlobStore;
2480
- readonly makeWorkerAgent: MakeWorkerAgent;
2481
- readonly perWorker: Budget;
2482
- readonly analysts?: AnalystRegistry;
2483
- readonly onEvent?: (event: CoordinationEvent) => void | Promise<void>;
2484
- readonly questionPolicy?: QuestionPolicy;
2485
- /** Analyst kind ids to run AUTOMATICALLY when a worker settles `done` (the analyst-on-settle
2486
- * hook). Each result is published as a `finding` event on the bus — pass-through to subscribers
2487
- * and queued for the driver to pull via `await_event`. Omit/empty = no auto-analysis (default;
2488
- * the driver can still run lenses on demand via `run_analyst`). Requires `analysts`. */
2489
- readonly analyzeOnSettle?: ReadonlyArray<string>;
2490
- /** Hard cap on how many workers may be LIVE (spawned but not yet settled) at once. `spawn_agent`
2491
- * counts the scope's non-terminal nodes and fails closed (`error: 'max-live-workers'`) BEFORE
2492
- * reserving from the pool when the cap is already met — a concurrency fence on top of the
2493
- * conserved-budget fence (the pool bounds total work; this bounds simultaneous work, e.g. live
2494
- * sandboxes/boxes). Omit or `<= 0` = no cap (the prior behavior; the pool stays the only fence). */
2495
- readonly maxLiveWorkers?: number;
2496
- /** Max wall-clock ms a single `await_event` call may block waiting on a live worker to settle
2497
- * before it returns a non-error `{ pending: true, live }` snapshot and lets the caller re-poll.
2498
- * The underlying `scope.next()` blocks for the WHOLE (multi-minute) worker run; over a remote MCP
2499
- * transport that block outlives the client's per-request timeout, so an unbounded await surfaces
2500
- * to the supervisor as a hard tool ERROR on every call — the exact failure that leaves it flying
2501
- * blind. Bounding the wait converts that error into a re-pollable liveness signal. The background
2502
- * drain keeps running, so a settlement that lands after the bound is published to the bus and
2503
- * pulled by the next call — nothing is lost. Omit = {@link DEFAULT_AWAIT_EVENT_TIMEOUT_MS}; `<= 0`
2504
- * restores the prior UNBOUNDED block (only safe for in-process drivers with no transport timeout). */
2505
- readonly awaitTimeoutMs?: number;
2506
- /**
2507
- * OPT-IN: run the ONLINE detector panel over each spawned worker's live tool trace and raise a
2508
- * `finding` on the bus the moment a detector fires — so the driver learns "this worker is
2509
- * looping" mid-run, from `await_event`, instead of at settle.
2510
- *
2511
- * This closes the `watchTrace` → `raiseFinding` wire whose own docstring already described it
2512
- * ("the seam an ONLINE detector uses to tell the driver 'this worker is looping/erroring' the
2513
- * moment it happens") but which nothing connected. Workers whose executor exposes no
2514
- * `traceSource` are simply not watched; nothing fails.
2515
- *
2516
- * Omit = no online watching (the settle-time analysts are unaffected).
2517
- */
2518
- readonly watchWorkers?: WorkerWatchOptions;
2519
- /**
2520
- * How long a worker may go without metered activity before `observe_agent` reports it as
2521
- * `stalled`. A derived read at observation time, never a background watchdog — nothing is
2522
- * killed or retried. Omit = the runtime default.
2523
- */
2524
- readonly stallAfterMs?: number;
2525
- }
2526
- /** Online-detector wiring for spawned workers (`CoordinationToolsOptions.watchWorkers`). */
2527
- interface WorkerWatchOptions {
2528
- /** Detector panel; omit for the default stuck-loop + error-streak pair. */
2529
- readonly detectors?: WatchTraceOptions['detectors'];
2530
- /** Raise at most this many findings per worker, so one pathological worker cannot flood the
2531
- * driver's inbox with the same signal every span. Default 3; `<= 0` = unlimited. */
2532
- readonly maxFindingsPerWorker?: number;
2533
- }
2534
- /**
2535
- * The supervisor-side toolbox returned by {@link createCoordinationTools}: the MCP tool
2536
- * descriptors a driver `AgentProfile` calls to spawn, steer, observe, and settle workers
2537
- * over a live `Scope`, plus the typed accessors (`settled`/`questions`/`history`/`stats`/
2538
- * `raiseFinding`) for the bidirectional coordination bus. This is the live, backend-of-your-
2539
- * choice, steerable counterpart to the one-shot own-sandbox delegation MCP.
2540
- */
2541
- interface CoordinationTools {
2542
- readonly tools: McpToolDescriptor[];
2543
- isStopped(): boolean;
2544
- stopReason(): string | undefined;
2545
- settled(): ReadonlyArray<SettledWorker>;
2546
- questions(): ReadonlyArray<QuestionRecord>;
2547
- /** The full ordered log of every bus event — UP (settled / question / finding) and DOWN
2548
- * (steer / answer) — the observability audit + replay trail. Each record carries seq,
2549
- * timestamp, and priority. */
2550
- history(): ReadonlyArray<BusRecord<CoordinationEvent>>;
2551
- /** Bus throughput counters (published / pulled / by-kind) for live dashboards. */
2552
- stats(): BusStats;
2553
- /** Raise a `finding` on the bus from outside the settle hook — the seam an ONLINE detector
2554
- * (mid-run, on the worker pipe) uses to tell the driver "this worker is looping/erroring" the
2555
- * moment it happens, instead of only at settle. Queued for `await_event` + pass-through. */
2556
- raiseFinding(finding: AnalystFindingEvent): Promise<void>;
2557
- /**
2558
- * Post-loop drain: pull every ALREADY-settled, unpulled child into the ledger (publishing each
2559
- * as a `settled` bus event for the audit trail) WITHOUT awaiting live children. The driver
2560
- * calls this once its brain loop ends, so a delivered child the brain never awaited still
2561
- * reaches `finalizeBestDelivered` — a gate-verified delivery must never be lost to the
2562
- * driver's pull discipline. Analyst-on-settle hooks do NOT fire here (the driver has stopped;
2563
- * nobody is left to read a finding, and analysts spend real compute). Returns the count.
2564
- */
2565
- drainResolved(): Promise<number>;
2566
- }
2567
- /** Build the driver's MCP tools over a live scope. */
2568
- declare function createCoordinationTools(opts: CoordinationToolsOptions): CoordinationTools;
2569
-
2570
- export { DelegationStateCorruptError as $, type AnalystRegistry as A, type DelegateResearchArgs as B, type CappedDelegationTrace as C, type DelegationExecutor as D, type ExecutorConfig as E, type FleetHandle as F, type DelegateResearchConfig as G, type DelegateResearchResult as H, type DelegateResult as I, type DelegateRunCtx as J, type DelegateUiAuditConfig as K, type DelegateUiAuditRoute as L, type MakeWorkerAgent as M, type DelegationError as N, type DelegationFeedbackSnapshot as O, type DelegationHistoryEntry as P, DelegationPersistenceError as Q, type DelegationProfile as R, type DelegationProgress as S, type DelegationRecord as T, type UiAuditorDelegate as U, type DelegationResultPayload as V, type WorkerWatchOptions as W, type DelegationResumeContext as X, type DelegationResumeDriver as Y, type DelegationResumeTick as Z, type DelegationRunContext as _, DelegationTaskQueue as a, traceContextToEnv as a$, type DelegationStatus as a0, type DelegationStore as a1, type DelegationTaskQueueOptions as a2, type DelegationTraceCaps as a3, type DelegationTraceCollector as a4, type DelegationTraceSpan as a5, type DetachedSessionDelegateOptions as a6, type DetachedSessionRefParts as a7, type DetachedTurn as a8, type DetachedTurnResumeDriverOptions as a9, type SiblingSandboxExecutorOptions as aA, type SubmitInput as aB, type SubmitOutput as aC, type TraceContext as aD, type UiAuditorDelegationOutput as aE, buildDelegationTraceSpans as aF, capDelegationTrace as aG, coderTaskFromArgs as aH, composeLoopTraceEmitters as aI, createCoordinationTools as aJ, createDelegateHandler as aK, createDelegationTraceCollector as aL, createDetachedTurnResumeDriver as aM, createFleetWorkspaceExecutor as aN, createInProcessTransport as aO, createMcpServer as aP, createPropagatingTraceEmitter as aQ, createSiblingSandboxExecutor as aR, detachedSessionDelegate as aS, detachedTurnEvents as aT, eventToSnapshot as aU, formatDetachedSessionRef as aV, hashIdempotencyInput as aW, parseDetachedSessionRef as aX, readTraceContextFromEnv as aY, runDetachedTurn as aZ, settleDetachedCoderTurn as a_, type DetachedWinnerSelection as aa, type DriveTurnCapableBox as ab, type DriveTurnTick as ac, type FeedbackEvent as ad, type FeedbackRating as ae, type FeedbackRefersTo as af, FileDelegationStore as ag, type FileDelegationStoreOptions as ah, type FleetWorkspaceExecutorOptions as ai, InMemoryDelegationStore as aj, InMemoryFeedbackStore as ak, type JsonRpcMessage as al, type JsonRpcResponse as am, type McpServer as an, type McpServerOptions as ao, type McpToolDescriptor as ap, type McpTransport as aq, type Question as ar, type QuestionDecision as as, type QuestionPolicy as at, type QuestionRecord as au, type ResearchOutputShape as av, type ResearchSource as aw, type RunDetachedTurnOptions as ax, type SettleDetachedCoderTurnOptions as ay, type SettledWorker as az, type FeedbackStore as b, validateDelegateArgs as b0, type EvalRunEvent as b1, type EvalRunGeneration as b2, type EvalRunsExportConfig as b3, type EvalRunsExportResult as b4, INTELLIGENCE_WIRE_VERSION as b5, type LoopSpanNode as b6, type OtelAttribute as b7, type OtelExportConfig as b8, type OtelExporter as b9, createInbox as bA, createSteerableSandboxSession as bB, defaultToolDetectors as bC, piExecutor as bD, piSeamKey as bE, watchTrace as bF, type OtelSpan as ba, type RuntimeEventOtelOptions as bb, buildLoopOtelSpans as bc, buildLoopSpanNodes as bd, buildRuntimeEventOtelSpans as be, createOtelExporter as bf, exportEvalRuns as bg, loopEventToOtelSpan as bh, type BusEvent as bi, type BusRecord as bj, type BusStats as bk, DEFAULT_SANDBOX_STEERING_MAX_TURNS as bl, type EventBus as bm, type Inbox as bn, type InboxMessage as bo, PI_RUNTIME as bp, type PiSeam as bq, type ProviderSeam as br, type PublishOptions as bs, type SandboxSteeringOptions as bt, type SteerableSandboxSession as bu, type WatchTraceOptions as bv, cliWorktreeExecutor as bw, createEventBus as bx, createExecutor as by, createExecutorRegistry as bz, type DelegateFeedbackResult as c, type DelegateFeedbackArgs as d, type DelegateUiAuditArgs as e, type DelegateUiAuditResult as f, type DelegationHistoryResult as g, type DelegationHistoryArgs as h, type DelegationStatusResult as i, type DelegationStatusArgs as j, type CoderDelegate as k, type CoderReview as l, type CoderReviewer as m, type CoordinationEvent as n, type CoordinationTools as o, type CoordinationToolsOptions as p, DELEGATE_DESCRIPTION as q, DELEGATE_INPUT_SCHEMA as r, DELEGATE_TOOL_NAME as s, DELEGATION_TRACE_MAX_BYTES as t, DELEGATION_TRACE_MAX_SPANS as u, type DelegateArgs as v, type DelegateCodeArgs as w, type DelegateCodeConfig as x, type DelegateCodeResult as y, type DelegateHandlerOptions as z };