@tangle-network/agent-runtime 0.71.1 → 0.73.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 (66) hide show
  1. package/README.md +8 -8
  2. package/dist/agent.d.ts +27 -17
  3. package/dist/agent.js +21 -9
  4. package/dist/agent.js.map +1 -1
  5. package/dist/analyst-loop.d.ts +1 -1
  6. package/dist/analyst-loop.js +3 -2
  7. package/dist/{chunk-4KGQHS7U.js → chunk-7ODB76J5.js} +2 -18
  8. package/dist/chunk-7ODB76J5.js.map +1 -0
  9. package/dist/chunk-HPYWEFVY.js +4039 -0
  10. package/dist/chunk-HPYWEFVY.js.map +1 -0
  11. package/dist/{chunk-P5OKDSLB.js → chunk-IODKUOBA.js} +5 -3
  12. package/dist/{chunk-P5OKDSLB.js.map → chunk-IODKUOBA.js.map} +1 -1
  13. package/dist/chunk-NBV35BR6.js +68 -0
  14. package/dist/chunk-NBV35BR6.js.map +1 -0
  15. package/dist/{chunk-5ISW5JUF.js → chunk-NCH4XUZ7.js} +2004 -6551
  16. package/dist/chunk-NCH4XUZ7.js.map +1 -0
  17. package/dist/chunk-PBE35ULD.js +52 -0
  18. package/dist/chunk-PBE35ULD.js.map +1 -0
  19. package/dist/{chunk-VLF5RHEQ.js → chunk-T2HVQVB4.js} +1 -66
  20. package/dist/chunk-T2HVQVB4.js.map +1 -0
  21. package/dist/{chunk-K3RM4MPM.js → chunk-U56XGKVY.js} +9 -24
  22. package/dist/chunk-U56XGKVY.js.map +1 -0
  23. package/dist/chunk-UZ5SODU7.js +29 -0
  24. package/dist/chunk-UZ5SODU7.js.map +1 -0
  25. package/dist/{delegates-CsXJPZDH.d.ts → coordination-DU0saWeg.d.ts} +875 -1099
  26. package/dist/index.d.ts +7 -8
  27. package/dist/index.js +10 -12
  28. package/dist/index.js.map +1 -1
  29. package/dist/intelligence.d.ts +1 -1
  30. package/dist/lifecycle.d.ts +281 -0
  31. package/dist/lifecycle.js +174 -0
  32. package/dist/lifecycle.js.map +1 -0
  33. package/dist/{loop-runner-bin-DLM_bVQO.d.ts → loop-runner-bin-eD3m0rHW.d.ts} +10 -27
  34. package/dist/loop-runner-bin.d.ts +5 -6
  35. package/dist/loop-runner-bin.js +6 -6
  36. package/dist/loops.d.ts +47 -10
  37. package/dist/loops.js +47 -42
  38. package/dist/mcp/bin.js +17 -329
  39. package/dist/mcp/bin.js.map +1 -1
  40. package/dist/mcp/index.d.ts +10 -190
  41. package/dist/mcp/index.js +949 -47
  42. package/dist/mcp/index.js.map +1 -1
  43. package/dist/{openai-tools-kdCS-T12.d.ts → openai-tools-CBurv8Cu.d.ts} +11 -13
  44. package/dist/otel-export-BKmNwiCb.d.ts +180 -0
  45. package/dist/profiles.d.ts +3 -3
  46. package/dist/profiles.js +1 -5
  47. package/dist/profiles.js.map +1 -1
  48. package/dist/{substrate-BoRXgvka.d.ts → substrate-rNj6TDc3.d.ts} +7 -23
  49. package/dist/{types-CdnEAE3U.d.ts → types-JufmXF2a.d.ts} +80 -1
  50. package/dist/worktree-DaxOvw-C.d.ts +702 -0
  51. package/dist/{worktree-fanout-CK2ypmEm.d.ts → worktree-fanout-DIffZohV.d.ts} +15 -5
  52. package/package.json +6 -1
  53. package/dist/chunk-4KGQHS7U.js.map +0 -1
  54. package/dist/chunk-5ISW5JUF.js.map +0 -1
  55. package/dist/chunk-74UAWZXE.js +0 -278
  56. package/dist/chunk-74UAWZXE.js.map +0 -1
  57. package/dist/chunk-DVQGYDN5.js +0 -59
  58. package/dist/chunk-DVQGYDN5.js.map +0 -1
  59. package/dist/chunk-INXDNX2W.js +0 -475
  60. package/dist/chunk-INXDNX2W.js.map +0 -1
  61. package/dist/chunk-K3RM4MPM.js.map +0 -1
  62. package/dist/chunk-VLF5RHEQ.js.map +0 -1
  63. package/dist/chunk-XRYEZPR6.js +0 -88
  64. package/dist/chunk-XRYEZPR6.js.map +0 -1
  65. package/dist/coordination-BPQmuwv8.d.ts +0 -666
  66. package/dist/delegation-profile-Bvfro2m1.d.ts +0 -99
@@ -0,0 +1,702 @@
1
+ import { ChildProcess } from 'node:child_process';
2
+ import { DefaultVerdict } from '@tangle-network/agent-eval';
3
+ import { AgentProfile } from '@tangle-network/agent-interface';
4
+ import { BackendType } from '@tangle-network/sandbox';
5
+ import { L as LoopTokenUsage, b as RuntimeHooks } from './types-JufmXF2a.js';
6
+
7
+ /**
8
+ * @experimental
9
+ *
10
+ * Subprocess wrappers for the local coding-harness CLIs installed in the
11
+ * sandbox image (claude-code, codex, opencode). Used by the in-process
12
+ * delegation executor (`createInProcessExecutor`) so a `delegate_code` call
13
+ * spawns a real harness on a real git worktree instead of provisioning a
14
+ * sibling sandbox.
15
+ *
16
+ * All harness invocations:
17
+ * - run with `cwd` set to the worktree
18
+ * - inherit env from the parent (the MCP server inside the sandbox has
19
+ * the harness's auth already)
20
+ * - capture stdout/stderr
21
+ * - support cancellation via AbortSignal
22
+ * - enforce a wall-clock timeout
23
+ */
24
+
25
+ /** Local coding harness available inside the sandbox. */
26
+ type LocalHarness = 'claude' | 'codex' | 'opencode';
27
+ /** @experimental */
28
+ interface RunLocalHarnessOptions {
29
+ harness: LocalHarness;
30
+ /** Working directory for the subprocess (typically a worktree path). */
31
+ cwd: string;
32
+ /** Prompt forwarded as the harness CLI's task argument. */
33
+ taskPrompt: string;
34
+ /**
35
+ * Pre-built command + args (e.g. from `harnessInvocation` so the full authored
36
+ * `AgentProfile` — systemPrompt + model — reaches the harness). When set it OVERRIDES the
37
+ * default prompt-only `buildArgs(taskPrompt)` path; `command` defaults to the harness's
38
+ * default binary when only `args` is supplied. When absent the legacy prompt-only shape
39
+ * is used unchanged.
40
+ */
41
+ invocation?: {
42
+ command?: string;
43
+ args: ReadonlyArray<string>;
44
+ };
45
+ /** Wall-clock kill deadline (ms). Default 5 min. Subprocess SIGTERMed on expiry. */
46
+ timeoutMs?: number;
47
+ /** Caller cancellation. SIGTERM is sent on abort. */
48
+ signal?: AbortSignal;
49
+ /** Override env (defaults to inheriting from the parent). */
50
+ env?: NodeJS.ProcessEnv;
51
+ /**
52
+ * Test seam — inject a custom spawner so unit tests can mock the
53
+ * subprocess without touching the OS. Defaults to node's `child_process.spawn`.
54
+ */
55
+ spawn?: (command: string, args: ReadonlyArray<string>, opts: {
56
+ cwd: string;
57
+ env: NodeJS.ProcessEnv;
58
+ stdio: 'pipe';
59
+ }) => ChildProcess;
60
+ }
61
+ /** @experimental */
62
+ interface LocalHarnessResult {
63
+ /** OS exit code. `null` when killed before exit. */
64
+ exitCode: number | null;
65
+ /** Concatenated stdout. */
66
+ stdout: string;
67
+ /** Concatenated stderr. */
68
+ stderr: string;
69
+ /** Set when the process exited via signal (timeout / abort). */
70
+ killedBySignal: NodeJS.Signals | null;
71
+ /** Wall-clock duration ms (spawn → exit). */
72
+ durationMs: number;
73
+ /** Set when timeoutMs elapsed before exit. */
74
+ timedOut: boolean;
75
+ }
76
+ /**
77
+ * Spawn a local coding harness CLI as a subprocess + collect its output.
78
+ *
79
+ * NOT responsible for parsing the harness's output or extracting a diff —
80
+ * the in-process executor's `streamPrompt` orchestrates `git diff` against
81
+ * the worktree after this resolves. This function is intentionally narrow:
82
+ * spawn, wait, capture, return.
83
+ *
84
+ * Fails loud — throws when:
85
+ * - `cwd` doesn't exist (subprocess emits ENOENT; surfaced as Error)
86
+ * - the harness binary is not on PATH (ENOENT)
87
+ *
88
+ * Does NOT throw when:
89
+ * - the subprocess exits non-zero (`result.exitCode` carries the code)
90
+ * - the subprocess is aborted / timed out (`result.killedBySignal` /
91
+ * `result.timedOut` carries the reason)
92
+ *
93
+ * @experimental
94
+ */
95
+ declare function runLocalHarness(options: RunLocalHarnessOptions): Promise<LocalHarnessResult>;
96
+
97
+ /**
98
+ * @experimental
99
+ *
100
+ * Recursive execution atom — the FROZEN type surface (the keystone contract).
101
+ *
102
+ * One self-similar `Agent` atom runs inside a budget-conserving reactive `Scope`,
103
+ * orchestrated by a `Supervisor` over an event-sourced `SpawnJournal`. A leaf is an
104
+ * `Agent` that never calls `scope.spawn`; a driver is an `Agent` that spawns and runs
105
+ * a policy over its children's streaming results.
106
+ *
107
+ * Two invariants the surface exists to make enforceable:
108
+ * - Budget is an atomically-reserved CONSERVED pool, so `Σk(treatment) ≡ Σk(blind)` by
109
+ * construction (reserve-on-spawn, refund-unspent-on-settle, fail-closed admission).
110
+ * - The journal records a content-addressed `outRef` per child result, so replay
111
+ * rehydrates the exact `Settled` the driver branched on (the replay invariant below).
112
+ *
113
+ * The leaf RUNTIME is one OPEN `Executor` interface, not a closed `inline|sandbox|cli`
114
+ * union the call site switches on. The built-ins (router/inline, sandbox, cli) are the
115
+ * initial IMPLEMENTATIONS; any user agent is first-class the moment it implements the
116
+ * interface. The interface IS the extension point — no per-vendor adapters live here.
117
+ *
118
+ * Layering: substrate types (`DefaultVerdict`) come from `@tangle-network/agent-eval`;
119
+ * runtime-shaped types (everything else) live here. Pure types/interfaces only — this
120
+ * module typechecks standalone and is imported by every keystone impl.
121
+ */
122
+
123
+ /**
124
+ * One self-similar atom. A leaf is an `Agent` that never calls `scope.spawn`; a driver
125
+ * is an `Agent` whose `act` spawns children and reacts to them via `scope.next()`. An
126
+ * analyst is an `Agent` whose task is "read these traces → findings" — `where` it runs
127
+ * is its executor, not a separate type.
128
+ *
129
+ * `act` MUST be replay-safe: it may read `verdict`, `spent`, and `out` (rehydrated by
130
+ * `outRef`) off each `Settled`; it MUST NOT read `Date.now`, `Math.random`, or any
131
+ * unordered collection. `scope.next()` delivers strictly in recorded `seq` order.
132
+ */
133
+ interface Agent<Task, Out> {
134
+ readonly name: string;
135
+ act(task: Task, scope: Scope<Out>): Promise<Out>;
136
+ }
137
+ /**
138
+ * The leaf runtime — ONE open interface, not a closed union. `execute` returns a
139
+ * `Promise<ExecutorResult>` for one-shot executors OR an `AsyncIterable<UsageEvent>` for
140
+ * streaming ones; a streaming executor reports incremental normalized usage as it runs
141
+ * (the budget pool reconciles against it) and exposes its terminal artifact via
142
+ * `resultArtifact()`. Both shapes normalize usage to `UsageEvent` so the conserved pool
143
+ * meters every runtime identically.
144
+ *
145
+ * Built-in implementations (in `runtime.ts`, NOT variants here): router/inline (a direct
146
+ * Router/HTTP inference call, no box), sandbox (COMPOSES `runLoop` as a leaf, forwarding
147
+ * PR #150's optional `lineage` passthrough — does NOT reinvent checkpoint/fork), cli
148
+ * (Halo/RLM subprocess; `budgetExempt`, excluded from equal-k by construction). A user's
149
+ * own agent (mastra/agno/raw HTTP/anything) is first-class by implementing this interface.
150
+ */
151
+ interface Executor<Out> {
152
+ /** Stable runtime tag for traces + the equal-k exemption check. */
153
+ readonly runtime: Runtime;
154
+ /**
155
+ * When true, this executor's spend is NOT metered against the conserved pool and its
156
+ * iterations are excluded from the equal-k assertion (a `cli` subprocess without
157
+ * token accounting). Fail-loud everywhere else: a metered executor MUST report usage.
158
+ */
159
+ readonly budgetExempt?: boolean;
160
+ /**
161
+ * One-shot → resolves a `ExecutorResult`; streaming → yields incremental `UsageEvent`s and
162
+ * the terminal artifact is read from `resultArtifact()` after the stream drains.
163
+ * `signal` is the spawn-scoped abort (chains the acquire lifecycle for sandbox).
164
+ */
165
+ execute(task: unknown, signal: AbortSignal): Promise<ExecutorResult<Out>> | AsyncIterable<UsageEvent>;
166
+ /**
167
+ * Optional inbox: receive an out-of-band message from the driver mid-run (the `send`/`steer_agent`
168
+ * verb). A streaming executor drains pending messages between turns and folds them into the next
169
+ * step (a steer / interrupt / resume). A one-shot executor that can't be steered mid-flight omits
170
+ * this; `Scope.send` then returns `false` for it. Never throws — a malformed message is the
171
+ * executor's to ignore.
172
+ */
173
+ deliver?(msg: unknown): void;
174
+ /**
175
+ * Tear the executor's resources down. `grace` mirrors the OTP shutdown spec
176
+ * (`'brutalKill'` = immediate, a number = ms grace, `'infinity'` = await clean exit).
177
+ */
178
+ teardown(grace: number | 'brutalKill' | 'infinity'): Promise<{
179
+ destroyed: boolean;
180
+ }>;
181
+ /**
182
+ * The replay source (B1): the content-addressed `outRef` + the materialized output the
183
+ * driver branched on, its verdict, and the conserved spend. Read once, after settle.
184
+ */
185
+ resultArtifact(): {
186
+ outRef: string;
187
+ out: Out;
188
+ verdict?: DefaultVerdict;
189
+ spent: Spend;
190
+ };
191
+ /**
192
+ * A driver-executor's OWN-inference subtree total (rolled up from its nested tree's `metered`
193
+ * events) — the parent scope journals it as a `metered` event for this node on settle, on BOTH
194
+ * the done AND the down/crash paths, so a crashed sub-driver's partial inference still re-homes
195
+ * (the pool already debited it via `observe`; the journal must match). NOT reconciled, so it never
196
+ * trips the reservation clamp. Read on settle, valid after `execute` resolves OR throws. Leaf
197
+ * executors omit it (returns `undefined`).
198
+ */
199
+ metered?(): Spend | undefined;
200
+ }
201
+ /** Terminal artifact of a one-shot `Executor.execute`. */
202
+ interface ExecutorResult<Out> {
203
+ outRef: string;
204
+ out: Out;
205
+ verdict?: DefaultVerdict;
206
+ spent: Spend;
207
+ }
208
+ /**
209
+ * Normalized usage event — the single channel every executor reports through, so the
210
+ * conserved pool meters all runtimes identically. `tokens` carries `LoopTokenUsage`'s
211
+ * `{ input, output }`; `usd` is a SEPARATE channel (never folded into tokens).
212
+ */
213
+ type UsageEvent = {
214
+ kind: 'tokens';
215
+ input: number;
216
+ output: number;
217
+ } | {
218
+ kind: 'cost';
219
+ usd: number;
220
+ } | {
221
+ kind: 'iteration';
222
+ };
223
+ /** The runtime tag of a `Executor` impl. Open by intent — `string` so a BYO executor
224
+ * names its own runtime; the built-ins use these literals. */
225
+ type Runtime = 'router' | 'inline' | 'sandbox' | 'cli' | (string & {});
226
+ /**
227
+ * `AgentProfile` does NOT carry a `harness`/backend field — `harness` lives on the
228
+ * sandbox SDK's `BackendConfig`, not the portable profile. So an agent is mapped to its
229
+ * executor through this MINIMAL wrapper, never by fabricating a field onto `AgentProfile`.
230
+ *
231
+ * Resolution (in `runtime.ts`):
232
+ * - `executor` present → BYO: use it verbatim (a user's own `Executor`).
233
+ * - `harness === null` → router/inline: a direct Router call, no box.
234
+ * - `harness` is a `BackendType` → sandbox: compose `runLoop` against `profile` on that backend.
235
+ * Fail loud on an unresolvable spec (no executor and an unknown harness).
236
+ */
237
+ interface AgentSpec {
238
+ readonly profile: AgentProfile;
239
+ /** `null` selects router/inline; a `BackendType` selects the sandboxed harness. */
240
+ readonly harness: BackendType | null;
241
+ /** Bring-your-own executor: when set, overrides harness-based resolution entirely. */
242
+ readonly executor?: Executor<unknown>;
243
+ }
244
+ /**
245
+ * Builds a fresh `Executor` for one spawn from the resolved spec. Per-spawn (not
246
+ * shared) so each child owns its own box/abort/teardown lifecycle. A BYO factory lets a
247
+ * user supply construction args without pre-instantiating.
248
+ */
249
+ type ExecutorFactory<Out> = (spec: AgentSpec, ctx: ExecutorContext) => Executor<Out>;
250
+ /** Construction context handed to a `ExecutorFactory` — the seams a built-in needs
251
+ * (sandbox client for the sandbox executor, router config for router/inline) without
252
+ * the factory reaching into module globals. */
253
+ interface ExecutorContext {
254
+ readonly signal: AbortSignal;
255
+ /** Opaque seams the registry threads through; a built-in narrows what it needs. */
256
+ readonly seams: Readonly<Record<string, unknown>>;
257
+ }
258
+ /**
259
+ * The OPEN resolver: maps an `AgentSpec` to a `ExecutorFactory`. The default
260
+ * registry resolves the three built-ins AND accepts a BYO `executor`/factory; callers
261
+ * register more runtimes by name. NOT a closed switch — registration is the extension
262
+ * point, mirroring the open `Executor` interface.
263
+ */
264
+ interface ExecutorRegistry {
265
+ /** Register a factory for a named runtime. Throws on a duplicate name (fail loud). */
266
+ register<Out>(runtime: Runtime, factory: ExecutorFactory<Out>): void;
267
+ /**
268
+ * Resolve a spec to a factory. Precedence: a BYO `spec.executor` → a trivial factory
269
+ * returning it; else `harness === null` → the `'router'` factory; else a registered
270
+ * factory for the harness-derived runtime. Returns a typed outcome — the caller
271
+ * inspects `succeeded` before `value` (no silent fallback).
272
+ */
273
+ resolve<Out>(spec: AgentSpec): {
274
+ succeeded: true;
275
+ value: ExecutorFactory<Out>;
276
+ } | {
277
+ succeeded: false;
278
+ error: string;
279
+ };
280
+ }
281
+ /** A budget envelope on a spawn or the root. All ceilings; the pool reserves against them. */
282
+ interface Budget {
283
+ readonly maxIterations: number;
284
+ readonly maxTokens: number;
285
+ readonly maxUsd?: number;
286
+ readonly deadlineMs?: number;
287
+ }
288
+ /** Conserved spend, reconciled from the normalized `UsageEvent` stream. Tokens and usd
289
+ * are separate channels (never folded). */
290
+ interface Spend {
291
+ iterations: number;
292
+ tokens: LoopTokenUsage;
293
+ usd: number;
294
+ ms: number;
295
+ }
296
+ /** OTP child-spec restart class. */
297
+ type Restart = 'temporary' | 'transient' | 'permanent';
298
+ /** `'acquiring'` is first-class (M1): a node spends real time + reaps an orphan box
299
+ * during sandbox acquire BEFORE it is `running`, so abort must be defined over it. */
300
+ type NodeStatus = 'pending' | 'acquiring' | 'running' | 'done' | 'failed' | 'cancelled';
301
+ /** Deterministic node id — `${parent}:s${seq}` from the cursor order, never wall-clock. */
302
+ type NodeId = string;
303
+ interface SpawnOpts {
304
+ readonly budget: Budget;
305
+ readonly label: string;
306
+ readonly restart?: Restart;
307
+ /** Teardown grace handed to the executor when this node is reaped. */
308
+ readonly shutdown?: number | 'brutalKill' | 'infinity';
309
+ }
310
+ /**
311
+ * A live child handle. `abort()` is defined over the ACQUIRE lifecycle: it chains into
312
+ * the `acquireSandbox` signal and reaps a find-by-name orphan box, so a node aborted
313
+ * mid-acquire never leaks (M1).
314
+ */
315
+ interface Handle<Out> {
316
+ readonly id: NodeId;
317
+ readonly label: string;
318
+ readonly status: NodeStatus;
319
+ abort(reason?: string): void;
320
+ /** Phantom: binds the handle to the child's output type so `spawn<C>` returns a
321
+ * `Handle<C>` distinct from a `Handle<other>`. Type-only — never present at runtime. */
322
+ readonly __out?: Out;
323
+ }
324
+ /**
325
+ * A settled child, delivered by `scope.next()`. `seq` is the monotonic cursor order
326
+ * `next()` yielded this settlement (B2) — NOT wall-clock — and replay delivers strictly
327
+ * in `seq` order. `outRef` rehydrates `out` from the `ResultBlobStore` on replay.
328
+ */
329
+ type Settled<Out> = {
330
+ kind: 'done';
331
+ handle: Handle<Out>;
332
+ out: Out;
333
+ outRef: string;
334
+ verdict?: DefaultVerdict;
335
+ spent: Spend;
336
+ seq: number;
337
+ } | {
338
+ kind: 'down';
339
+ handle: Handle<Out>;
340
+ reason: string;
341
+ /** True = infrastructure failure (excluded from merge `n` / equal-k), not a bad result. */
342
+ infra: boolean;
343
+ restartCount: number;
344
+ seq: number;
345
+ };
346
+ /**
347
+ * The budget-conserving reactive scope an `Agent.act` runs inside. `spawn` reserves
348
+ * budget atomically from the shared pool and fails closed when the pool cannot cover it.
349
+ * `next()` waits for one settlement from this scope's live set; `view` reads live state,
350
+ * not the replay log.
351
+ */
352
+ interface Scope<Out> {
353
+ /**
354
+ * Spawn a child. Reserves `opts.budget` from the conserved pool atomically; refunds the
355
+ * unspent remainder on settle. Returns a typed outcome — fail-closed on an exhausted
356
+ * pool or an exceeded depth ceiling (the caller inspects `ok` before `handle`).
357
+ */
358
+ spawn<C extends Out>(agent: Agent<unknown, C>, task: unknown, opts: SpawnOpts): {
359
+ ok: true;
360
+ handle: Handle<C>;
361
+ } | {
362
+ ok: false;
363
+ reason: 'budget-exhausted' | 'depth-exceeded';
364
+ };
365
+ /** ray.wait n=1 over this scope's in-memory live set; resolves as each child settles;
366
+ * `null` when the live set is empty. */
367
+ next(): Promise<Settled<Out> | null>;
368
+ /**
369
+ * Steer a RUNNING child out-of-band — deliver a message to its executor's inbox (the driver's
370
+ * `send` verb: next-instruction, interrupt, or resume). Returns `true` if the message was
371
+ * delivered to a live child whose executor accepts delivery, `false` otherwise (unknown id,
372
+ * already settled, or an executor with no inbox). The executor drains its inbox between turns;
373
+ * a leaf that does not implement `deliver` simply cannot be steered mid-flight. In-process this
374
+ * is a direct call; the sandbox/Agent-Bus transports surface the SAME verb as an MCP tool.
375
+ */
376
+ send(nodeId: NodeId, msg: unknown): boolean;
377
+ /** This scope's abort signal — aborted when the run is cancelled, a breaker trips, the pool
378
+ * is exhausted, or a parent scope cascades. A long-running driver `act` over this scope reads
379
+ * it to break promptly (the conserved pool + driver-stop are the other bounds). A nested
380
+ * scope carries its own signal, chained off its driver child's abort. */
381
+ readonly signal: AbortSignal;
382
+ /**
383
+ * Meter the driver's OWN compute against the conserved pool — its inference turns, which are
384
+ * real tokens/usd but not a spawned child (no reserve/reconcile). A direct `free → committed`
385
+ * debit, so equal-k counts the driver's tokens AND the in-loop budget guard (`budget.tokensLeft`)
386
+ * halts a driver that thinks the pool dry. `detail` rides an `agent.turn` trace event for live
387
+ * observability (turn index, tool calls, cumulative spend). It also journals a `metered` event —
388
+ * the durable twin of the pool debit (as `settled` is the twin of `reconcile`) — so every
389
+ * journal-based cost reader (`spentFromJournal`, `trajectoryReport`) sums driver inference
390
+ * automatically. A leaf never calls this; a driver meters each chat turn and awaits it (the
391
+ * metered event is cost-critical, so it lands before the join-barrier roll-up).
392
+ */
393
+ meter(spend: Spend, detail?: Record<string, unknown>): Promise<void>;
394
+ /** The live tree — reads the in-memory nursery, not the journal. */
395
+ readonly view: TreeView;
396
+ /** Conserved-pool readouts (post-reservation). */
397
+ readonly budget: Readonly<{
398
+ tokensLeft: number;
399
+ usdLeft: number;
400
+ usdCapped: boolean;
401
+ deadlineMs: number;
402
+ reservedTokens: number;
403
+ }>;
404
+ }
405
+ interface NodeSnapshot {
406
+ readonly id: NodeId;
407
+ readonly parent?: NodeId;
408
+ readonly label: string;
409
+ readonly status: NodeStatus;
410
+ readonly runtime: Runtime;
411
+ readonly budget: Budget;
412
+ /** Conserved spend so far for this node. */
413
+ readonly spent: Spend;
414
+ /** `outRef` once the node is `done` (the replay/result pointer). */
415
+ readonly outRef?: string;
416
+ }
417
+ /** The live tree — what `scope.view` / `RootHandle.view()` materialize for a viewer. */
418
+ interface TreeView {
419
+ readonly root: NodeId;
420
+ readonly nodes: ReadonlyArray<NodeSnapshot>;
421
+ /** Count of nodes in `running` or `acquiring` — the "what's in flow?" answer. */
422
+ readonly inFlight: number;
423
+ }
424
+ /** Journaled spawn-tree events (B1/B2). `seq` is the cursor order; `at` is an ISO
425
+ * timestamp for human inspection only (NOT a replay input). */
426
+ type SpawnEvent = {
427
+ kind: 'spawned';
428
+ id: NodeId;
429
+ parent?: NodeId;
430
+ label: string;
431
+ budget: Budget;
432
+ runtime: Runtime;
433
+ seq: number;
434
+ at: string;
435
+ } | {
436
+ kind: 'settled';
437
+ id: NodeId;
438
+ status: 'done' | 'down';
439
+ /** Content-addressed result pointer; rehydrates `out` from `ResultBlobStore`. */
440
+ outRef?: string;
441
+ verdict?: DefaultVerdict;
442
+ spent: Spend;
443
+ infra?: boolean;
444
+ seq: number;
445
+ at: string;
446
+ } | {
447
+ kind: 'cancelled';
448
+ id: NodeId;
449
+ reason: string;
450
+ seq: number;
451
+ at: string;
452
+ } | {
453
+ /** A driver's OWN inference spend, journaled separately from spawned-child work — the journal
454
+ * TWIN of `BudgetPool.observe`, exactly as `settled` is the twin of `reconcile`. So every
455
+ * journal-based cost reader sums it automatically — the journal is the single cost ledger.
456
+ * It carries spend only and is NOT a settlement: replay + `materializeTreeView` skip it for
457
+ * structure, and its `seq` lives outside the cursor-uniqueness namespace. A
458
+ * driver re-homes its nested subtree's metered total up to its parent (like settled spend),
459
+ * so summing any sub-tree root yields that sub-tree's true driver-inference cost. */
460
+ kind: 'metered';
461
+ id: NodeId;
462
+ spend: Spend;
463
+ seq: number;
464
+ at: string;
465
+ };
466
+ /**
467
+ * The spawn-tree event source (mirrors `ConversationJournal`'s begin/append/load shape).
468
+ * `loadTree` replays the full ordered event list for resume/replay; `appendEvent` is
469
+ * called only AFTER the event is observed-committed (never speculative).
470
+ */
471
+ interface SpawnJournal {
472
+ loadTree(root: NodeId): Promise<SpawnEvent[] | undefined>;
473
+ beginTree(root: NodeId, at: string): Promise<void>;
474
+ appendEvent(root: NodeId, ev: SpawnEvent): Promise<void>;
475
+ }
476
+ /** Content-addressed result blobs (the `outRef` → artifact map) backing the replay
477
+ * invariant. Split from the journal so the journal stays small (decisions) and the
478
+ * payloads (evidence) live where a viewer/replayer rehydrates them. */
479
+ interface ResultBlobStore {
480
+ put(outRef: string, artifact: unknown): Promise<void>;
481
+ get(outRef: string): Promise<unknown | undefined>;
482
+ }
483
+ /**
484
+ * Owns the conserved pool, the spawn log, the abort cascade, the OTP intensity breaker,
485
+ * and the root handle. `run` executes the root `Agent` to completion; `attach` wires a
486
+ * live `RootHandle` (the Q2 substrate the chat/pi-viz client later consumes).
487
+ */
488
+ interface Supervisor<Task, Out> {
489
+ run(root: Agent<Task, Out>, task: Task, opts: SupervisorOpts): Promise<SupervisedResult<Out>>;
490
+ attach(h: RootHandle<Out>): void;
491
+ }
492
+ interface SupervisorOpts {
493
+ /** The root conserved-pool ceiling (tokens + usd + iterations + deadline). */
494
+ readonly budget: Budget;
495
+ /** Trace-correlation root + the journal/blob root key. */
496
+ readonly runId: NodeId;
497
+ /** Event source — defaults to the in-memory journal in the impl; pass JSONL/FS for durability. */
498
+ readonly journal: SpawnJournal;
499
+ /** Result payload store backing `outRef` rehydration. */
500
+ readonly blobs: ResultBlobStore;
501
+ /** Executor resolution — the open registry mapping `AgentSpec` → `Executor`. */
502
+ readonly executors: ExecutorRegistry;
503
+ /** Runtime recursion-depth ceiling (paired with the conserved pool per R3). */
504
+ readonly maxDepth?: number;
505
+ /**
506
+ * OTP intensity breaker: more than `maxRestarts` child restarts within `withinMs`
507
+ * trips the supervisor to `no-winner` rather than restarting forever.
508
+ */
509
+ readonly maxRestarts?: number;
510
+ readonly withinMs?: number;
511
+ readonly now?: () => number;
512
+ readonly signal?: AbortSignal;
513
+ /** Lifecycle stream sink, threaded into the root `Scope` so every `spawn`/settle emits on the
514
+ * same `agent.spawn`/`agent.child` stream `runLoop` feeds — one observable recursive tree. */
515
+ readonly hooks?: RuntimeHooks;
516
+ }
517
+ /** Typed terminal result (M2) — a no-winner is NEVER coerced to a best-effort output. */
518
+ type SupervisedResult<Out> = {
519
+ kind: 'winner';
520
+ out: Out;
521
+ outRef: string;
522
+ verdict?: DefaultVerdict;
523
+ tree: TreeView;
524
+ spentTotal: Spend;
525
+ /** Where `spentTotal` went: `driverInference` = the drivers' own chat turns (metered via
526
+ * `Scope.meter`); `childWork` = every spawned child's reconciled spend (the journal sum).
527
+ * `driverInference + childWork === spentTotal`. Present whenever any driver metered. */
528
+ spentBreakdown?: {
529
+ driverInference: Spend;
530
+ childWork: Spend;
531
+ };
532
+ } | {
533
+ kind: 'no-winner';
534
+ reason: 'all-children-down' | 'budget-exhausted' | 'aborted';
535
+ tree: TreeView;
536
+ downCount: number;
537
+ /** The conserved spend incurred before the run failed — real cost is paid even when no
538
+ * worker delivers, so the caller always learns what the delegation actually spent. Summed
539
+ * off the same journal the `winner` path reads. */
540
+ spentTotal: Spend;
541
+ };
542
+ /** Live root handle — the substrate a chat/pi-viz client attaches to (Q2). `signal`
543
+ * delivers an out-of-band message to the running root; `view()` materializes the tree. */
544
+ interface RootHandle<Out> {
545
+ view(): TreeView;
546
+ signal(msg: RootSignal): void;
547
+ abort(reason?: string): void;
548
+ /** Phantom: binds the handle to the supervised run's output type. Type-only — never
549
+ * present at runtime; lets `attach(h: RootHandle<Out>)` stay output-typed. */
550
+ readonly __out?: Out;
551
+ }
552
+ /** Out-of-band message to a running root. Open by intent — a client extends it. */
553
+ type RootSignal = {
554
+ kind: 'pause';
555
+ } | {
556
+ kind: 'resume';
557
+ } | {
558
+ kind: 'cancel';
559
+ reason?: string;
560
+ } | {
561
+ kind: 'ask';
562
+ question: string;
563
+ };
564
+ /**
565
+ * The progressive-widening gate (MCTS-PW). Decides whether a settled child is
566
+ * `promising` enough to spawn another under the remaining pool. DEFAULTS TO FLAT
567
+ * (`shouldWiden` always false) so a gate run never widens and the selector≠judge
568
+ * firewall conflict (R2) stays dormant. When widening IS enabled, `promising` MUST be
569
+ * derived from TRACE findings (`analyses`), never raw `verdict` — or the gate carries
570
+ * an explicit, argued `judgeExempt: true` (the documented escape hatch, off by default).
571
+ */
572
+ interface WidenGate<Out> {
573
+ /** Default impl returns false for every settlement (flat — never widens). */
574
+ shouldWiden(settled: Settled<Out>, budget: Scope<Out>['budget']): boolean;
575
+ /** When true, widening may read `verdict` directly (collides with the steer firewall —
576
+ * must be explicitly argued per cell, never defaulted on). */
577
+ readonly judgeExempt?: boolean;
578
+ }
579
+
580
+ /**
581
+ * @experimental
582
+ *
583
+ * The completion-oracle: **settled ⟺ DELIVERED.**
584
+ *
585
+ * Foreman's one hard lesson (0/18 self-improvement deliverables) — "done" must mean a check
586
+ * PASSED, not the agent's say-so. `gateOnDeliverable` wraps an `Executor` so its settlement
587
+ * is `valid` ONLY when the deliverable check passes. The child still RUNS and settles (its
588
+ * spend is conserved into the pool either way), but a child that ran WITHOUT delivering
589
+ * settles `valid:false` — so a keep-best driver never counts it as done, and a gate never
590
+ * inflates with self-judged wins.
591
+ *
592
+ * Dual-purpose by construction:
593
+ * - product: the agent fleet only advances on real, checked deliverables.
594
+ * - proof: the gate's `valid` is the honest settle — equal-k comparisons can't be gamed by an
595
+ * arm that "ran" without producing the artifact.
596
+ *
597
+ * The check is a DEPLOYABLE oracle — a test command, a state verifier, the commit0 judge —
598
+ * read off the child's output, never the model judging itself. A throwing check is
599
+ * fail-closed (not delivered), never a crash.
600
+ */
601
+
602
+ /**
603
+ * The deployable completion oracle passed to {@link gateOnDeliverable}: a `check` that
604
+ * decides DELIVERED (settles `valid` ⟺ it resolves true) plus an optional `describe` of
605
+ * what the spawn was supposed to produce. The check reads the child's output — never the
606
+ * model judging itself.
607
+ */
608
+ interface DeliverableSpec<Out = unknown> {
609
+ /** The deployable check that decides DELIVERED. `settled.valid ⟺ this resolves true`. */
610
+ check: (out: Out) => boolean | Promise<boolean>;
611
+ /** What the spawn was supposed to produce — surfaced in traces/reports. */
612
+ describe?: string;
613
+ }
614
+ /**
615
+ * Wrap an `Executor` so its settlement `valid` reflects the deliverable check, not the
616
+ * inner verdict. Handles both `execute` shapes (one-shot `Promise<ExecutorResult>` and
617
+ * streaming `AsyncIterable<UsageEvent>` + `resultArtifact()`); the check runs once the inner
618
+ * executor has produced its output. The inner `score` is preserved; only `valid` is gated.
619
+ */
620
+ declare function gateOnDeliverable<Out>(inner: Executor<Out>, deliverable: DeliverableSpec<Out>): Executor<Out>;
621
+
622
+ /**
623
+ * @experimental
624
+ *
625
+ * Git worktree helpers for the in-process delegation executor. Each
626
+ * delegation runs in its own worktree so multiple parallel harness
627
+ * subprocesses (claude / codex / opencode in a 3-way fanout) don't clobber
628
+ * each other's edits on the shared workspace.
629
+ *
630
+ * Worktrees live under `<repoRoot>/.agent-worktrees/<runId>/`. After the
631
+ * harness exits + the diff is captured, the worktree is removed.
632
+ *
633
+ * All operations spawn `git` via `child_process.spawn` synchronously
634
+ * (via a `runGit` helper). Stays narrow on purpose: no commits, no rebases.
635
+ * Diff capture stages all changes (`git add -A`) into the ephemeral worktree's
636
+ * index so created (untracked) files appear in the `--cached` diff.
637
+ */
638
+ /** @experimental */
639
+ interface WorktreeHandle {
640
+ /** Absolute path to the worktree directory. */
641
+ path: string;
642
+ /** SHA the worktree was created at. */
643
+ baseSha: string;
644
+ /** Branch name created for this worktree (typically `delegate/<runId>`). */
645
+ branch: string;
646
+ }
647
+ /** @experimental */
648
+ interface CreateWorktreeOptions {
649
+ /** Absolute path to the main git checkout. */
650
+ repoRoot: string;
651
+ /** Unique id for the worktree path + branch. Use the delegation run id. */
652
+ runId: string;
653
+ /** Parent directory the worktree lives under. Defaults to `.agent-worktrees`. */
654
+ variantsDir?: string;
655
+ /** Override the base ref (default `HEAD`). */
656
+ baseRef?: string;
657
+ /** Test seam — inject a custom git runner. */
658
+ runGit?: GitRunner;
659
+ }
660
+ /** @experimental */
661
+ interface DiffOptions {
662
+ /** Worktree to diff. */
663
+ worktree: WorktreeHandle;
664
+ /** What to compare against. Default `worktree.baseSha`. */
665
+ baseRef?: string;
666
+ /** Test seam. */
667
+ runGit?: GitRunner;
668
+ }
669
+ /** @experimental */
670
+ interface DiffResult {
671
+ patch: string;
672
+ stats: {
673
+ filesChanged: number;
674
+ insertions: number;
675
+ deletions: number;
676
+ };
677
+ }
678
+ /** @experimental */
679
+ interface RemoveWorktreeOptions {
680
+ worktree: WorktreeHandle;
681
+ repoRoot: string;
682
+ /** Force removal even if dirty (default true; the loser of a fanout has uncommitted changes). */
683
+ force?: boolean;
684
+ /** Test seam. */
685
+ runGit?: GitRunner;
686
+ }
687
+ /** Pluggable git runner (sync) — replaceable in tests. */
688
+ type GitRunner = (args: ReadonlyArray<string>, opts: {
689
+ cwd: string;
690
+ }) => {
691
+ stdout: string;
692
+ stderr: string;
693
+ exitCode: number;
694
+ };
695
+ /** @experimental */
696
+ declare function createWorktree(options: CreateWorktreeOptions): Promise<WorktreeHandle>;
697
+ /** @experimental */
698
+ declare function captureWorktreeDiff(options: DiffOptions): Promise<DiffResult>;
699
+ /** @experimental */
700
+ declare function removeWorktree(options: RemoveWorktreeOptions): Promise<void>;
701
+
702
+ export { type AgentSpec as A, type Budget as B, type CreateWorktreeOptions as C, type DiffOptions as D, type ExecutorRegistry as E, type GitRunner as G, type LocalHarness as L, type NodeId as N, type RemoveWorktreeOptions as R, type SpawnJournal as S, type TreeView as T, type UsageEvent as U, type WorktreeHandle as W, type DiffResult as a, type LocalHarnessResult as b, type RunLocalHarnessOptions as c, captureWorktreeDiff as d, createWorktree as e, removeWorktree as f, type Agent as g, type ResultBlobStore as h, type RootHandle as i, type SupervisedResult as j, type Settled as k, type Spend as l, type Scope as m, type Executor as n, type DeliverableSpec as o, type ExecutorFactory as p, type SpawnEvent as q, runLocalHarness as r, type Supervisor as s, type ExecutorContext as t, type ExecutorResult as u, type SupervisorOpts as v, type WidenGate as w, gateOnDeliverable as x };