@tangle-network/agent-runtime 0.69.0 → 0.70.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 (78) hide show
  1. package/README.md +81 -13
  2. package/dist/agent.d.ts +4 -5
  3. package/dist/agent.js +3 -6
  4. package/dist/agent.js.map +1 -1
  5. package/dist/chunk-BYZCXQHF.js +474 -0
  6. package/dist/chunk-BYZCXQHF.js.map +1 -0
  7. package/dist/{chunk-G6U3GVK2.js → chunk-EDCVUZZC.js} +2 -2
  8. package/dist/{chunk-RTDI2DHD.js → chunk-L5ZFBVT6.js} +6 -6
  9. package/dist/chunk-L5ZFBVT6.js.map +1 -0
  10. package/dist/{chunk-NLRA6434.js → chunk-QXWGSDAQ.js} +716 -535
  11. package/dist/chunk-QXWGSDAQ.js.map +1 -0
  12. package/dist/{chunk-QZ6FA5LM.js → chunk-ZNQVMMR5.js} +3 -3
  13. package/dist/{coordination-Curpzeyc.d.ts → coordination-C7WxwHXq.d.ts} +8 -1
  14. package/dist/{delegates-CLFNAKyi.d.ts → delegates-DqAgo32T.d.ts} +105 -10
  15. package/dist/{improvement-adapter-BC4HhuAR.d.ts → improvement-adapter-BVuMragr.d.ts} +1 -1
  16. package/dist/index.d.ts +439 -100
  17. package/dist/index.js +583 -125
  18. package/dist/index.js.map +1 -1
  19. package/dist/intelligence.d.ts +2 -3
  20. package/dist/{loop-runner-bin-B6dzNZC8.d.ts → loop-runner-bin-B0NeLTRd.d.ts} +4 -4
  21. package/dist/loop-runner-bin.d.ts +7 -10
  22. package/dist/loop-runner-bin.js +4 -7
  23. package/dist/loops.d.ts +3149 -16
  24. package/dist/loops.js +34 -63
  25. package/dist/mcp/bin.js +14 -16
  26. package/dist/mcp/bin.js.map +1 -1
  27. package/dist/mcp/index.d.ts +8 -12
  28. package/dist/mcp/index.js +16 -19
  29. package/dist/mcp/index.js.map +1 -1
  30. package/dist/{openai-tools-CA2N3-Ak.d.ts → openai-tools-DPx9Gzvn.d.ts} +1 -1
  31. package/dist/profiles.d.ts +94 -6
  32. package/dist/profiles.js +320 -9
  33. package/dist/profiles.js.map +1 -1
  34. package/dist/{router-client-30Y_pca8.d.ts → router-client-C7kp_ECN.d.ts} +37 -1
  35. package/dist/{substrate-CUgk7F7s.d.ts → substrate-BoRXgvka.d.ts} +52 -1
  36. package/dist/{types-Crxftafi.d.ts → types-BYa2ZOAx.d.ts} +83 -2
  37. package/dist/{types-p8dWBIXL.d.ts → types-DJu6TBGp.d.ts} +2 -16
  38. package/dist/{worktree-fanout-DUiKPApb.d.ts → worktree-fanout-gNfl0Byj.d.ts} +9 -37
  39. package/package.json +9 -39
  40. package/dist/analyst-loop.d.ts +0 -52
  41. package/dist/analyst-loop.js +0 -11
  42. package/dist/analyst-loop.js.map +0 -1
  43. package/dist/audit.d.ts +0 -93
  44. package/dist/audit.js +0 -312
  45. package/dist/audit.js.map +0 -1
  46. package/dist/chunk-4B6U4CVQ.js +0 -15
  47. package/dist/chunk-4B6U4CVQ.js.map +0 -1
  48. package/dist/chunk-K4FQSEXQ.js +0 -214
  49. package/dist/chunk-K4FQSEXQ.js.map +0 -1
  50. package/dist/chunk-NLRA6434.js.map +0 -1
  51. package/dist/chunk-O2UPHN7X.js +0 -114
  52. package/dist/chunk-O2UPHN7X.js.map +0 -1
  53. package/dist/chunk-P5OKDSLB.js +0 -580
  54. package/dist/chunk-P5OKDSLB.js.map +0 -1
  55. package/dist/chunk-RTDI2DHD.js.map +0 -1
  56. package/dist/chunk-VLF5RHEQ.js +0 -143
  57. package/dist/chunk-VLF5RHEQ.js.map +0 -1
  58. package/dist/coder-2leJPOvC.d.ts +0 -52
  59. package/dist/improvement.d.ts +0 -208
  60. package/dist/improvement.js +0 -343
  61. package/dist/improvement.js.map +0 -1
  62. package/dist/local-harness-BE_h8szs.d.ts +0 -93
  63. package/dist/platform.d.ts +0 -255
  64. package/dist/platform.js +0 -229
  65. package/dist/platform.js.map +0 -1
  66. package/dist/run-loop-D3PwlG7J.d.ts +0 -112
  67. package/dist/runtime-hooks-C7JwKb9E.d.ts +0 -70
  68. package/dist/runtime.d.ts +0 -3120
  69. package/dist/runtime.js +0 -263
  70. package/dist/runtime.js.map +0 -1
  71. package/dist/topology.d.ts +0 -126
  72. package/dist/topology.js +0 -333
  73. package/dist/topology.js.map +0 -1
  74. package/dist/workflow.d.ts +0 -551
  75. package/dist/workflow.js +0 -1781
  76. package/dist/workflow.js.map +0 -1
  77. /package/dist/{chunk-G6U3GVK2.js.map → chunk-EDCVUZZC.js.map} +0 -0
  78. /package/dist/{chunk-QZ6FA5LM.js.map → chunk-ZNQVMMR5.js.map} +0 -0
package/dist/runtime.d.ts DELETED
@@ -1,3120 +0,0 @@
1
- import { CreateSandboxOptions, SandboxInstance, SandboxEvent, BackendType } from '@tangle-network/sandbox';
2
- export { AgentProfile, CreateSandboxOptions, SandboxEvent, SandboxInstance } from '@tangle-network/sandbox';
3
- import { g as ResultBlobStore, i as SpawnJournal, N as NodeId, b7 as SpawnEvent, b8 as TreeView, l as Settled, b9 as ExecutorFactory, A as Agent, B as Budget, S as Scope, k as SupervisedResult, m as Spend, ba as UsageEvent, h as AgentSpec, E as ExecutorRegistry, j as RootHandle, bb as Supervisor } from './delegates-CLFNAKyi.js';
4
- export { n as Executor, bc as ExecutorContext, bd as ExecutorResult, be as Handle, bf as NodeSnapshot, bg as NodeStatus, bh as Restart, bi as RootSignal, bj as Runtime, bk as SpawnOpts, bl as SupervisorOpts, bm as WidenGate } from './delegates-CLFNAKyi.js';
5
- import { R as RuntimeHooks } from './runtime-hooks-C7JwKb9E.js';
6
- import { ChatClient, AnalystFinding, AgentProfile, AnalystRunInputs, ToolSpan, StreamingDetector, DetectorSignal, buildTrajectory } from '@tangle-network/agent-eval';
7
- export { DefaultVerdict } from '@tangle-network/agent-eval';
8
- import { I as Iteration, S as SandboxClient, b as LoopResult, f as LoopTokenUsage, R as RuntimeStreamEvent, A as AgentRunSpec, E as ExecCtx } from './types-Crxftafi.js';
9
- export { D as Driver, F as LoopDecisionPayload, G as LoopEndedPayload, H as LoopIterationDispatchPayload, J as LoopIterationEndedPayload, M as LoopIterationStartedPayload, a as LoopLineageOptions, N as LoopPlanDescription, P as LoopPlanPayload, e as LoopSandboxPlacement, Q as LoopStartedPayload, T as LoopTeardownFailedPayload, g as LoopTraceEmitter, d as LoopTraceEvent, L as LoopWinner, O as OutputAdapter, U as ValidationCtx, V as Validator } from './types-Crxftafi.js';
10
- import { C as CorpusRecord, c as Corpus, O as Outcome, S as ScopeAnalyzeInput, d as AssertTraceDerivedFindings, e as SteerContext, f as ScopeAnalyst, F as FanoutOptions, g as CombinatorShape, h as ScopeWidenGate, L as LoopUntilSpec, P as PanelSpec, i as PipelineStage, W as WinnerStrategy, j as FanoutWinnerSelector, V as VerifySpec, k as WidenSpec, l as CorpusFilter, R as RenderCorpusToInstructionsOptions, D as DefinePersonaInput, m as Persona, n as RunPersonifiedOptions, o as ShapeRegistry, p as LoopShape, E as EqualKArm, q as EqualKOnCostOptions, r as EqualKVerdict, T as TrajectoryReportOptions, s as TrajectoryReport, t as DeliverableSpec } from './worktree-fanout-DUiKPApb.js';
11
- export { A as AuthoredHarness, u as CoderCheckConstraints, v as CoderCheckInput, w as DefinePersona, x as EqualKOnCost, y as Fanout, z as FanoutSynthesis, B as FlatWidenGate, G as LoopUntil, H as LoopUntilState, I as Panel, J as PanelJudge, K as PanelVerdict, M as PatchDeliverableOptions, N as PersonaContext, Q as PersonaExecutors, U as Pipeline, X as RenderCorpusToInstructions, Y as RunPersonified, Z as ShapeBudget, _ as ShapeContext, $ as TrajectoryNode, a0 as TrajectoryReportFn, a1 as Verify, a2 as Widen, a3 as WidenDecision, a4 as WidenLineage, a5 as WorktreeCliExecutorOptions, a6 as WorktreeCommandResult, a as WorktreeFanoutOptions, b as WorktreePatchArtifact, a7 as countDiffLines, a8 as createWorktreeCliExecutor, a9 as gateOnDeliverable, aa as isNonEmptyPatch, ab as patchDelivered, ac as runCoderChecks, ad as touchedPathsFromPatch, ae as touchesSecretPath, af as worktreeFanout } from './worktree-fanout-DUiKPApb.js';
12
- import { Scenario, ProfileDispatchFn } from '@tangle-network/agent-eval/campaign';
13
- import { R as RunLoopOptions } from './run-loop-D3PwlG7J.js';
14
- export { c as createSandboxForSpec, d as defaultSelectWinner, r as runLoop } from './run-loop-D3PwlG7J.js';
15
- import { b as AnalystRegistryLike } from './types-p8dWBIXL.js';
16
- import { AgentProfile as AgentProfile$1 } from '@tangle-network/agent-interface';
17
- import { R as RouterConfig, T as ToolSpec } from './router-client-30Y_pca8.js';
18
- export { a as RouterChatResult, b as RouterChatToolsResult, c as RouterToolCall, d as RouterToolLoopResult, r as routerChatWithTools, e as routerChatWithUsage, f as routerToolLoop } from './router-client-30Y_pca8.js';
19
- import { M as MakeWorkerAgent, a as CoordinationTools, A as AnalystRegistry, C as CoordinationEvent, j as QuestionPolicy } from './coordination-Curpzeyc.js';
20
- export { B as BusEvent, p as BusRecord, q as BusStats, E as EventBus, P as PublishOptions, r as createEventBus } from './coordination-Curpzeyc.js';
21
- import { L as LocalHarness } from './local-harness-BE_h8szs.js';
22
- import { stuckLoopView, toolWasteView } from '@tangle-network/agent-eval/pipelines';
23
- import './coder-2leJPOvC.js';
24
- import './substrate-CUgk7F7s.js';
25
- import 'node:child_process';
26
-
27
- /**
28
- * @experimental
29
- *
30
- * Event-sourced spawn journal for the recursive execution atom (build steps 3 + 7).
31
- *
32
- * The supervision tree is journaled as an append-only event log: every `spawned`,
33
- * `settled`, and `cancelled` is recorded AFTER it is observed-committed (never
34
- * speculative), mirroring `ConversationJournal`'s begin/append/load shape. The log
35
- * holds only the THIN decision record — ids, parentage, budget, the spend a decision
36
- * consumed, and a content-addressed `outRef`. The payloads the driver branched on
37
- * (the `out` artifacts) live in a separate `ResultBlobStore`, keyed by `outRef`, so
38
- * the journal stays small (decisions) and replay rehydrates the exact `Settled` from
39
- * the blob store (evidence). This is the decision/payload split the replay argument
40
- * rests on (B1/B2).
41
- *
42
- * Replay determinism (B2): `seq` is the monotonic cursor order `scope.next()` yielded
43
- * each settlement — NOT wall-clock. `replaySpawnTree` sorts strictly by `seq` before
44
- * touching the blob store, so the order in which rehydration `get`s resolve can never
45
- * reorder the replayed `Settled[]`; the result is identical regardless of blob latency.
46
- */
47
-
48
- /**
49
- * Mint the content-addressed `outRef` for a result artifact: `sha256:<hex>` over a
50
- * stable JSON encoding. Producers call this to derive the `outRef` they journal and
51
- * `put`; the FS/in-mem stores re-derive it on `put` to verify the supplied ref
52
- * matches (fail loud on a mismatch — a forged ref breaks the replay invariant).
53
- *
54
- * Stable encoding: object keys are sorted recursively so two structurally-equal
55
- * artifacts hash identically regardless of key insertion order.
56
- */
57
- declare function contentAddress(artifact: unknown): string;
58
- /**
59
- * In-memory `ResultBlobStore`. Content-addressed: `put` verifies the supplied
60
- * `outRef` matches the artifact's hash so a stale/forged ref fails loud rather than
61
- * silently rehydrating the wrong payload. Idempotent on an identical re-put.
62
- */
63
- declare class InMemoryResultBlobStore implements ResultBlobStore {
64
- private readonly blobs;
65
- put(outRef: string, artifact: unknown): Promise<void>;
66
- get(outRef: string): Promise<unknown | undefined>;
67
- }
68
- /**
69
- * FS `ResultBlobStore`. One JSON file per artifact under `dir`, named by a
70
- * filesystem-safe encoding of the `outRef` (`sha256:<hex>` → `sha256-<hex>.json`).
71
- * `put` fsyncs so a crash between writes never loses an acknowledged blob.
72
- */
73
- declare class FileResultBlobStore implements ResultBlobStore {
74
- private readonly dir;
75
- constructor(dir: string);
76
- put(outRef: string, artifact: unknown): Promise<void>;
77
- get(outRef: string): Promise<unknown | undefined>;
78
- private blobPath;
79
- }
80
- /**
81
- * In-memory `SpawnJournal`. Appends are observed-committed only; the impl enforces
82
- * the corruption guards a durable replay rests on:
83
- * - an event before `beginTree` is a corrupted tree (fail loud),
84
- * - a duplicate `seq` within a tree is a corrupted cursor (fail loud) — two
85
- * settlements cannot share the cursor position replay orders by.
86
- */
87
- declare class InMemorySpawnJournal implements SpawnJournal {
88
- private readonly trees;
89
- loadTree(root: NodeId): Promise<SpawnEvent[] | undefined>;
90
- beginTree(root: NodeId, at: string): Promise<void>;
91
- appendEvent(root: NodeId, ev: SpawnEvent): Promise<void>;
92
- }
93
- /**
94
- * JSONL on disk. One line per record: the first record is `begin`, subsequent records
95
- * are `event` envelopes wrapping a `SpawnEvent`. `loadTree` replays the whole file,
96
- * filtering by `root`, and applies the same begin-precedes-events + unique-seq
97
- * corruption guards as the in-memory impl. Each append fsyncs so a crash between
98
- * writes never loses an acknowledged event.
99
- */
100
- declare class FileSpawnJournal implements SpawnJournal {
101
- private readonly path;
102
- constructor(path: string);
103
- loadTree(root: NodeId): Promise<SpawnEvent[] | undefined>;
104
- beginTree(root: NodeId, at: string): Promise<void>;
105
- appendEvent(root: NodeId, ev: SpawnEvent): Promise<void>;
106
- private loadTreeBegin;
107
- private appendRecord;
108
- }
109
- /**
110
- * Re-feed a journaled spawn tree in strict `seq` order, rehydrating each settled
111
- * child's `out` from the blob store by `outRef`, and return the `Settled[]` exactly
112
- * as `scope.next()` originally delivered them.
113
- *
114
- * Determinism (B2): the events are sorted by `seq` BEFORE any blob `get`, so the
115
- * replay order is the recorded cursor order regardless of how fast each rehydration
116
- * resolves. `at` (wall-clock) is never a replay input. Fail loud on a tree that was
117
- * never begun, a settled-done event missing its `outRef`, or a blob the store can't
118
- * rehydrate — a silent gap would let `act` branch on the wrong evidence.
119
- */
120
- declare function replaySpawnTree(journal: SpawnJournal, blobs: ResultBlobStore, root: NodeId): Promise<Settled<unknown>[]>;
121
- /**
122
- * Materialize the live tree (`TreeView`) from a journaled event list for resume. Folds
123
- * `spawned`/`settled`/`cancelled` into a per-node snapshot in `seq` order, then adds each
124
- * `metered` event's driver-inference spend onto its node in a separate additive pass — so the
125
- * resumed view matches what `scope.view` showed at the recorded cursor position.
126
- */
127
- declare function materializeTreeView(events: SpawnEvent[]): TreeView;
128
-
129
- /**
130
- * createWaterfallCollector — 100% trajectory observability from the lifecycle stream:
131
- * every spawn/settle (shots, analysts, nested agents) becomes one timed, billed span.
132
- * The sum of spans IS the run's cost story — what each step cost in dollars, tokens,
133
- * and wall-clock, rendered as a text waterfall or exported as structured rows for any
134
- * chart. Attach the collector's `hooks` to `runAgentic`/`runBenchmark`; spans accumulate
135
- * across every task the hooks observe.
136
- */
137
-
138
- interface WaterfallSpan {
139
- id: string;
140
- /** The spawn label (`shot:0`, `analyst:1`, a nested agent's label) — the row name. */
141
- label: string;
142
- runId: string;
143
- parentId?: string;
144
- startMs: number;
145
- endMs?: number;
146
- status: 'running' | 'done' | 'down';
147
- usd: number;
148
- tokens: {
149
- input: number;
150
- output: number;
151
- };
152
- score?: number;
153
- }
154
- interface WaterfallReport {
155
- spans: WaterfallSpan[];
156
- /** Wall-clock of the observed window (first spawn → last settle). */
157
- totalMs: number;
158
- totalUsd: number;
159
- totalTokens: {
160
- input: number;
161
- output: number;
162
- };
163
- /** Rollup by label prefix (the part before ':') — shots vs analysts vs anything else. */
164
- byKind: Record<string, {
165
- count: number;
166
- ms: number;
167
- usd: number;
168
- tokens: {
169
- input: number;
170
- output: number;
171
- };
172
- }>;
173
- }
174
- interface WaterfallCollector {
175
- /** Attach these to RunAgenticOptions.hooks / BenchmarkConfig.hooks. */
176
- hooks: RuntimeHooks;
177
- report(): WaterfallReport;
178
- /** The text waterfall — one row per span, bars scaled to the observed window. */
179
- render(opts?: {
180
- width?: number;
181
- maxRows?: number;
182
- }): string;
183
- reset(): void;
184
- }
185
- declare function createWaterfallCollector(): WaterfallCollector;
186
-
187
- /**
188
- * anytimeReport — time-to-satisfactory-output metrics, derived entirely from the
189
- * waterfall's spans (no new instrumentation): per task, the best-so-far score after each
190
- * shot with its elapsed wall-clock and cumulative spend; per strategy, the standard
191
- * anytime-optimization metrics:
192
- *
193
- * TTT time-to-target — elapsed ms until best-so-far ≥ the target (per task; median
194
- * over tasks that reached it)
195
- * STT shots-to-target — attempts until best-so-far ≥ target
196
- * ERT expected running time (the COCO benchmarking convention): TOTAL time spent
197
- * across all tasks — including failures' full budgets — divided by the number of
198
- * tasks that reached the target. The honest "how long per success, all-in".
199
- * AUC the anytime curve's area (mean best-so-far score across the budget, per shot
200
- * index) — higher = climbs earlier.
201
- *
202
- * The "satisfactory" bar follows the COCO/BBOB convention: a SET of satisficing targets
203
- * (e.g. [0.5, 0.8, 1.0] on the normalized check score), each measured independently —
204
- * runtime-to-target per (task, target) pair — optionally overridden per task
205
- * (`targetFor`) when satisfaction is task-specific. Spans come from
206
- * `createWaterfallCollector().report()`; tasks are grouped by the supervisor runId
207
- * (`agentic:<strategy>:<taskId>`); shot spans are `shot:N` labels.
208
- */
209
-
210
- interface AnytimeTaskCurve {
211
- taskId: string;
212
- strategy: string;
213
- /** Best-so-far after each settled shot: elapsed ms from the task's first spawn,
214
- * cumulative usd, and the running max score. */
215
- points: Array<{
216
- elapsedMs: number;
217
- cumUsd: number;
218
- best: number;
219
- }>;
220
- /** Per satisficing target (keyed by the target value as a string): the first point
221
- * where best ≥ target, or null when never reached within budget. */
222
- hits: Record<string, {
223
- ms: number;
224
- shots: number;
225
- usd: number;
226
- } | null>;
227
- }
228
- interface AnytimeStrategySummary {
229
- strategy: string;
230
- /** The satisficing target this row summarizes. */
231
- target: number;
232
- tasks: number;
233
- reachedTarget: number;
234
- /** Median time-to-target over the tasks that reached it (null when none did). */
235
- medianTttMs: number | null;
236
- medianShotsToTarget: number | null;
237
- /** COCO ERT: Σ all task wall-time (incl. failures) / #successes. Null when 0 succeed. */
238
- ertMs: number | null;
239
- /** Same construction over dollars: Σ all spend / #successes. */
240
- erUsd: number | null;
241
- /** Mean best-so-far score by shot index (the anytime curve, averaged over tasks). */
242
- curveByShot: number[];
243
- /** Area under the per-shot anytime curve, normalized to [0,1]. */
244
- auc: number;
245
- }
246
- interface AnytimeReport {
247
- targets: number[];
248
- perTask: AnytimeTaskCurve[];
249
- /** One summary per (strategy, target) pair — the COCO-style multi-target view. */
250
- perStrategy: AnytimeStrategySummary[];
251
- }
252
- /** Derive anytime metrics from waterfall spans. `targets` are the satisficing score
253
- * bars (default [1] = fully resolved; COCO-style multi-target: [0.5, 0.8, 1]);
254
- * `targetFor` overrides the bar per task (task-specific satisfaction) — when set, the
255
- * per-task bar replaces every entry of `targets` for that task. */
256
- declare function anytimeReport(spans: WaterfallSpan[], opts?: {
257
- targets?: number[];
258
- targetFor?: (taskId: string) => number;
259
- }): AnytimeReport;
260
- /** One row per (strategy, satisficing target): the shareable time-to-satisfactory table. */
261
- declare function renderAnytimeTable(report: AnytimeReport): string;
262
-
263
- /**
264
- * auditIntent — the route-rigor analyst: is this trajectory even going the RIGHT WAY?
265
- *
266
- * `observe()` critiques execution quality ("what's unfinished"). This audits ALIGNMENT —
267
- * a different failure class the score can't see until it's too late: an agent can be
268
- * executing flawlessly down the wrong route. The auditor reads the trajectory and
269
- * compares three intents:
270
- *
271
- * declared — what the task says to do (the prompt / acceptance criteria)
272
- * revealed — what the agent is ACTUALLY optimizing, inferred from its action pattern
273
- * (the inverse-inference move: actions reveal objectives)
274
- * user — what the principal actually wants (the contract, when it differs from
275
- * the literal task text), plus where the user's own trajectory is heading
276
- *
277
- * and returns a verdict (aligned / drifting / diverged) with evidence and ONE
278
- * recommended intervention. FIREWALLED like every analyst: input is the trajectory and
279
- * the intents — never the verifier or its data (zero check-leakage, so route auditing
280
- * is always Goodhart-safe to run online).
281
- *
282
- * Where it runs: between shots (steer the next one), as a watchdog over the lifecycle
283
- * stream (abort-and-refund a diverged rollout — the budget pool makes early abort
284
- * strictly valuable), or post-hoc over a whole BenchmarkReport (the meta-intent pass:
285
- * is the LOOP optimizing the right thing — degenerate submissions, check-gaming shapes,
286
- * objective drift across tasks).
287
- */
288
-
289
- interface AuditIntentInput {
290
- /** The declared intent: the task text / acceptance criteria the agent was given. */
291
- declaredIntent: string;
292
- /** The trajectory so far — tool calls + results + assistant turns (any event shapes). */
293
- trace: ReadonlyArray<unknown>;
294
- /** The principal's actual intent when it differs from the literal task (the contract). */
295
- userIntent?: string;
296
- /** The loop-level purpose (meta-intent): what the WHOLE run is for — lets the auditor
297
- * flag locally-sensible work that serves the wrong larger objective. */
298
- metaIntent?: string;
299
- runId?: string;
300
- }
301
- interface AuditIntentOptions {
302
- chat: ChatClient;
303
- model?: string;
304
- /** Override the auditor instruction (optimizable like any analyst prompt). */
305
- auditorInstruction?: string;
306
- /** Cap trace lines fed to the auditor. Default 80. */
307
- maxTraceLines?: number;
308
- signal?: AbortSignal;
309
- }
310
- interface IntentAudit {
311
- /** What the agent's actions reveal it is actually optimizing — one sentence. */
312
- revealedIntent: string;
313
- verdict: 'aligned' | 'drifting' | 'diverged';
314
- /** Trajectory-grounded evidence for the verdict (specific calls/patterns). */
315
- evidence: string;
316
- /** The single recommended intervention. */
317
- recommendation: 'continue' | 'steer' | 'abort';
318
- /** When recommendation is 'steer': the corrective instruction to inject. */
319
- steer?: string;
320
- confidence: number;
321
- }
322
- declare const defaultAuditorInstruction: string;
323
- declare function auditIntent(input: AuditIntentInput, opts: AuditIntentOptions): Promise<IntentAudit>;
324
-
325
- /**
326
- * @experimental
327
- *
328
- * Completion / satisfiability — the OTHER output of the pluggable analyst (the steer output
329
- * is `AnalystFinding[]` via the `analyze` hook; this is the "is it done?" output via the
330
- * `complete` hook). A `CompletionAnalyst` reads a node's trace and returns a `CompletionVerdict`
331
- * the PARENT (driver) acts on: end the node, or keep going. It fits ANY node and composes to
332
- * any depth — a 1-deep loop has one; an N-deep tree has one per node.
333
- *
334
- * The verdict's authority scales with its DETERMINISM (the thing that varies by task):
335
- * - `deterministic` — build/test/lint pass, a proof checks, every claim's citation resolves:
336
- * ground truth, the driver TRUSTS it and ends. Not an opinion.
337
- * - `probabilistic` — a quality/soundness judgment (marketing, "the experiment is sound"):
338
- * ADVISORY. It passes to the driver with its reasons; the driver validates (here: a
339
- * confidence threshold; a richer driver may re-examine the reasons) before ending.
340
- *
341
- * Two stop-signal mechanisms, by node mode, both → one `CompletionVerdict`:
342
- * - sandbox-agent (text stream): a unique per-node STOP SENTINEL the agent emits when done
343
- * (`stopSentinel` / `sentinelCompletion`) — ralph-loop style; the seed makes it
344
- * unguessable + attributable, so it can't be spuriously emitted or confused with content.
345
- * - deterministic check (compile/test/citation/proof): `deterministicCompletion(check)` —
346
- * a verifier over the output, never the judge verdict (selector ≠ judge holds).
347
- */
348
-
349
- /** Trace-derived evidence for a completion claim — an artifact (output) or a verifier metric,
350
- * never the judge's own verdict. Mirrors the steer-firewall's provenance discipline. */
351
- interface CompletionEvidence {
352
- kind: 'artifact' | 'metric';
353
- uri: string;
354
- }
355
- /** The "is it done?" verdict an analyst returns to the parent. */
356
- interface CompletionVerdict {
357
- done: boolean;
358
- /** How verifiable the claim is — sets whether the driver trusts it or validates it. */
359
- determinism: 'deterministic' | 'probabilistic';
360
- /** Why the analyst believes it is (or isn't) done — what the driver validates. */
361
- reasons?: string;
362
- /** 0..1, for probabilistic verdicts; the driver's validation threshold reads this. */
363
- confidence?: number;
364
- evidence?: ReadonlyArray<CompletionEvidence>;
365
- }
366
- /** Reads a node's trace → a completion verdict. Same input shape as the `analyze` hook, so
367
- * ONE analyst node can back both channels (findings for steer, a verdict for stop). */
368
- interface CompletionAnalyst<Task, Output> {
369
- assess(input: {
370
- task: Task;
371
- history: ReadonlyArray<Iteration<Task, Output>>;
372
- }): CompletionVerdict | Promise<CompletionVerdict>;
373
- }
374
- /** When a verdict authorizes the driver to END. Deterministic → trust (ground truth);
375
- * probabilistic → validate by confidence threshold (the driver's check). */
376
- interface CompletionPolicy {
377
- /** Minimum confidence a PROBABILISTIC verdict must clear to end. Default 0.8. */
378
- minConfidence?: number;
379
- }
380
- declare function completionAuthorizes(v: CompletionVerdict, policy?: CompletionPolicy): boolean;
381
- /**
382
- * A unique, attributable stop sentinel for a node (ralph-loop style). Deterministic from the
383
- * seed (no Math.random — reproducible + attributable to the node); the agent is instructed to
384
- * emit it VERBATIM when it judges itself done. Unguessable enough that content never trips it.
385
- */
386
- declare function stopSentinel(seed: string): string;
387
- /**
388
- * Completion for a sandbox-agent node: done iff the latest output carries the node's stop
389
- * sentinel. PROBABILISTIC (the agent's own self-judgment) — the driver validates it.
390
- */
391
- declare function sentinelCompletion<Task>(sentinel: string, opts?: {
392
- confidence?: number;
393
- }): CompletionAnalyst<Task, string>;
394
- /**
395
- * Completion for a DETERMINISTIC check (build/test/lint/citation/proof): done iff the check
396
- * passes. Ground truth — the driver ends directly, no validation. The check reads the output
397
- * (a verifier), never the judge verdict — selector ≠ judge stays intact.
398
- */
399
- declare function deterministicCompletion<Task, Output>(check: (output: Output, history: ReadonlyArray<Iteration<Task, Output>>) => {
400
- passed: boolean;
401
- reasons?: string;
402
- }): CompletionAnalyst<Task, Output>;
403
-
404
- /**
405
- * The third-person observer — the connective tissue that closes the loop.
406
- *
407
- * A driver spawns a worker; the worker can't see itself. `observe` reads the
408
- * worker's TRACE (what it actually did — every tool call, cost, failure) and
409
- * produces two streams:
410
- * - `findings` / `report` — fed back DOWN (a steer for the next attempt) and
411
- * OUT (the operator-facing "what I noticed + what to change").
412
- * - `learned` — durable facts written to the cross-run `Corpus` so the NEXT
413
- * run starts smarter (the continuous half of "continuous self-improvement").
414
- *
415
- * Findings are TRACE-derived, never JUDGE-derived (`derived_from_judge:false`):
416
- * the observer reads behavior, never the acceptance verdict — the selector≠judge
417
- * firewall (docs/learning-flywheel.md). The observer is harness-agnostic: it
418
- * reads a trace + an output, so it watches opencode, codex, hermes, or a BYO
419
- * agent identically.
420
- */
421
-
422
- interface ObserveInput {
423
- /** What the worker was asked to do. */
424
- task: string;
425
- /** What it produced (its final answer / artifact summary). */
426
- output: string;
427
- /** The worker's trace — any event array (sandbox events, tool-call records). */
428
- trace: ReadonlyArray<unknown>;
429
- /** Terminal status only (passed/failed/unknown) — NOT a judge score; the
430
- * observer never reads the verdict, it reads behavior. */
431
- outcome?: 'passed' | 'failed' | 'unknown';
432
- /** Provenance back to the run. */
433
- runId?: string;
434
- }
435
- interface ObserveOptions {
436
- /** The model-call seam (agent-eval `createChatClient`: router / cli-bridge / …). */
437
- chat: ChatClient;
438
- model?: string;
439
- /** When set, learned facts are appended (idempotent) for the next run to read. */
440
- corpus?: Corpus;
441
- /** Tags written onto learned facts + used by the next run's corpus query. */
442
- tags?: ReadonlyArray<string>;
443
- signal?: AbortSignal;
444
- /** Cap the trace lines fed to the observer (keeps the call cheap). Default 80. */
445
- maxTraceLines?: number;
446
- /** Override the analyst's system instruction — the prompt that turns a trace into
447
- * findings + recommended_actions. The analyst IS the steerer, so this is the knob a
448
- * prompt optimizer (GEPA) tunes. Omitted ⇒ the default observer instruction. The
449
- * firewall (trace-only, never the verdict) is structural (input has no score), so a
450
- * custom instruction cannot break it. */
451
- analystInstruction?: string;
452
- }
453
- /** The default observer instruction — exported so an optimizer can seed its population. */
454
- declare const defaultAnalystInstruction: string;
455
- interface Observation {
456
- findings: AnalystFinding[];
457
- /** Facts persisted to the corpus (empty when no corpus was supplied). */
458
- learned: CorpusRecord[];
459
- /** Operator-facing markdown: what the observer noticed + what to change. */
460
- report: string;
461
- }
462
- declare function observe(input: ObserveInput, opts: ObserveOptions): Promise<Observation>;
463
- /** Operator-facing report, split by who should act. The agent block is the
464
- * steer; the operator block is the advice. */
465
- declare function renderReport(findings: ReadonlyArray<AnalystFinding>): string;
466
-
467
- /**
468
- * harvestCorpus — production traces → corpus, the G2 bridge (the playbook's step 6).
469
- * The flywheel's write side, batched: run the firewalled `observe()` analyst over a
470
- * stream of completed runs (yesterday's production traces, a benchmark's rollouts, a
471
- * fleet's day) and accrete the trace-derived facts into the durable corpus.
472
- *
473
- * Store-agnostic by design: the caller maps its trace store's rows (a
474
- * `ProductionTraceSink` ndjson, OTLP spans, RunRecords) to `ObserveInput` — task text,
475
- * final output, the event trace, terminal outcome. The analyst reads BEHAVIOR only
476
- * (the firewall is structural: the input carries no judge verdict), and corpus appends
477
- * are idempotent on (claim + tags), so re-harvesting the same window is safe.
478
- *
479
- * The nightly product job is then three lines:
480
- * const runs = mapSinkRowsToObserveInputs(await readSink(yesterday))
481
- * const report = await harvestCorpus({ runs, chat, corpus, tags: ['gtm-agent'] })
482
- * log(report) // runsObserved / findings / learned / failures
483
- *
484
- * NOTE on the read side: harvesting is safe and cheap; *injecting* facts back into runs
485
- * is the measured danger zone — naive unconditional priming tested NEGATIVE (−11.6pp,
486
- * context pollution; result now in .evolve/current.json + memory). Gate any priming design on its
487
- * own A/B; the corpus's first consumers are operators and optimizers, not prompts.
488
- */
489
-
490
- interface HarvestCorpusOptions {
491
- /** The completed runs to analyze — map your store's rows to `ObserveInput`. */
492
- runs: AsyncIterable<ObserveInput> | Iterable<ObserveInput>;
493
- /** The model-call seam (agent-eval `createChatClient`). */
494
- chat: ChatClient;
495
- model?: string;
496
- /** The durable corpus the facts accrete into. */
497
- corpus: Corpus;
498
- /** Tags written onto learned facts (the product/domain key the read side queries by). */
499
- tags?: ReadonlyArray<string>;
500
- /** Override the analyst instruction (the GEPA-tunable knob). */
501
- analystInstruction?: string;
502
- /** Runs analyzed in parallel. Default 4. */
503
- concurrency?: number;
504
- /** Hard cap on runs consumed from the stream (a cost guard for unbounded stores). */
505
- maxRuns?: number;
506
- signal?: AbortSignal;
507
- }
508
- interface HarvestFailure {
509
- runId: string;
510
- error: string;
511
- }
512
- interface HarvestReport {
513
- runsObserved: number;
514
- /** Total findings the analyst produced (including ones already known). */
515
- findings: number;
516
- /** NEW facts actually appended (idempotent dedup excludes re-learned ones). */
517
- learned: number;
518
- /** Per-run analysis failures — reported, never silently dropped. */
519
- failures: HarvestFailure[];
520
- }
521
- declare function harvestCorpus(opts: HarvestCorpusOptions): Promise<HarvestReport>;
522
-
523
- /**
524
- * Adapt an `ExecutorFactory` into a `SandboxClient` for `runLoop`. The factory is
525
- * instantiated fresh per `streamPrompt` (mirrors the per-spawn executor lifecycle):
526
- * run once on the prompt, emit the terminal result event, tear down.
527
- */
528
- declare function inlineSandboxClient(factory: ExecutorFactory<unknown>): SandboxClient;
529
-
530
- /**
531
- * `loopDispatch` — turn `runLoop` into an agent-eval campaign dispatch.
532
- *
533
- * Without this adapter a consumer wiring `runLoop` into `runProfileMatrix` /
534
- * `runCampaign` has to, by hand, every time: (a) build an `ExecCtx` with a
535
- * sandbox client, (b) adapt the campaign `DispatchContext.trace` into a
536
- * `LoopTraceEmitter` (or lose all loop trace correlation), and (c) remember to
537
- * forward the loop's cost + tokens via `ctx.cost` (forgetting it yields a
538
- * `{0,0}` cell the backend-integrity guard reads as a stub). Three foot-guns,
539
- * the third silent. The fleet's products skipped (c) and fell back to a
540
- * `workerRecords[]` side-channel — the exact anti-pattern the substrate exists
541
- * to kill.
542
- *
543
- * `loopDispatch` collapses all three into one typed call:
544
- *
545
- * const dispatch = loopDispatch({
546
- * sandboxClient,
547
- * toLoopOptions: (scenario, profile) => ({ driver, agentRun, output, validator, task }),
548
- * })
549
- * await runProfileMatrix({ profiles, scenarios, dispatch, judges, commitSha })
550
- *
551
- * Usage is reported automatically; trace events are forwarded automatically;
552
- * the ctx is built automatically. The seam becomes impossible to mis-wire.
553
- *
554
- * Typed structurally against the campaign `DispatchContext` (imported type-only
555
- * from `@tangle-network/agent-eval/campaign`) — a downward dependency, never an
556
- * inversion.
557
- */
558
-
559
- /** runLoop options minus the `ctx` (loopDispatch builds the ctx). */
560
- type LoopOptionsForDispatch<Task, Output, Decision> = Omit<RunLoopOptions<Task, Output, Decision>, 'ctx'>;
561
- interface LoopDispatchOptions<Task, Output, Decision, TScenario extends Scenario, TArtifact> {
562
- /** Sandbox client used for every cell's `runLoop`. Supplied once. */
563
- sandboxClient: SandboxClient;
564
- /** Build the per-cell runLoop options from the scenario (+ profile, when
565
- * used with `runProfileMatrix`). */
566
- toLoopOptions: (scenario: TScenario, profile: AgentProfile) => LoopOptionsForDispatch<Task, Output, Decision>;
567
- /** Map the finished loop to the artifact the judges score. Default:
568
- * `result.winner?.output`. A loop with no winner yields `undefined` (judges
569
- * skip the cell) — but the loop's token usage is STILL reported, so the
570
- * integrity guard sees real activity. */
571
- toArtifact?: (result: LoopResult<Task, Output, Decision>) => TArtifact;
572
- /** Forward `loop.*` trace events into the campaign's scoped trace so loop
573
- * spans correlate with the cell. Default true. */
574
- forwardTrace?: boolean;
575
- /** Cost-meter source label for the loop's spend. Default `'loop'`. */
576
- costSource?: string;
577
- }
578
- /**
579
- * Adapter for `runProfileMatrix` (profile is an axis). Returns a
580
- * `ProfileDispatchFn` that runs `runLoop` per (profile, scenario) cell and
581
- * reports usage automatically.
582
- */
583
- declare function loopDispatch<Task, Output, Decision, TScenario extends Scenario, TArtifact>(opts: LoopDispatchOptions<Task, Output, Decision, TScenario, TArtifact>): ProfileDispatchFn<TScenario, TArtifact>;
584
-
585
- /**
586
- * The general agentic primitive — sequential (depth) and parallel (breadth) over a shared,
587
- * checkable artifact, driven through the keystone Supervisor as one recursive `Agent.act`.
588
- *
589
- * The domain lives behind ONE seam — `AgenticSurface` (open an artifact, list tools, call a tool,
590
- * score the artifact, close it). EnterpriseOps implements it (seed a gym DB, MCP tools, SQL
591
- * verifier); Commit0/AppWorld/terminal-bench implement it the same way (a repo workspace, shell
592
- * tools, the test suite). The drivers below are domain-blind: they run over any surface.
593
- *
594
- * Two shapes, the agent's POMDP rollout as the unit:
595
- * - DEPTH one persistent artifact carried across shots. Each shot the agent works the tool loop;
596
- * between shots a trace-analyst (selector≠judge: reads the trajectory, never the score)
597
- * steers the resumed session toward what's unfinished. shot n stands on shot n-1's
598
- * artifact state + history. This is continuation — long-horizon, same artifact.
599
- * - BREADTH K independent artifacts, each a fresh rollout, the deployable verifier picks the best.
600
- *
601
- * Both are an `Agent` whose `act` spawns leaf shots through `scope.spawn` and reacts via
602
- * `scope.next()` — so the conserved budget pool meters them (equal-k by construction), the journal
603
- * records the tree, and the same primitive nests. `runAgentic` runs the chosen driver through
604
- * `createSupervisor().run`. The leaf (one shot over a handle) is resolved per-spawn from a
605
- * surface-closed registry — the open `Executor` seam, not bespoke per-benchmark glue.
606
- */
607
-
608
- interface AgenticTask {
609
- readonly id: string;
610
- readonly systemPrompt: string;
611
- readonly userPrompt: string;
612
- /** Opaque domain payload the surface reads (EOPS: servers/verifiers/tools). Drivers never read it. */
613
- readonly meta?: Record<string, unknown>;
614
- }
615
- interface ArtifactHandle {
616
- readonly id: string;
617
- readonly surface: string;
618
- /** Opaque per-artifact context the surface stashes (EOPS: the seeded gym server + db id). */
619
- readonly ctx?: unknown;
620
- }
621
- interface AgenticTool {
622
- readonly type: 'function';
623
- readonly function: {
624
- name: string;
625
- description?: string;
626
- parameters: Record<string, unknown>;
627
- };
628
- }
629
- interface SurfaceScore {
630
- passes: number;
631
- total: number;
632
- /** Checks excluded as malformed (data defect, not the agent). `total === 0` ⇒ unscoreable. */
633
- errored: number;
634
- }
635
- /** A stateful, checkable environment an agent operates over with tools. Open behind one interface. */
636
- interface AgenticSurface {
637
- readonly name: string;
638
- open(task: AgenticTask): Promise<ArtifactHandle>;
639
- tools(task: AgenticTask, handle: ArtifactHandle): Promise<AgenticTool[]>;
640
- call(handle: ArtifactHandle, name: string, args: Record<string, unknown>): Promise<string>;
641
- score(task: AgenticTask, handle: ArtifactHandle): Promise<SurfaceScore>;
642
- close(handle: ArtifactHandle): Promise<void>;
643
- }
644
- interface AgenticOptions {
645
- routerBaseUrl: string;
646
- routerKey: string;
647
- model: string;
648
- temperature?: number;
649
- /** Completion cap per worker turn — REQUIRED for thinking models (they burn unbounded
650
- * budgets on reasoning and return empty content without it). Omitted ⇒ provider default. */
651
- maxTokens?: number;
652
- /** Turns the agent may take within ONE shot before the driver intervenes. */
653
- innerTurns?: number;
654
- /** The depth STEERER's analyst instruction (observe()'s system prompt). The knob a
655
- * prompt optimizer (GEPA) tunes — the analyst IS the steerer. Omitted ⇒ the default. */
656
- analystInstruction?: string;
657
- /** The critic's model — lets the analyst be a stronger (or cheaper) model than the
658
- * worker. Omitted ⇒ the worker's `model`. */
659
- analystModel?: string;
660
- /** Across-run learning: when set, the analyst's observe() pass appends trace-derived
661
- * facts here (the flywheel write side). Priming (the read side) is the caller's move —
662
- * query the corpus and fold facts into the task's systemPrompt before runAgentic. */
663
- corpus?: Corpus;
664
- /** Tags written onto learned facts (and used by the caller's priming query). */
665
- corpusTags?: string[];
666
- }
667
- type Msg = Record<string, unknown>;
668
- interface ShotResult {
669
- messages: Msg[];
670
- score: number;
671
- passes: number;
672
- total: number;
673
- completions: number;
674
- toolErrors: number;
675
- }
676
- interface AgenticRunResult {
677
- /** The strategy name (built-in 'depth'/'breadth' or a custom strategy's name). */
678
- mode: string;
679
- score: number;
680
- resolved: boolean;
681
- completions: number;
682
- /** DEPTH: score after each shot — the progress-over-rounds curve. BREADTH: best-so-far per rollout. */
683
- progression: number[];
684
- shots: number;
685
- /** The cost vector, stamped by `runAgentic` from the Supervisor's conserved pool: real
686
- * router tokens, priced usd (0 when the model is unpriced — never fabricated), wall ms. */
687
- usd: number;
688
- ms: number;
689
- tokens: {
690
- input: number;
691
- output: number;
692
- };
693
- }
694
- /** DEPTH: one persistent artifact, carried across analyst-steered shots. */
695
- declare function depthDriver(surface: AgenticSurface, task: AgenticTask, opts: AgenticOptions, cfg: {
696
- maxShots: number;
697
- }): Agent<unknown, Outcome<unknown>>;
698
- /** BREADTH: K independent rollouts (each own artifact), verifier picks the best. */
699
- declare function breadthDriver(_surface: AgenticSurface, task: AgenticTask, opts: AgenticOptions, cfg: {
700
- width: number;
701
- }): Agent<unknown, Outcome<unknown>>;
702
- /**
703
- * A Strategy is HOW you spend the compute budget to beat the Environment's check — it
704
- * builds the driver `Agent` the Supervisor runs. This is the OPEN extension point: a dev
705
- * authors their own by implementing `driver()` to return an Agent whose `act()` spawns
706
- * shots/analysts via `scope.spawn` / `scope.next` / `scope.send`. The two built-ins are
707
- * the reference implementations to copy:
708
- * sample — K INDEPENDENT attempts, keep the best-verifying (best-of-N / resample).
709
- * refine — attempt → observe() reads the trace → steer the next → repeat (iterate).
710
- * (A multi-agent "team" is just a Strategy whose driver spawns several different agents.)
711
- */
712
- interface Strategy {
713
- readonly name: string;
714
- driver(surface: AgenticSurface, task: AgenticTask, opts: AgenticOptions, budget: number): Agent<unknown, Outcome<unknown>>;
715
- }
716
- declare const sample: Strategy;
717
- declare const refine: Strategy;
718
- /** A role for one shot — multi-agent loops (researcher + engineer, a panel of k
719
- * researchers) give each shot its own system prompt and optionally its own model. */
720
- interface ShotPersona {
721
- /** Replaces the task's systemPrompt for a FRESH shot; on a carried conversation it is
722
- * injected as a hand-off message (the transcript's earlier roles stay intact). */
723
- systemPrompt?: string;
724
- /** Per-shot model override (e.g. a stronger model for the engineer shot). */
725
- model?: string;
726
- }
727
- interface ShotSpec {
728
- /** present ⇒ continue this artifact (depth); absent ⇒ the shot opens a fresh one (sample/restart). */
729
- handle?: ArtifactHandle;
730
- messages?: Msg[];
731
- steer?: string;
732
- persona?: ShotPersona;
733
- /** Restrict THIS shot to a subset of the domain's tools (by name) — focus a shot on
734
- * the relevant capabilities. Restriction-only; unknown names throw. Omitted ⇒ all. */
735
- tools?: string[];
736
- }
737
- interface StrategyResult {
738
- score: number;
739
- resolved: boolean;
740
- completions: number;
741
- progression: number[];
742
- shots: number;
743
- }
744
- /** Artifact lifecycle a strategy may manage itself — open/close ONLY. Raw `call`/`score`
745
- * are withheld: scores reach the body solely through `shot()`'s ShotResult (the
746
- * harness-verified channel), so a body cannot peek the check or fabricate around it. */
747
- interface StrategyArtifacts {
748
- readonly name: string;
749
- open(task: AgenticTask): Promise<ArtifactHandle>;
750
- close(handle: ArtifactHandle): Promise<void>;
751
- }
752
- /** What a strategy body composes with: the artifact lifecycle, the budget, and the two steps. */
753
- interface StrategyCtx {
754
- /** Open/close artifacts the body manages itself (e.g. one persistent handle for depth). */
755
- readonly surface: StrategyArtifacts;
756
- readonly task: AgenticTask;
757
- readonly opts: AgenticOptions;
758
- readonly budget: number;
759
- readonly scope: Scope<Outcome<unknown>>;
760
- /** Run ONE worker shot; its harness-scored result, or null if it went down. */
761
- shot(spec?: ShotSpec): Promise<ShotResult | null>;
762
- /** The firewalled critic reads the trajectory → a steer string, or null on COMPLETE/down. */
763
- critique(messages: Msg[]): Promise<string | null>;
764
- /** The RAW analyst channel: the firewalled critic answers `instruction` over the
765
- * trajectory verbatim — no findings extraction, so verdict-shaped formats
766
- * (CONTINUE/STOP decisions, calibrated predictions) survive. Same firewall:
767
- * trajectory in, never scores. Null when the analyst went down. */
768
- consult(messages: Msg[], instruction: string): Promise<string | null>;
769
- /** The tools THIS artifact's task actually offers (names + descriptions only — never
770
- * the implementations). Tool sets vary per task on heterogeneous domains; a strategy
771
- * that restricts shots MUST select from this list, never from hardcoded names. */
772
- listTools(handle: ArtifactHandle): Promise<Array<{
773
- name: string;
774
- description?: string;
775
- }>>;
776
- }
777
- /** Author a Strategy from the composable steps — the open, compact way. */
778
- declare function defineStrategy(name: string, run: (ctx: StrategyCtx) => Promise<StrategyResult>): Strategy;
779
- /** A NEW strategy, authored from the steps (~20 lines): refine, but when a steered shot
780
- * fails to improve the score it ABANDONS that line and restarts fresh (branch-when-stuck)
781
- * — the widen/MCTS idea the depth-stuck failure motivated. Scored keep-best (the best
782
- * checkpoint across all lines), the deployable metric. This is the "experts build BETTER
783
- * optimizations" path: a new technique, compact, with zero Supervisor ceremony. */
784
- declare const adaptiveRefine: Strategy;
785
- /** The explore-then-exploit MIX: spend ⌈budget/2⌉ on independent samples (kept open),
786
- * then refine the best-verifying line with the remaining budget. Sample's basin escape +
787
- * refine's accumulation — the third built-in, authored from the public steps. */
788
- declare const sampleThenRefine: Strategy;
789
- interface RunAgenticOptions extends AgenticOptions {
790
- surface: AgenticSurface;
791
- task: AgenticTask;
792
- /** Lifecycle observability — every spawn/settle (shots, analysts) streams here live.
793
- * The seam online watchdogs/route-auditors subscribe to. */
794
- hooks?: RuntimeHooks;
795
- /** A Strategy (the open way) — author/pass your own. Overrides `mode` when present. */
796
- strategy?: Strategy;
797
- /** Built-in shorthand: 'depth'→refine, 'breadth'→sample. Default 'depth'. */
798
- mode?: 'depth' | 'breadth';
799
- /** budget: refine→max shots; sample→rollout width. */
800
- budget: number;
801
- rootBudget?: Budget;
802
- }
803
- /** Run a Strategy through the keystone Supervisor — `Agent.act` over a conserved-budget Scope. */
804
- declare function runAgentic(opts: RunAgenticOptions): Promise<AgenticRunResult>;
805
-
806
- /**
807
- * runBenchmark — the packaged optimization suite. Define a domain by implementing an
808
- * `Environment` (open / tools / call / score / close); get the optimization strategies
809
- * compared, scored by your own deployable check, with a paired-bootstrap report — free.
810
- *
811
- * The mental model: you have a TASK + a deployable CHECK + a compute BUDGET. A strategy
812
- * is how you spend the budget to beat the check. Two built-ins:
813
- *
814
- * sample — N independent attempts, keep the best-verifying one. (best-of-N / resample)
815
- * refine — attempt → a critic reads the trace → steer the next → repeat. (iterate-with-feedback)
816
- *
817
- * Both run at equal budget through the Supervisor's conserved pool; the headline is the
818
- * paired lift of refine over sample. Author your own strategy with `defineStrategy`.
819
- */
820
-
821
- /** A checkable task domain — implement these 5 hooks and the suite does the rest. The
822
- * same seam as `AgenticSurface`; `Environment` is the RL/gym-standard name for it. */
823
- type Environment = AgenticSurface;
824
- interface BenchmarkConfig {
825
- /** The task domain (5 hooks). */
826
- environment: Environment;
827
- /** The tasks to score across. */
828
- tasks: AgenticTask[];
829
- /** The worker: model + router + (optional) the critic's instruction (the steerer knob). */
830
- worker: AgenticOptions;
831
- /** Which strategies to compare. Pass the built-ins (`refine`, `sample`) or your own.
832
- * Default: [sample, refine]. */
833
- strategies?: Strategy[];
834
- /** Shots (refine) / width (sample) — the equal compute budget per strategy. Default 3. */
835
- budget?: number;
836
- /** Tasks scored in parallel. Default 3. */
837
- concurrency?: number;
838
- /** Progress hook — fires as each task settles (the live-monitoring seam: append to a
839
- * progress file, render a tree, stream to a dashboard). `done` counts settled tasks. */
840
- onTask?: (row: BenchmarkTaskRow, done: number, total: number) => void;
841
- /** Lifecycle observability — every spawn/settle of every cell's shots/analysts streams
842
- * here live (the watchdog/route-auditor seam, passed through to `runAgentic`). */
843
- hooks?: RuntimeHooks;
844
- }
845
- interface BenchmarkLift {
846
- /** Mean of paired deltas (refine − sample). */
847
- mean: number;
848
- low: number;
849
- high: number;
850
- n: number;
851
- }
852
- /** One strategy's outcome on one task — the per-task cell an optimizer consumes. */
853
- interface BenchmarkCell {
854
- score: number;
855
- resolved: boolean;
856
- /** The progress curve (refine: score per shot; sample: best-so-far per rollout). */
857
- progression: number[];
858
- usd: number;
859
- ms: number;
860
- tokens: {
861
- input: number;
862
- output: number;
863
- };
864
- }
865
- interface BenchmarkTaskRow {
866
- taskId: string;
867
- /** Per-strategy cells; absent when the task errored before completing all strategies. */
868
- cells?: Record<string, BenchmarkCell>;
869
- /** Per-strategy failures on this task: the strategy competed, threw, and scored an
870
- * honest zero — it loses, it does not poison the row. The message is kept so a later
871
- * generation's author can see WHY a candidate died. */
872
- errors?: Record<string, string>;
873
- /** Why the task was excluded (infra/setup failure) — never silently dropped. */
874
- error?: string;
875
- }
876
- interface BenchmarkStrategySummary {
877
- /** Mean verifier score (0..1). */
878
- score: number;
879
- /** Fraction of tasks fully resolved. */
880
- resolved: number;
881
- /** Mean cost vector per task. */
882
- usd: number;
883
- ms: number;
884
- }
885
- interface BenchmarkReport {
886
- n: number;
887
- excluded: number;
888
- /** Per-strategy means (keyed by strategy.name). */
889
- perStrategy: Record<string, BenchmarkStrategySummary>;
890
- /** The full per-task × per-strategy table — the LOSSES an optimizer (GEPA, a
891
- * strategy-author, an operator) consumes. Includes errored tasks with the reason. */
892
- perTask: BenchmarkTaskRow[];
893
- /** The non-dominated strategies on (score ↑, $/task ↓) — collapse-last, per the canon:
894
- * a strategy that ties on score at half the cost WINS and a scalar would hide it. */
895
- pareto: string[];
896
- /** The headline when both `refine` and `sample` ran: paired-bootstrap lift of refine over sample. */
897
- refineVsSample?: BenchmarkLift;
898
- }
899
- /** Run the requested strategies over the tasks, scored by the Environment's own check.
900
- * Resilient: a task whose rollouts fail (transient infra) is excluded from the stats but
901
- * reported in `perTask` with the error — never silently dropped. */
902
- declare function runBenchmark(cfg: BenchmarkConfig): Promise<BenchmarkReport>;
903
- /** Pretty-print a report — the "free optimization" verdict, with the cost vector. */
904
- declare function printBenchmarkReport(report: BenchmarkReport): void;
905
-
906
- /**
907
- * createMcpEnvironment — wrap any MCP server as an `Environment` (the product-adoption
908
- * primitive: a product's agent tools are usually already an MCP surface, so the domain
909
- * only writes the lifecycle hooks — open a scoped artifact, score it with a deployable
910
- * check, close it — and the tool plumbing is derived from the server).
911
- *
912
- * What the helper owns (the generic 80%, hardened on the EnterpriseOps gym):
913
- * - JSON-RPC `tools/list` → `AgenticTool[]`, with schemas coerced to the
914
- * OpenAI-tool-valid shape (top-level oneOf/anyOf/allOf/enum/not are rejected by
915
- * tool-calling providers; nested combinators are fine).
916
- * - JSON-RPC `tools/call` → the tool's text content (errors surfaced as `ERROR: …`
917
- * strings — a bad call is the agent's outcome, not an infra fault).
918
- * - SSE response parsing (streamable-HTTP MCP servers answer with `data:` lines).
919
- * - Bounded retry with backoff on thrown fetches (transient network ≠ task failure).
920
- *
921
- * What the domain supplies: `open` (create/seed the per-task artifact and return its
922
- * MCP endpoint — url + headers carry the per-artifact scoping, e.g. a database id
923
- * header), `score` (the deployable check), and optional `close`/`selectTools`.
924
- */
925
-
926
- /** Where a handle's MCP server lives; headers carry per-artifact scoping. */
927
- interface McpEndpoint {
928
- url: string;
929
- headers?: Record<string, string>;
930
- }
931
- interface McpEnvironmentOptions {
932
- name: string;
933
- /** Create/seed the per-task artifact; return its handle + the MCP endpoint scoped to it. */
934
- open(task: AgenticTask): Promise<{
935
- handle: ArtifactHandle;
936
- endpoint: McpEndpoint;
937
- }>;
938
- /** The deployable check over the artifact's current state. */
939
- score(task: AgenticTask, handle: ArtifactHandle): Promise<SurfaceScore>;
940
- /** Teardown (delete the seeded artifact). Optional — omit for stateless servers. */
941
- close?(handle: ArtifactHandle): Promise<void>;
942
- /** Restrict/order the server's tools per task (e.g. the task's selected_tools). Default: all. */
943
- selectTools?(task: AgenticTask, all: AgenticTool[]): AgenticTool[];
944
- /** Cap on a tool result's text fed back to the worker. Default 1500 chars. */
945
- maxResultChars?: number;
946
- }
947
- declare function createMcpEnvironment(opts: McpEnvironmentOptions): Environment;
948
-
949
- /**
950
- * @experimental
951
- *
952
- * Analyst-on-scope (G1) — the analyze→findings→steer wire over the reactive `Scope`.
953
- *
954
- * The analyst runs over the children drained so far and hands its findings to the steer
955
- * decision behind a provenance firewall (`assertTraceDerivedFindings`) that keeps the external
956
- * write-only judge out of that decision (selector ≠ judge). The reactive `Scope` has no rounds:
957
- * a combinator's `act` asks a `ScopeAnalyst` to turn the children it has drained off
958
- * `scope.next()` SO FAR into `AnalystFinding[]`, and steers from THOSE findings through a single
959
- * `SteerContext`.
960
- *
961
- * The analyst itself is not a new type — it is "just an `Agent<unknown, AnalystFinding[]>`" the
962
- * combinator spawns over a child's trace (harness `null`/`cli`). `createScopeAnalyst` spawns that
963
- * agent through `Scope.spawn` (so its compute is metered by the conserved pool like any child),
964
- * drains its settlement, then enforces the firewall on the way out — a judge-derived finding
965
- * ABORTS, it is never filtered. Fail loud: a down analyst, a non-array result, or a tainted finding
966
- * throws; there is no silent empty-findings path that would let a combinator steer on nothing.
967
- */
968
-
969
- declare const assertTraceDerivedFindings: AssertTraceDerivedFindings;
970
- /**
971
- * The analyst run an `Agent<unknown, AnalystFinding[]>` performs over the children settled so far.
972
- * The combinator supplies the analyst's task projection (how to frame the drained settlements as
973
- * the analyst's input) — the analyst's `act` reads the trace and returns its raw findings; the
974
- * firewall is enforced afterwards by `createScopeAnalyst`, not by the analyst itself.
975
- */
976
- interface CreateScopeAnalystOptions<D> {
977
- /** The analyst agent the combinator spawns over the trace. `harness` is the persona's choice
978
- * (`null` for an inline router analyst, a `BackendType` for a sandboxed one). Its `act` returns
979
- * the RAW findings; this module asserts the firewall on them before returning. */
980
- readonly analyst: Agent<unknown, ReadonlyArray<AnalystFinding>>;
981
- /** Build the analyst agent's task from the analyze input (the root-task framing + the children
982
- * drained so far). Pure projection — the analyst interprets it, this never reads it. */
983
- buildTask(input: ScopeAnalyzeInput<D>): unknown;
984
- /** The conserved budget reserved for one analyst spawn. The pool reserves against it and fails
985
- * closed; an analyst that cannot be admitted is a fail-loud abort, never silent empty findings. */
986
- readonly budget: Budget;
987
- /** Trace/journal label for the spawned analyst child. Default `'analyst'`. */
988
- readonly label?: string;
989
- }
990
- /**
991
- * Build a `ScopeAnalyst` that spawns the analyst agent through `Scope.spawn` (so its compute is
992
- * metered by the conserved pool), drains its single settlement, and enforces the trace-derived
993
- * firewall before returning. The `scope` is the SAME scope the combinator is draining its children
994
- * from — the analyst is spawned as a sibling and its result is read off `scope.next()` in cursor
995
- * order, replay-safe like any other child.
996
- *
997
- * Fail loud (no silent empty findings):
998
- * - the pool refuses the analyst spawn → `AnalystError` (the steer would otherwise run on nothing)
999
- * - the analyst settles `down` → `AnalystError` (a broken capture path, not a verdict)
1000
- * - the analyst returns a non-array → `PlannerError`
1001
- * - any finding cites judge-derived metric evidence → `PlannerError` via the firewall
1002
- */
1003
- declare function createScopeAnalyst<D>(scope: Scope<Outcome<D>>, options: CreateScopeAnalystOptions<D>): ScopeAnalyst<D>;
1004
- /**
1005
- * Project a `ScopeAnalyzeInput` into the `AnalystRegistry.run` arguments. The registry runs over a
1006
- * `runId` + `AnalystRunInputs` (a trace store / run record / artifact dir), NOT in-memory scope
1007
- * settlements — so the CALLER owns the projection from the combinator's drained children to the
1008
- * registry's inputs (e.g. the trace store the run already wrote). This adapter never invents that
1009
- * bridge; it only runs the projected inputs and firewalls the merged findings.
1010
- */
1011
- interface RegistryAnalyzeProjection {
1012
- readonly runId: string;
1013
- readonly inputs: AnalystRunInputs;
1014
- /** Optional `run` opts (e.g. `priorFindings`) forwarded verbatim to the registry. */
1015
- readonly opts?: Parameters<AnalystRegistryLike['run']>[2];
1016
- }
1017
- /**
1018
- * A `ScopeAnalyst` backed by an `AnalystRegistry` — the panel-of-analysts seam. The registry merges
1019
- * N analyst KINDS into one `AnalystRunResult.findings`; `analyze` runs it over the caller-projected
1020
- * `{ runId, inputs }` and pipes the merged findings through the SAME `assertTraceDerivedFindings`
1021
- * firewall `createScopeAnalyst` uses (single-sourced selector≠judge). Distinct from `panel()`
1022
- * (judges-vs-one-artifact) — this is analysts-over-a-trace, the diagnosis side of the wire.
1023
- *
1024
- * Fail loud: a registry that throws propagates; a judge-derived finding aborts via the firewall.
1025
- * The projection is the caller's (`buildInputs`) — if the scope settlements do not cleanly map to
1026
- * the registry's `AnalystRunInputs`, that is a caller-side contract gap, surfaced there, not papered
1027
- * over with a fabricated input here.
1028
- */
1029
- declare function registryScopeAnalyst<D>(registry: AnalystRegistryLike, buildInputs: (input: ScopeAnalyzeInput<D>) => RegistryAnalyzeProjection): ScopeAnalyst<D>;
1030
- /**
1031
- * Build the `SteerContext` a combinator reads to steer (its `loopUntil.until`, `widen` gate, any
1032
- * future steer). One place enforces the firewall: `findings` is asserted trace-derived before it is
1033
- * surfaced, and `lastValidScore` is provided for OBSERVABILITY only — a combinator that steers off
1034
- * it re-introduces selector = judge, the coupling the architecture forbids.
1035
- *
1036
- * `findings` is re-asserted here even when it came from `createScopeAnalyst` (which already asserted
1037
- * it): the assertion is cheap and idempotent, and a `SteerContext` may be built from findings that
1038
- * arrived by another path (a caller-supplied diagnosis). Belt-and-suspenders on the one coupling
1039
- * that must never leak.
1040
- */
1041
- declare function buildSteerContext<D>(findings: ReadonlyArray<AnalystFinding>, settledSoFar: ReadonlyArray<Settled<Outcome<D>>>): SteerContext<D>;
1042
-
1043
- /**
1044
- * @experimental
1045
- *
1046
- * The generic combinator library — the content-free act-bodies the wave's §1 contract froze.
1047
- *
1048
- * Each export is a `CombinatorShape<Task, D>` (an alias of `LoopShape<Task, D>`): a factory
1049
- * `(ShapeContext) => Agent<Task, Outcome<D>>` whose `act` runs ONE composition shape over the
1050
- * keystone `Scope` — spawn children through `ctx.spawnChild` + `scope.spawn`, drain settlements
1051
- * via `scope.next()`, select across `done` children with the SINGLE-SOURCED `settledToIteration`
1052
- * + `defaultSelectWinner` (selector≠judge — never a re-rank behind the driver), and synthesize a
1053
- * terminal `Outcome<D>`.
1054
- *
1055
- * The shapes carry NO domain: a "research sweep over angles" is `fanout(angles, { synthesize })`
1056
- * under a research persona; a "code build test" is `pipeline([plan, implement, integrate])` under
1057
- * a coder persona. The SHAPE is here; the model/prompt/role/goal live on the `Persona` + task,
1058
- * threaded to each child verbatim by the spec-objects the builders take. No model name, prompt,
1059
- * role, or domain noun appears below.
1060
- *
1061
- * Two fail-loud invariants every combinator honors: a child the conserved pool cannot admit is a
1062
- * CONCRETE blocker (never an eager over-fan, never a silent drop), and a `blocked` outcome always
1063
- * names at least one blocker (a shape that cannot finish MUST say why — `blocked([])` throws).
1064
- */
1065
-
1066
- /**
1067
- * The single content-free valid-only winner selector. Among the gated-VALID children only
1068
- * (`verdict.valid === true`), pick by `strategy` — best score / smallest delivered artifact /
1069
- * earliest — ties broken by earliest index; returns `undefined` when NONE is valid (an ungated
1070
- * output can never win — the deliverable gate is the point). `sizeOf` (for `'smallest-artifact'`)
1071
- * reads the child's settled deliverable — the raw value a leaf settles, or the unwrapped `Outcome<D>`
1072
- * a delegate path produces; a domain passes e.g. patch diff-lines. This is the de-duplicated home of
1073
- * the selection logic previously copied per role.
1074
- */
1075
- declare function selectValidWinner<D>(opts?: {
1076
- strategy?: WinnerStrategy;
1077
- sizeOf?: (deliverable: D) => number;
1078
- }): FanoutWinnerSelector<D>;
1079
- /**
1080
- * `pipeline(stages)` — run the stages in order, feeding each stage's `done` deliverable into the
1081
- * next stage's task. The first stage that ends `blocked` (a child that went down, a child the
1082
- * pool would not admit, or a stage whose `collect` chose to block) short-circuits — its blockers
1083
- * ARE the pipeline's blockers, never coerced past a failed stage. The terminal stage's `done`
1084
- * deliverable is the pipeline's deliverable.
1085
- */
1086
- declare function pipeline<Task, D>(stages: ReadonlyArray<PipelineStage<Task, unknown, unknown>>): CombinatorShape<Task, D>;
1087
- /**
1088
- * `fanout(items, opts)` — spawn one child per item in a single round (bounded by the conserved
1089
- * pool's fail-closed admission), drain via `scope.next()`, then either synthesize over the
1090
- * gathered settlements (one SEPARATE synthesis child) or return the best-valid child via the
1091
- * single-sourced selector. A round that admitted zero children, or whose synthesis child could
1092
- * not be admitted, is a concrete blocker.
1093
- */
1094
- declare function fanout<Task, Item, D>(items: ReadonlyArray<Item>, opts: FanoutOptions<Item, D>): CombinatorShape<Task, D>;
1095
- /**
1096
- * `loopUntil(seed, spec)` — one `step` child per round; `fold` accumulates each settlement into
1097
- * the running state; `until` (reading the round's trace findings, NOT a fresh raw verdict) is
1098
- * the deployable stop. The conserved pool IS the loop bound: once `spawn` fails closed the loop
1099
- * stops. A loop that exhausted the pool without `until` ever satisfying is a concrete blocker.
1100
- *
1101
- * When `ctx.analyst` is set, each round runs it over the children settled so far and steers
1102
- * `until` on the resulting trace-derived findings (the analyst spawns into THIS scope, so its
1103
- * compute is conserved-pooled — equal-k holds by construction). Absent an analyst the findings
1104
- * argument is the empty array — never a fabricated finding (fail-loud honesty over a silent default).
1105
- */
1106
- declare function loopUntil<Task, State, D>(seed: State, spec: LoopUntilSpec<Task, State, D>): CombinatorShape<Task, D>;
1107
- /**
1108
- * `panel(spec)` — spawn the M judge children over the SAME artifact, drain their settlements,
1109
- * and fold them into a panel verdict via the pure WRITE-ONLY `merge` (a judge's output never
1110
- * reaches another judge's task; the merge never spawns or re-ranks). A `down` judge carries no
1111
- * verdict and is excluded from the merge denominator. A panel that admitted no judge is a
1112
- * concrete blocker before `merge` is consulted.
1113
- */
1114
- declare function panel<Task, Artifact, D>(spec: PanelSpec<Artifact, D>): CombinatorShape<Task, D>;
1115
- /**
1116
- * `verify(spec)` — an IMPLEMENT child produces a candidate, then a SEPARATE VERIFIER child grades
1117
- * it; only a `valid` verifier verdict ships. Any other outcome (implement down, verifier down,
1118
- * verifier verdict absent or not `valid`) is a concrete blocker carrying the failure verbatim —
1119
- * never a coerced "done". The implement child does not grade itself.
1120
- */
1121
- declare function verify<Task, Candidate, D>(spec: VerifySpec<Task, Candidate, D>): CombinatorShape<Task, D>;
1122
- /**
1123
- * `widen(spec)` — the streaming spawn-on-completion driver. Spawns the seed lineages, then REACTS
1124
- * to each `scope.next()`: on every settled child it consults `spec.gate.decide` and, when the gate
1125
- * returns `widen`, spawns AT MOST ONE more child toward the chosen lineage under the remaining
1126
- * conserved pool. `promising` is derived from the round's trace findings (the analyst seam),
1127
- * never a child's raw `verdict` — and the default gate (`flatWidenGate`) never widens, so the R2
1128
- * firewall stays dormant. Terminal selection is `spec.synthesize` over every settled lineage.
1129
- *
1130
- * When `ctx.analyst` is set, `decide` is consulted with that round's trace-derived findings;
1131
- * absent an analyst the findings argument is the empty array a flat gate ignores. The analyst
1132
- * spawns into THIS scope (conserved-pooled, so equal-k holds). Streaming caveat: a wired analyst
1133
- * drains its own child off the SHARED cursor by id-match, so on a NON-flat gate (which spawns
1134
- * widen children that are live concurrently) the analyst can consume a sibling's settlement before
1135
- * the widen loop sees it. The shipped default (`flatWidenGate`) never widens, so no widen child is
1136
- * ever live when the analyst runs and the wire is exact; a non-flat gate must drive the analyst on
1137
- * a scope whose siblings are quiesced, or read findings without the shared-cursor drain.
1138
- */
1139
- declare function widen<Task, Seed, D>(spec: WidenSpec<Seed, D>): CombinatorShape<Task, D>;
1140
- /**
1141
- * The flat default `ScopeWidenGate` — never widens, keeping the R2 selector≠judge collision
1142
- * dormant. A gate run passes this explicitly; a test asserts the default is flat.
1143
- */
1144
- declare function flatWidenGate<D>(): ScopeWidenGate<D>;
1145
-
1146
- /**
1147
- * @experimental
1148
- *
1149
- * The cross-run corpus (G2) — the learning-flywheel's durable accreted-fact store.
1150
- *
1151
- * `Corpus` is DISTINCT from the per-run `SpawnJournal` (decisions/replay) and `ResultBlobStore`
1152
- * (payloads): a `CorpusRecord` is a FACT one run LEARNED that a FUTURE run reads back (the
1153
- * world-model), not a replay input. This module owns the two impls the wave surface pins —
1154
- * `InMemoryCorpus` and `FileCorpus` (JSONL, append-only) — plus `renderCorpusToInstructions`,
1155
- * the READ side that projects accreted facts into a fresh `AgentProfile`'s instruction seams.
1156
- *
1157
- * The boundary is fail-loud, typed-outcome: `append` is idempotent on an identical record and
1158
- * returns a typed error (never throws, never a silent overwrite) on a conflicting re-append under
1159
- * the same `id`. Malformed records — a structurally-invalid `CorpusRecord` from disk or a caller —
1160
- * fail loud (the validator throws), since a corpus that silently accepts garbage would poison
1161
- * every downstream run that reads it back.
1162
- */
1163
-
1164
- /**
1165
- * In-memory `Corpus`. Keyed by record `id`; `append` validates the record, is idempotent on an
1166
- * identical re-append, and returns a typed `{ succeeded: false }` on a conflicting re-append under
1167
- * the same `id` (never overwrites). `query` routes through the single-sourced `applyFilter`.
1168
- */
1169
- declare class InMemoryCorpus implements Corpus {
1170
- private readonly byId;
1171
- append(record: CorpusRecord): Promise<{
1172
- succeeded: true;
1173
- } | {
1174
- succeeded: false;
1175
- error: string;
1176
- }>;
1177
- query(filter: CorpusFilter): Promise<ReadonlyArray<CorpusRecord>>;
1178
- }
1179
- /**
1180
- * JSONL on disk — one validated `CorpusRecord` per line, append-only. `query` replays the whole
1181
- * file, validating every line (a malformed line fails loud — a corrupted corpus must never read
1182
- * back silently) and folding by `id`: a later identical line dedups, a later conflicting line
1183
- * under the same `id` is a corruption (fail loud). `append` first replays to enforce the same
1184
- * idempotence/conflict contract as the in-mem impl, then fsyncs the new line so a crash between
1185
- * writes never loses an acknowledged fact. Shares the JSONL append-line spine with the spawn
1186
- * journal, but the interface stays separate (a learned fact is not a replay record).
1187
- */
1188
- declare class FileCorpus implements Corpus {
1189
- private readonly path;
1190
- constructor(path: string);
1191
- append(record: CorpusRecord): Promise<{
1192
- succeeded: true;
1193
- } | {
1194
- succeeded: false;
1195
- error: string;
1196
- }>;
1197
- query(filter: CorpusFilter): Promise<ReadonlyArray<CorpusRecord>>;
1198
- private load;
1199
- private appendLine;
1200
- }
1201
- /**
1202
- * The learning-flywheel READ side. Queries the corpus through `filter`, renders the matching facts
1203
- * (most-confident first, capped by `maxLines`) into instruction lines, and returns a FRESH
1204
- * `AgentProfile` with them merged in — never mutates the input profile. Default `target: 'prompt'`
1205
- * appends the lines to `prompt.instructions[]` (the additive append-line seam); `target:
1206
- * 'resources'` folds them into the single-blob `resources.instructions` string (preserving any
1207
- * existing blob, but failing loud on a non-string existing blob — a `resources.instructions` that
1208
- * was already an `AgentProfileResourceRef` cannot be string-appended without dropping it).
1209
- *
1210
- * An empty query result returns a fresh COPY of the profile with no instruction change (a valid
1211
- * "nothing learned yet" read, not an error).
1212
- */
1213
- declare function renderCorpusToInstructions(opts: RenderCorpusToInstructionsOptions): Promise<AgentProfile$1>;
1214
-
1215
- /**
1216
- * @experimental
1217
- *
1218
- * The personify layer impl — `definePersona` (the thin builder) + `runPersonified` (composes
1219
- * the persona + chosen shape onto the keystone `Supervisor`), plus `createShapeContext`, the
1220
- * seam that hands a shape its spawn helpers without it touching the registry.
1221
- *
1222
- * This file adds NO engine: `runPersonified` is `createSupervisor().run(rootAgent, task, …)`
1223
- * where `rootAgent` is the persona's chosen `LoopShape` applied to a `ShapeContext`. All the
1224
- * conserved-budget / journal / abort / typed-result machinery is the keystone's; this layer
1225
- * only wires the persona's CONTENT (root spec + directive + context + seams) into it.
1226
- *
1227
- * One non-obvious invariant it must honor: `createSupervisor().run` builds the root `Scope`
1228
- * with an EMPTY seam bag (`seams: {}`), so the built-in metered runtimes (router/sandbox/cli)
1229
- * cannot read their seams off `ExecutorContext` through the default supervisor path. A persona
1230
- * that supplies raw `seams` is therefore wrapped here into a registry whose resolved factories
1231
- * receive a ctx with the persona seams merged in — so a persona never has to pre-close its
1232
- * factories by hand. A persona may instead supply a fully-built `registry` and skip the wrap.
1233
- */
1234
-
1235
- /**
1236
- * Build a frozen `Persona`. Fails loud on the executors-supplied invariant: a persona with
1237
- * neither a pre-built registry nor a seam bag cannot resolve its built-in runtimes, so it is
1238
- * unrunnable — refuse it at definition time, not at the first spawn. Pure; no I/O.
1239
- */
1240
- declare function definePersona<D = unknown>(input: DefinePersonaInput<D>): Persona<D>;
1241
- /**
1242
- * Compose the persona + chosen shape onto a fresh keystone `Supervisor`. Resolves the shape
1243
- * (a factory verbatim, or a registered name through `builtinShapes`), applies it to a
1244
- * `ShapeContext`, and runs the resulting root `Agent` to a typed `SupervisedResult<Outcome>`.
1245
- * Fail loud on an unknown shape name or an unresolvable persona registry — never a silent
1246
- * default-shape fallback.
1247
- */
1248
- declare function runPersonified<Task, D>(options: RunPersonifiedOptions<Task, D>): Promise<SupervisedResult<Outcome<D>>>;
1249
-
1250
- /**
1251
- * @experimental
1252
- *
1253
- * The loop-shape registry — the OPEN, content-free extension point for the personify layer.
1254
- *
1255
- * A `LoopShape` is reusable STRUCTURE (how to decompose / fan out / verify / synthesize),
1256
- * parameterized by a persona's CONTENT. The registry lets a caller resolve a composed shape by
1257
- * NAME: register a factory once, then `runPersonified({ shape: '<name>' })` resolves it with zero
1258
- * edits elsewhere. `register` fails loud on a duplicate; `resolve` returns a typed outcome so an
1259
- * unknown name is a named error, never a silent default.
1260
- *
1261
- * No shape is pre-registered: the generic combinators (`pipeline`/`fanout`/`loopUntil`/`panel`/
1262
- * `verify`/`widen`) take spec arguments, so they are not bare zero-arg factories — a caller that
1263
- * wants name-resolution registers its own COMPOSED shape (a combinator already applied to its
1264
- * spec) on a registry instance. The registry carries SHAPE only; the domain lives on the persona.
1265
- */
1266
-
1267
- /**
1268
- * Build a fresh open `ShapeRegistry`. A factory is stored type-erased and re-cast on resolve — the
1269
- * caller asserts the `<Task, D>` it expects, exactly as the executor registry stores its factories.
1270
- */
1271
- declare function createShapeRegistry(): ShapeRegistry;
1272
- /** The default registry `runPersonified` resolves a shape name against. Empty by construction —
1273
- * a caller registers its own composed shapes; the engine ships no domain shape. */
1274
- declare const builtinShapes: ShapeRegistry;
1275
- /** Register a composed shape on the default `builtinShapes` registry — the one-call extension
1276
- * point a caller invokes so its shape is resolvable by name with zero edits to the engine. */
1277
- declare function registerShape<Task, D>(name: string, factory: LoopShape<Task, D>): void;
1278
-
1279
- /**
1280
- * @experimental
1281
- *
1282
- * Trajectory trace + cost ledger — the post-hoc tree reconstructor (§4 of `wave-types`).
1283
- *
1284
- * `trajectoryReport` rebuilds the WHOLE realized spawn tree from the durable
1285
- * `SpawnJournal` (+ optionally the `ResultBlobStore` for `done` artifacts): every node
1286
- * (driver AND leaf), the real parent/child edges, each node's terminal status, its OWN
1287
- * conserved `Spend`, and the `Spend` ROLLED UP over its subtree. Roll-up is a post-order
1288
- * fold over the parent edges: a node's `rolledUpSpend` is its own spend plus every
1289
- * descendant's, so a driver is charged for the fanout it caused — the root's roll-up is
1290
- * the whole run's conserved total (tokens + usd + iterations + ms).
1291
- *
1292
- * `equalKOnCost` compares separate runs (arms) on that conserved COST, not on raw
1293
- * iteration COUNT. The sandbox executor reports tokens/usd INCLUSIVE of a leaf's internal
1294
- * sub-agent fanout, so charging an arm by `total.tokens`/`total.usd` (not by how many
1295
- * `next()` cursors it logged) closes the leaf-fanout confound: a treatment leaf that fanned
1296
- * out internally pays for it in cost, where a per-iteration count would hide it. The
1297
- * within-run conserved pool already guarantees `Σk` equal by construction; this check is the
1298
- * CROSS-run analogue the pool cannot reach — proving equal compute before any win is claimed.
1299
- *
1300
- * Pure over the journal/blobs — no live agent calls; safe to run on a finished run's log.
1301
- */
1302
-
1303
- /**
1304
- * Reconstruct the whole spawn tree for `root` with per-node + rolled-up `Spend`. Reads the
1305
- * journal for structure + spend and, when `withOutputs`, the blob store for each `done`
1306
- * node's artifact. Fail loud on a tree that was never journaled, a settle/cancel for an
1307
- * un-spawned node (a corrupted log), or — under `withOutputs` — a `done` node whose blob the
1308
- * store cannot rehydrate (a silent gap would mis-cost or mis-evidence the tree).
1309
- */
1310
- declare function trajectoryReport(journal: SpawnJournal, blobs: ResultBlobStore, root: NodeId, options?: TrajectoryReportOptions): Promise<TrajectoryReport>;
1311
- /**
1312
- * Assert the arms are comparable at EQUAL conserved COST (tokens + usd), NOT raw iteration
1313
- * count. Compares each arm's root-rolled-up `total` on the two conserved channels: an arm is
1314
- * within-tolerance when the per-channel spread (max − min across arms) over the median is
1315
- * `≤ tolerance`. Pure over the reports — no I/O. Fails loud on an empty arm list (nothing to
1316
- * compare) so a vacuous "equal" is never returned.
1317
- */
1318
- declare function equalKOnCost(arms: ReadonlyArray<EqualKArm>, options?: EqualKOnCostOptions): EqualKVerdict;
1319
-
1320
- interface PromotionGateOptions {
1321
- /** The HOLDOUT report — must carry per-task cells for both strategy names. */
1322
- report: BenchmarkReport;
1323
- /** The incumbent champion's strategy name. */
1324
- incumbent: string;
1325
- /** The challenger's strategy name. */
1326
- candidate: string;
1327
- /** 'superiority' (default): the candidate must score significantly BETTER.
1328
- * 'non-inferiority': the candidate must prove its score is not worse than the
1329
- * incumbent by more than `scoreTolerance` AND its cost savings are significant —
1330
- * the gate for "same quality, cheaper" claims. */
1331
- mode?: 'superiority' | 'non-inferiority';
1332
- /** non-inferiority: the score CI lower bound must clear −scoreTolerance. Default 0.05. */
1333
- scoreTolerance?: number;
1334
- /** The CI lower bound on the paired lift must EXCEED this (score scale). Default 0. */
1335
- deltaThreshold?: number;
1336
- /** Minimum paired tasks before significance can be claimed. Default 6 — below that
1337
- * the bootstrap CI is too wide to separate a real lift from the per-task noise. */
1338
- minPairedTasks?: number;
1339
- /** Bootstrap statistic over the paired deltas. Default 'mean'. */
1340
- statistic?: 'mean' | 'median';
1341
- /** Fixed by the substrate by default — the same report always yields the same verdict. */
1342
- seed?: number;
1343
- resamples?: number;
1344
- }
1345
- interface PromotionVerdict {
1346
- promoted: boolean;
1347
- reason: 'identical-champion' | 'few-tasks' | 'no-margin' | 'significant' | 'non-inferior-and-cheaper' | 'non-inferiority-unproven' | 'not-cheaper';
1348
- mode: 'superiority' | 'non-inferiority';
1349
- /** Paired tasks that carried both strategies' cells. */
1350
- n: number;
1351
- /** Paired (candidate − incumbent) lift across the holdout tasks. */
1352
- lift: {
1353
- mean: number;
1354
- median: number;
1355
- low: number;
1356
- high: number;
1357
- };
1358
- /** non-inferiority mode: paired (incumbent − candidate) cost SAVINGS per task (usd) —
1359
- * positive means the candidate is cheaper; significant iff the CI low clears zero. */
1360
- costSavings?: {
1361
- mean: number;
1362
- median: number;
1363
- low: number;
1364
- high: number;
1365
- };
1366
- /** Paired (candidate − incumbent) wall-clock per task (ms) — negative = the candidate
1367
- * is FASTER. Informational in every mode (never gates); the latency answer to "what
1368
- * does this win actually cost the user?". */
1369
- latency?: {
1370
- mean: number;
1371
- median: number;
1372
- low: number;
1373
- high: number;
1374
- };
1375
- }
1376
- declare function promotionGate(opts: PromotionGateOptions): PromotionVerdict;
1377
-
1378
- /**
1379
- * Bridge a finished `runLoop` into an agent-eval campaign / profile-matrix
1380
- * dispatch.
1381
- *
1382
- * `runProfileMatrix` (and `runCampaign`) run the backend-integrity guard over
1383
- * the token usage a dispatch reports through `ctx.cost`. A dispatch that wraps
1384
- * `runLoop` must forward the loop's cost AND token usage, or the guard reads
1385
- * the run as a stub and throws. `reportLoopUsage` is that one line:
1386
- *
1387
- * const dispatch: ProfileDispatchFn<S, A> = async (profile, scenario, ctx) => {
1388
- * const result = await runLoop({ ...optsFor(profile, scenario), ctx: loopCtx })
1389
- * reportLoopUsage(ctx, result)
1390
- * return result.winner?.output as A
1391
- * }
1392
- *
1393
- * Typed structurally against the campaign `DispatchContext.cost` so this module
1394
- * stays free of an agent-eval import — it works with any cost meter exposing
1395
- * `observe` + `observeTokens`.
1396
- */
1397
-
1398
- /** The slice of an agent-eval campaign `DispatchContext.cost` this needs. */
1399
- interface UsageSink {
1400
- observe(amountUsd: number, source: string): void;
1401
- observeTokens(usage: LoopTokenUsage): void;
1402
- }
1403
- /**
1404
- * Forward a `LoopResult`'s aggregated cost + token usage into a campaign cost
1405
- * meter so the backend-integrity guard sees real LLM activity. `source`
1406
- * defaults to `'loop'`.
1407
- */
1408
- declare function reportLoopUsage<Task, Output, Decision>(cost: UsageSink, result: Pick<LoopResult<Task, Output, Decision>, 'costUsd' | 'tokenUsage'>, source?: string): void;
1409
-
1410
- /**
1411
- * @experimental
1412
- *
1413
- * `acquireSandbox` — cold-start-resilient sandbox acquisition. Eliminates the
1414
- * "create timed out at the proxy" failure mode conceptually by DECOUPLING "the
1415
- * create HTTP call returned" from "the sandbox is ready":
1416
- *
1417
- * - Create is initiated with a known `name`.
1418
- * - Readiness is observed from the sandbox's own `status` (`refresh()` polls
1419
- * true state), NOT from whether the create call returned in time.
1420
- * - If the create call itself times out at a gateway (502/503/504/522/524 or
1421
- * a transport timeout), provisioning is still running server-side — so we
1422
- * find the named sandbox via `list()` and wait for it to reach `running`.
1423
- *
1424
- * Result: a scale-from-zero cold start (node boot + host-agent registration,
1425
- * minutes) can no longer surface as a create failure behind a ~100s proxy
1426
- * limit. The loop becomes indifferent to whether the host pool is warm or cold.
1427
- *
1428
- * Invariant: an instance reporting no `status` (the minimal test fakes) is
1429
- * treated as ready; only an explicit `pending`/`provisioning` status triggers
1430
- * waiting, and only a retryable THROW triggers the find-by-name path. Real
1431
- * errors (auth, validation, budget) fail loud. A box that is created (or found)
1432
- * but never reaches `running` (abort, terminal status, budget) is torn down
1433
- * before the failure propagates, so an abort storm during cold start does not
1434
- * leak live sandboxes.
1435
- */
1436
-
1437
- /** @experimental */
1438
- interface AcquireOptions {
1439
- /**
1440
- * Total budget for the sandbox to reach `running`, covering on-demand node
1441
- * cold-start. Default 600_000ms — matches the orchestrator's pending-host
1442
- * registration window so we never give up before the platform itself would.
1443
- */
1444
- readyTimeoutMs?: number;
1445
- /** Poll interval while waiting for `running` / for the named sandbox to appear. */
1446
- pollIntervalMs?: number;
1447
- /** Cancellation (user abort). Distinct from create-call timeouts. */
1448
- signal?: AbortSignal;
1449
- /** Stamp a name so a timed-out create is recoverable by lookup. Auto-generated if absent. */
1450
- name?: string;
1451
- /** Clock override for deterministic tests. */
1452
- now?: () => number;
1453
- /** Sleep override for deterministic tests. */
1454
- sleep?: (ms: number) => Promise<void>;
1455
- }
1456
- /** @experimental */
1457
- declare function acquireSandbox(client: SandboxClient, options: CreateSandboxOptions, acquire?: AcquireOptions): Promise<SandboxInstance>;
1458
-
1459
- /**
1460
- * @experimental
1461
- *
1462
- * Capability probe for the loop kernel's backend-blind lineage seams. The
1463
- * kernel must NEVER ask "is this Docker or Firecracker?"; it asks "can this
1464
- * platform fork a checkpoint?" via `client.criuStatus()` and degrades to fresh
1465
- * boxes when the answer is no. CRIU availability is a per-platform fact, so the
1466
- * probe is memoized per client — one network round-trip, reused across every
1467
- * fanout in the run.
1468
- *
1469
- * Invariant: a client with no `criuStatus` method (the loop's test fakes, the
1470
- * raw SDK before it grew the probe) reports `canFork = false`. The seam is
1471
- * fail-CLOSED — never assume forking works, only enable it on a positive probe.
1472
- */
1473
-
1474
- /**
1475
- * What the loop kernel is allowed to know about a sandbox backend: a single
1476
- * capability bit, never the backend's identity. `canFork` gates the
1477
- * checkpoint+fork fanout path; everything else (session continuation) is a
1478
- * universal SDK feature that needs no probe.
1479
- *
1480
- * @experimental
1481
- */
1482
- interface SandboxCapabilities {
1483
- /**
1484
- * True only when `client.criuStatus()` returned `{ available: true }`. When
1485
- * false, a fork-enabled fanout degrades to independent fresh boxes — same
1486
- * result, no shared context prefix.
1487
- */
1488
- canFork: boolean;
1489
- }
1490
- /**
1491
- * Probe (and memoize per client) what the loop may rely on. A client without a
1492
- * `criuStatus` method, or whose probe rejects, yields `canFork = false` — a
1493
- * failed probe must never claim a capability the platform may not have. The
1494
- * promise is cached so concurrent fanout branches share one round-trip.
1495
- *
1496
- * @experimental
1497
- */
1498
- declare function probeSandboxCapabilities(client: SandboxClient): Promise<SandboxCapabilities>;
1499
- /**
1500
- * Narrowed view of the optional CRIU probe. The loop-side `SandboxClient`
1501
- * does not require `criuStatus`; this widens it optionally so the probe can be
1502
- * read without importing sandbox-backend specifics. @experimental
1503
- */
1504
- interface CriuCapableClient {
1505
- criuStatus?: () => Promise<{
1506
- available: boolean;
1507
- criuVersion?: string;
1508
- reason?: string;
1509
- }>;
1510
- }
1511
-
1512
- /**
1513
- * Sandbox-event → runtime-event mapping.
1514
- *
1515
- * The sandbox SDK emits a polymorphic `SandboxEvent = { type, data, id? }`
1516
- * whose `type` vocabulary is backend-determined (opencode, etc.) rather than
1517
- * enumerated by the SDK. Two consumers project it:
1518
- * - the loop kernel's cost ledger (`extractLlmCallEvent`) — sums usage off
1519
- * every cost-bearing event, regardless of stream shape;
1520
- * - the `AgentRuntime.act` streaming contract (`mapSandboxEvent`) — projects
1521
- * incremental events to the `RuntimeStreamEvent` chat-UX vocabulary.
1522
- *
1523
- * Both live here so the empirically-observed `type` vocabulary has one home.
1524
- */
1525
-
1526
- /**
1527
- * Extract a `RuntimeStreamEvent`-shaped `llm_call` from a sandbox event when
1528
- * the event carries usage/cost data. Returns `undefined` for non-cost events
1529
- * so the kernel can iterate the full stream without branching.
1530
- *
1531
- * Canonical cost-carrying types observed in the wild:
1532
- * - `llm_call` — `data: { model, tokensIn, tokensOut, costUsd, ... }`
1533
- * - `message.completed` / `result` — `data: { usage: { inputTokens,
1534
- * outputTokens, totalCostUsd? } }`
1535
- * - `cost.usage` / `usage` — same shape under a dedicated type
1536
- *
1537
- * Numeric coercion is strict: `Number.isFinite` gates every accumulator write
1538
- * so a sentinel `NaN` from a misbehaving backend cannot poison the ledger.
1539
- */
1540
- declare function extractLlmCallEvent(event: SandboxEvent, agentRunName: string): (RuntimeStreamEvent & {
1541
- type: 'llm_call';
1542
- }) | undefined;
1543
- /**
1544
- * Project one `SandboxEvent` onto the `RuntimeStreamEvent` chat-UX vocabulary,
1545
- * for runtimes that bridge a sandbox `streamPrompt` into the
1546
- * `AgentRuntime.act` streaming contract. Returns `undefined` for events that
1547
- * have no faithful projection — the raw stream is preserved separately for the
1548
- * `OutputAdapter`, so an unmapped event never loses data.
1549
- *
1550
- * Mapped (the task-optional incremental variants — no synthesized task
1551
- * lifecycle, no guessed tool-part shapes):
1552
- * - `message.part.updated` text part → `text_delta`
1553
- * - `message.part.updated` reasoning/thinking part → `reasoning_delta`
1554
- * - cost-bearing events → `llm_call` (shared with the ledger extractor)
1555
- *
1556
- * The opencode backend emits incremental text as
1557
- * `{ type: 'message.part.updated', data: { part: { type, text }, delta } }`;
1558
- * `delta` is the increment, `part.text` the running accumulation.
1559
- */
1560
- declare function mapSandboxEvent(event: SandboxEvent, opts?: {
1561
- agentRunName?: string;
1562
- }): RuntimeStreamEvent | undefined;
1563
-
1564
- /**
1565
- * @experimental
1566
- *
1567
- * `SandboxLineage` — the backend-blind owner of box + session handles for a
1568
- * single `runLoop` invocation. It exists so `run-loop.ts` never references a
1569
- * backend (Docker / Firecracker): the lineage turns "continue this session" and
1570
- * "fork this branch" into capability-gated sandbox-SDK calls and degrades to
1571
- * fresh boxes when a capability is absent.
1572
- *
1573
- * Three operations, mirroring the kernel's per-iteration choices:
1574
- * - `start(spec, prompt)` → a fresh box; the FIRST `streamPrompt` carries a
1575
- * minted `sessionId` so later `continue` calls reuse the same server-side
1576
- * conversation instead of re-injecting prior context as prompt text.
1577
- * - `continue(handle, prompt)` → the SAME box, `streamPrompt({ sessionId })`.
1578
- * The context lives in the sandbox; the prompt is only the new turn. Before
1579
- * streaming it ASSERTS the session is still live server-side (via
1580
- * `box.session(id).status()`): if the platform never honored the
1581
- * client-minted id (or reaped it), `status()` is `null` and `continue`
1582
- * fails loud rather than silently re-running the turn without prior context.
1583
- * - `fork(handle, n, ...)` → when `canFork`, `checkpoint({ leaveRunning })` on
1584
- * the parent then `fork(checkpointId)` × n so N branches inherit a shared
1585
- * context prefix; otherwise N independent fresh boxes (same result, no
1586
- * prefix). Either way each branch streams its own turn. Child-box creation
1587
- * is bounded by the lineage's `maxConcurrency` — a 20-way fanout under a
1588
- * concurrency cap of 2 provisions boxes in bounded waves, not all at once.
1589
- *
1590
- * Invariant: the lineage OWNS every box it starts or forks and tears them all
1591
- * down on `teardown()` (or earlier via `prune`). It never tears down a box
1592
- * mid-flight — the kernel decides when a handle is done. Streaming itself stays
1593
- * in `run-loop.ts`; the lineage only hands back the live `streamPrompt` iterable
1594
- * so the kernel keeps ownership of event collection, cost accounting, and trace
1595
- * emission.
1596
- */
1597
-
1598
- /**
1599
- * A live box plus the session that threads its iterations together. Handed back
1600
- * by `start`/`fork`, passed into `continue`/`fork` to descend from. Opaque to
1601
- * the kernel beyond `box` (for placement/teardown) and `sessionId` (trace).
1602
- *
1603
- * @experimental
1604
- */
1605
- interface SandboxLineageHandle {
1606
- /** The owned, running sandbox this handle drives. */
1607
- box: SandboxInstance;
1608
- /**
1609
- * Stable session id threaded through this box's `streamPrompt` calls. Minted
1610
- * by the lineage on `start`; reused on `continue` so the server continues the
1611
- * same conversation. A forked handle starts a fresh session on its new box —
1612
- * the shared context comes from the checkpoint, not a shared session id.
1613
- */
1614
- sessionId: string;
1615
- }
1616
- /**
1617
- * Owns box + session handles for one loop run and offers the three
1618
- * capability-gated lifecycle moves. Construct via `createSandboxLineage`.
1619
- *
1620
- * @experimental
1621
- */
1622
- interface SandboxLineage {
1623
- /**
1624
- * Acquire a fresh box and begin a new session on it. Returns the handle and
1625
- * the live `streamPrompt` iterable for the first turn (caller drains it).
1626
- */
1627
- start(spec: AgentRunSpec<unknown>, prompt: string, signal: AbortSignal): Promise<{
1628
- handle: SandboxLineageHandle;
1629
- events: AsyncIterable<SandboxEvent>;
1630
- }>;
1631
- /**
1632
- * Continue an existing handle's session with one more turn on the SAME box.
1633
- * The prior context is server-side; `prompt` is only the new turn. Asserts the
1634
- * session is still known to the sandbox first (fail-loud) so a platform that
1635
- * silently dropped the client-minted session id surfaces as an error instead
1636
- * of a contextless turn the caller mistakes for a real continuation.
1637
- */
1638
- continue(handle: SandboxLineageHandle, prompt: string, signal: AbortSignal): Promise<AsyncIterable<SandboxEvent>>;
1639
- /**
1640
- * Branch `count` children from `parent`. When the platform can fork, each
1641
- * child inherits `parent`'s checkpoint — and therefore the parent's IMAGE and
1642
- * PROFILE: under a real fork `specs[i]` does NOT re-select a per-branch
1643
- * profile (the SDK forks the running box, it can't swap the image). `specs[i]`
1644
- * picks the per-branch profile ONLY on the degraded fresh-box path (no CRIU).
1645
- * A heterogeneous-profile fanout therefore homogenizes to the parent's profile
1646
- * when fork is available — pass a single shared spec for forked fanouts, or
1647
- * use `random@k` (no fork) when branches must differ. Each child's first turn
1648
- * streams `prompts[i]`. Child-box creation is bounded by `maxConcurrency`.
1649
- */
1650
- fork(parent: SandboxLineageHandle, prompts: string[], specs: AgentRunSpec<unknown>[], signal: AbortSignal): Promise<{
1651
- handle: SandboxLineageHandle;
1652
- events: AsyncIterable<SandboxEvent>;
1653
- }[]>;
1654
- /**
1655
- * Destroy every owned box whose handle is NOT in `keep`, freeing it before
1656
- * loop end. The kernel calls this after a round when it can prove no future
1657
- * round will descend from the pruned boxes (deterministic, monotonic branch
1658
- * selection); boxes still reachable as a future branch source are retained.
1659
- * Best-effort, bounded, parallel — a failed delete never throws.
1660
- */
1661
- prune(keep: Iterable<SandboxLineageHandle>): Promise<void>;
1662
- /** Destroy every box this lineage owns. Best-effort, bounded, parallel. */
1663
- teardown(): Promise<void>;
1664
- }
1665
- /**
1666
- * Build a lineage bound to one client + its probed capabilities. The
1667
- * capabilities are passed in (not re-probed) so the kernel probes once per run
1668
- * and the lineage stays a pure function of "what this platform can do".
1669
- *
1670
- * @experimental
1671
- */
1672
- declare function createSandboxLineage(client: SandboxClient, capabilities: SandboxCapabilities, options?: {
1673
- maxConcurrency?: number;
1674
- streaming?: 'sse' | 'poll';
1675
- }): SandboxLineage;
1676
- /**
1677
- * Loop-side widening of the box's optional checkpoint method. The
1678
- * `SandboxClient`/`SandboxInstance` surface the kernel relies on does not
1679
- * require checkpointing; this reads it optionally so the lineage can probe-gate
1680
- * without importing sandbox-backend specifics. @experimental
1681
- */
1682
- interface CheckpointCapableBox {
1683
- checkpoint?: (options?: {
1684
- leaveRunning?: boolean;
1685
- tags?: string[];
1686
- }) => Promise<{
1687
- checkpointId: string;
1688
- }>;
1689
- }
1690
- /** Loop-side widening of the box's optional fork method. @experimental */
1691
- interface ForkCapableBox {
1692
- fork?: (checkpointId: string, options?: {
1693
- name?: string;
1694
- }) => Promise<SandboxInstance>;
1695
- }
1696
- /**
1697
- * Loop-side widening of the box's optional session accessor. The real
1698
- * `SandboxInstance` exposes `session(id).status()`; the loop reads it optionally
1699
- * so `continue` can assert session liveness without requiring it of the test
1700
- * fakes. `status()` resolves `null` when the id is unknown to the sandbox.
1701
- * @experimental
1702
- */
1703
- interface SessionCapableBox {
1704
- session?: (id: string) => {
1705
- status: () => Promise<unknown | null>;
1706
- };
1707
- }
1708
-
1709
- /**
1710
- * `openSandboxRun` — the ONE harness-agnostic seam for running an agent in a
1711
- * sandbox over a persistent artifact: run it, stream it, RESUME the same session
1712
- * across turns. Domain-agnostic: a coding agent, a research agent, a tax/legal
1713
- * agent — all flow through this; the domain lives only in the `Deliverable<Out>`
1714
- * the caller supplies, never in a per-domain copy of this function.
1715
- *
1716
- * It is a thin facade (NOT a new layer) over code that already exists and is
1717
- * already hardened:
1718
- * - `acquireSandbox` — cold-start / 502-503-504 / gateway-timeout recovery,
1719
- * - `buildBackendOptions` — the harness IS `backend.type` (opencode / codex /
1720
- * claude-code / kimi-code / hermes / pi); the only "which agent" knob,
1721
- * - `createSandboxLineage` — `start` mints a session; `resume` continues the
1722
- * SAME server-side session with a fail-loud `assertSessionLive`.
1723
- *
1724
- * The one genuinely-new piece is {@link Deliverable}: it widens the pure
1725
- * `OutputAdapter.parse(events)` to ALSO admit a post-turn read off the box FS —
1726
- * the structural gap that made the bench gates hand-roll `box.fs.read`, because a
1727
- * large produced file (a git diff, a generated document) truncates in the chat
1728
- * stream and a pure events-parser cannot reach the workspace. Per the SDK, a
1729
- * RELATIVE `deliverable.path` resolves from the workspace root and an ABSOLUTE one
1730
- * (e.g. `/tmp/solution.patch`) reads the container filesystem directly — both are
1731
- * valid; pick the one the agent actually wrote to. Avoid `..` traversal segments.
1732
- *
1733
- * What this deliberately does NOT do (so it stays a facade, not slop): no custom
1734
- * reconnect/replay (the SDK + platform own per-session buffering + `Last-Event-ID`);
1735
- * no fork verb (platform CRIU is probe-gated and currently absent — fork lives in
1736
- * `SandboxLineage.fork` behind the capability probe, surfaced only if it returns).
1737
- * It is also distinct from `runLoop`: `runLoop` is the multi-round, driver-driven
1738
- * kernel (fresh box per round, events deliverable); this is a SINGLE rollout +
1739
- * artifact-or-events deliverable + resume over ONE persistent box.
1740
- */
1741
-
1742
- /**
1743
- * @experimental
1744
- * How a typed deliverable `Out` is materialized from a finished turn.
1745
- * - `events` — pure parse over the event array (identical to `OutputAdapter`).
1746
- * - `artifact` — read a file off the box AFTER the turn drains, then map it (+ the
1747
- * events). For diffs/codebases/documents that don't fit the chat
1748
- * stream. `path` relative ⇒ workspace root; absolute ⇒ container FS.
1749
- */
1750
- type Deliverable<Out> = {
1751
- kind: 'events';
1752
- fromEvents: (events: SandboxEvent[]) => Out;
1753
- } | {
1754
- kind: 'artifact';
1755
- path: string;
1756
- fromArtifact: (raw: string, events: SandboxEvent[]) => Out;
1757
- };
1758
- /**
1759
- * @experimental
1760
- * One finished turn over the artifact. A failed FS read is surfaced in `readError`
1761
- * (never masked as an empty deliverable) so a caller distinguishes "agent produced
1762
- * nothing" from a transport/FS fault.
1763
- */
1764
- interface TurnResult<Out> {
1765
- out: Out;
1766
- events: SandboxEvent[];
1767
- readError?: string;
1768
- }
1769
- /** @experimental A live run over ONE persistent artifact (box + session). Close it
1770
- * when done — `close()` tears the box down. */
1771
- interface SandboxRun<Out> {
1772
- readonly box: SandboxInstance;
1773
- readonly sessionId: string;
1774
- /** First turn over the fresh box (mints the session). Throws if already started. */
1775
- start(prompt: string): Promise<TurnResult<Out>>;
1776
- /** Continue THE SAME session over THE SAME artifact — a resumed turn/rollout. */
1777
- resume(prompt: string): Promise<TurnResult<Out>>;
1778
- close(): Promise<void>;
1779
- }
1780
- /** @experimental */
1781
- interface OpenSandboxRunOptions {
1782
- /** Profile + sandbox env/overrides. `sandboxOverrides.backend.type` is the harness. */
1783
- agentRun: AgentRunSpec<string>;
1784
- signal: AbortSignal;
1785
- /** Optional execution-scoped observers. Hook failures never fail the run. */
1786
- hooks?: RuntimeHooks;
1787
- /** Stable run id for trace joins. Defaults to a short runtime-minted id. */
1788
- runId?: string;
1789
- /** Optional benchmark/scenario id carried into emitted hook events. */
1790
- scenarioId?: string;
1791
- /** Test seam for deterministic hook timestamps. Defaults to `Date.now`. */
1792
- now?: () => number;
1793
- /** Bounds box-creation bursts inside lineage fanout. Default from lineage. */
1794
- maxConcurrency?: number;
1795
- /** Base backoff (ms) for retrying a transient artifact `fs.read` failure; the i-th
1796
- * retry waits `readRetryDelayMs * i`. Default 1000. Set 0 to disable the wait (tests). */
1797
- readRetryDelayMs?: number;
1798
- }
1799
- /**
1800
- * @experimental
1801
- * Open a sandbox run. Harness-agnostic: the harness lives in
1802
- * `options.agentRun.sandboxOverrides.backend.type`, so opencode/codex/claude-code/
1803
- * kimi-code all flow through this one entrypoint with identical env/auth wiring.
1804
- */
1805
- declare function openSandboxRun<Out>(client: SandboxClient, options: OpenSandboxRunOptions, deliverable: Deliverable<Out>): Promise<SandboxRun<Out>>;
1806
-
1807
- /**
1808
- * authorStrategy — the agent-authored layer as a package primitive (software-3.0): an
1809
- * LLM reads a benchmark's per-task LOSSES + the defineStrategy contract and writes a NEW
1810
- * optimization strategy as code; the caller gates it like any human-built candidate
1811
- * (runBenchmark + a frozen holdout).
1812
- *
1813
- * Structurally safe by construction: the authored body composes shot()/critique() and
1814
- * spends through the Supervisor's conserved pool — it can be wrong, but it cannot
1815
- * Goodhart the check (it never sees the verifiers) and it cannot win by overspending.
1816
- *
1817
- * The authored module is written to `outDir` and dynamically imported — run under a
1818
- * TS-capable loader (tsx) since models often emit type annotations.
1819
- */
1820
-
1821
- /** The compressed consumable a skill carries: everything an author needs to emit a loop. */
1822
- declare const strategyAuthorContract = "\nYou author an OPTIMIZATION STRATEGY for an agentic loop system. A strategy decides how to\nspend a compute budget to beat a task's deployable check. You compose exactly two steps:\n\n shot(spec?: { handle?, messages?, steer?, persona?, tools? }): Promise<ShotResult | null>\n Runs ONE worker attempt (a bounded tool loop) over an artifact.\n - omit handle => the shot opens its OWN fresh artifact and closes it after (a sample).\n - pass handle => the shot CONTINUES that artifact (state accumulates across shots).\n - messages => the carried conversation (pass the previous ShotResult.messages to continue).\n - steer => a corrective instruction injected before the shot.\n - persona => { systemPrompt?, model? } \u2014 give THIS shot its own role and/or model\n (multi-agent strategies: a researcher shot then an engineer shot, a panel of k\n personas over one budget). On a fresh shot the systemPrompt replaces the task's; on\n a carried conversation it arrives as a hand-off message. Same conserved budget.\n - tools => string[] \u2014 restrict THIS shot to a subset of the task's tools by\n name (focus an explore shot on read-only tools, an execute shot on write tools).\n Restriction-only; unknown names make the shot fail. ALWAYS select from\n await listTools(handle) \u2014 never hardcode. Omitted => the shot sees every tool.\n ShotResult = { messages, score (0..1 on the task's check), passes, total, completions, toolErrors }\n Returns null if the attempt failed infra-wise.\n\n critique(messages): Promise<string | null>\n A firewalled trace-analyst reads the attempt's trajectory and returns ONE corrective\n instruction (or null when it judges the work complete). Costs ~1 completion.\n\n consult(messages, instruction): Promise<string | null>\n The RAW analyst channel: the same firewalled critic answers YOUR instruction over the\n trajectory verbatim (no reformatting) \u2014 use it when you need a specific reply format\n (a decision, a prediction). Costs ~1 completion.\n\n surface.open(task) / surface.close(handle)\n Open a persistent artifact you manage yourself (remember to close in a finally).\n close is idempotent \u2014 closing an already-closed handle is a safe no-op.\n\n listTools(handle): Promise<Array<{ name, description? }>>\n The tools THIS task actually offers. TOOL SETS VARY PER TASK \u2014 if you restrict a\n shot with `tools`, you MUST pick names from await listTools(handle); hardcoding\n names from an example kills your shots on every task whose tools differ.\n\nRules:\n- ALWAYS await every shot/critique/surface call \u2014 a floating promise that rejects\n crashes the whole benchmark run.\n- Stay within ~budget total shots; every shot/critique spends from a conserved pool.\n- For a FRESH attempt OMIT `messages` entirely (never pass `[]` \u2014 an empty array is a\n fresh conversation too, but be explicit). To CONTINUE, pass the previous\n ShotResult.messages unchanged.\n- Return { score, resolved, completions, progression, shots } \u2014 score = the BEST checkpoint\n you reached (keep-best, never final-state), progression = score after each shot.\n- The module must be EXACTLY this shape (no other imports, no commentary outside code):\n\nimport { defineStrategy } from '@tangle-network/agent-runtime/loops'\nexport default defineStrategy('your-strategy-name', async ({ surface, task, budget, shot, critique, listTools }) => {\n // your composition (listTools comes from the destructured context \u2014 it is NOT a global)\n})\n";
1823
- interface AuthorStrategyOptions {
1824
- /** The model-call seam (agent-eval `createChatClient`). */
1825
- chat: ChatClient;
1826
- model?: string;
1827
- /** A NAMED fallback author tried once when the primary call fails or returns no code
1828
- * block (thinking models time out at the edge on long authoring prompts, or return
1829
- * empty content without `maxTokens`). Opt-in — absent means the primary's failure
1830
- * propagates. */
1831
- fallbackModel?: string;
1832
- /** The contract text shown to the author. Default `strategyAuthorContract`. The
1833
- * meta-optimization coordinate: a GEPA/skill loop can evolve this text and gate each
1834
- * variant on the same frozen holdout as any strategy. */
1835
- contract?: string;
1836
- /** The environment the losses came from (orientation only — never the verifiers). */
1837
- environmentName: string;
1838
- /** The per-task losses table (e.g. JSON.stringify(report.perTask)) — the gradient. */
1839
- lossesJson: string;
1840
- /** The budget the strategy must respect (shots/width). */
1841
- budget: number;
1842
- /** Where the authored module file is written (created if missing). */
1843
- outDir: string;
1844
- temperature?: number;
1845
- /** Completion cap — required by thinking-model authors that stream reasoning first. */
1846
- maxTokens?: number;
1847
- signal?: AbortSignal;
1848
- }
1849
- /** Static CONTRACT lint over an authored strategy module — the module-boundary
1850
- * enforcement of the harness's two measurement invariants:
1851
- * - author blindness: the only import allowed is the loops surface. A body that could
1852
- * reach the filesystem, network, or process could read or mutate verifier/artifact
1853
- * state outside the brokered shots, and the harness-verified score would stop
1854
- * meaning "what the shots achieved".
1855
- * - conserved dose: no out-of-band compute (fetch/require/eval) — every unit a
1856
- * strategy spends is metered by the Supervisor's pool, which is what makes
1857
- * equal-budget comparisons between strategies valid.
1858
- * A lint, not a sandbox: its job is keeping the benchmark numbers interpretable. */
1859
- declare function assertStrategyContract(code: string): void;
1860
- interface AuthoredStrategy {
1861
- strategy: Strategy;
1862
- file: string;
1863
- code: string;
1864
- }
1865
- /** Author + load a strategy from losses. Throws when the author emits no loadable module;
1866
- * with `fallbackModel` set, the named fallback gets one attempt first. */
1867
- declare function authorStrategy(opts: AuthorStrategyOptions): Promise<AuthoredStrategy>;
1868
-
1869
- /**
1870
- * runStrategyEvolution — the multi-generation strategy search: per generation the system
1871
- * authors a POPULATION of candidate strategies from the current tournament's losses,
1872
- * plays them against the incumbent at equal budget, and advances a champion; one final
1873
- * promotion decision runs on a NEVER-BEFORE-USED holdout slice through `promotionGate`.
1874
- *
1875
- * Measurement invariants (the reasons this design is shaped the way it is):
1876
- * - The author sees TRAIN losses only. The holdout slice is drawn fresh (disjoint task
1877
- * offsets) after all authoring is done — one promotion decision, one untouched slice,
1878
- * so adaptive reuse of evaluation data never enters the verdict.
1879
- * - Every tournament runs at the same per-strategy budget through the conserved pool;
1880
- * candidates cannot win by overspending.
1881
- * - Champion selection within the search is a SEARCH policy (configurable, default
1882
- * cost-aware: ties on score go to the cheapest strategy — a scalar hides a strategy
1883
- * that ties at half the cost). The promotion verdict never comes from search
1884
- * selection; it comes from the gate on the fresh slice.
1885
- * - Every authored artifact's description length (gzip bits) is recorded, so the
1886
- * artifact-complexity-vs-holdout-gap relation is analyzable from any run's report.
1887
- *
1888
- * Lineage fields (`parent`, `generation`) are recorded on every archive node so a
1889
- * descendant-productivity parent-selection policy can be added without changing the
1890
- * report schema; the v1 search authors from the latest tournament's losses.
1891
- */
1892
-
1893
- interface EvolutionAuthor {
1894
- /** The model-call seam (agent-eval `createChatClient`). */
1895
- chat: ChatClient;
1896
- model?: string;
1897
- fallbackModel?: string;
1898
- temperature?: number;
1899
- maxTokens?: number;
1900
- }
1901
- type ChampionPolicy = 'score' | 'costAware';
1902
- interface StrategyEvolutionConfig {
1903
- environment: Environment;
1904
- /** Task supply by DISJOINT slice: `(offset, n)` must return n tasks unique to that
1905
- * offset range. Train draws [0, trainN); the holdout draws [trainN + holdoutOffset,
1906
- * …) — tasks the search never touched. */
1907
- tasks: (offset: number, n: number) => Promise<AgenticTask[]>;
1908
- trainN: number;
1909
- holdoutN: number;
1910
- /** Extra offset past the train slice for the holdout draw (rotate across runs). */
1911
- holdoutOffset?: number;
1912
- worker: AgenticOptions;
1913
- author: EvolutionAuthor;
1914
- /** Rollouts (sample) / shots (refine) per strategy per task. Default 3. */
1915
- budget?: number;
1916
- concurrency?: number;
1917
- /** Author→tournament rounds after gen0. Default 2. */
1918
- generations?: number;
1919
- /** Authored candidates per generation. Default 2. */
1920
- populationSize?: number;
1921
- /** The gen0 field. Default [sample, refine, sampleThenRefine]. */
1922
- baselines?: Strategy[];
1923
- /** What "better" means for PROMOTION. 'score' (default): the candidate must beat the
1924
- * incumbent's score (superiority gate). 'cost': the candidate must prove score
1925
- * NON-INFERIORITY (not worse by more than `scoreTolerance`) plus significant cost
1926
- * savings — the "same quality, cheaper" objective. The author is told the objective
1927
- * and sees per-task spend either way. */
1928
- objective?: 'score' | 'cost';
1929
- /** Cost objective: the score CI lower bound must clear −scoreTolerance. Default 0.05. */
1930
- scoreTolerance?: number;
1931
- /** Search-side champion selection. Default 'costAware'. */
1932
- champion?: ChampionPolicy;
1933
- /** Score band treated as a tie under 'costAware'. Default 0.01. */
1934
- championEpsilon?: number;
1935
- /** Where authored modules are written. */
1936
- outDir: string;
1937
- /** Promotion-gate evidence floor (paired holdout tasks). */
1938
- minPairedTasks?: number;
1939
- /** BAND-AWARE scoring — concentrate the measurement where lift is possible.
1940
- * Holdout: draw `holdoutPoolN` candidate tasks and run `baselines[0]` once at the run
1941
- * budget as an INDEPENDENT reference screen; keep tasks scoring ≤ `maxRefScore`
1942
- * (headroom exists) and take the first `holdoutN`. Band membership is decided before
1943
- * either finalist touches a task and both finalists then face the SAME tasks — the
1944
- * estimand becomes "paired lift on headroom tasks", pre-registered by this config.
1945
- * Train: champion selection ignores zero-spread tasks (every field strategy scored
1946
- * identically — zero selection information, pure noise dilution). */
1947
- band?: {
1948
- holdoutPoolN: number;
1949
- /** Keep holdout tasks where the reference scores ≤ this. Default 0.99 — drop only
1950
- * tasks the reference already solves fully (no headroom, a candidate can only tie). */
1951
- maxRefScore?: number;
1952
- };
1953
- /** What the author learns from a tournament. 'exact' (default) = scores + progressions
1954
- * per task; 'binary' = pass/fail only — the leakage-bounded channel (one bit per cell
1955
- * per generation reaches the author from the evaluation data). */
1956
- lossesDetail?: 'exact' | 'binary';
1957
- /** Reproducer certification (arXiv:2606.11045): when the final champion is AUTHORED,
1958
- * compress it to a short natural-language summary, have a fresh author re-implement
1959
- * from the summary alone (no losses, no code), and score the reproduction on the same
1960
- * holdout. A reproduction gap is an overfitting signal (their detector: 100%
1961
- * sensitivity / 91% specificity in the ML-agent setting) — recorded on the report,
1962
- * never gate-blocking in v1. */
1963
- reproducerCheck?: {
1964
- /** Word budget for the strategy summary. Default 64. */
1965
- summaryMaxWords?: number;
1966
- /** Reproduction counts as faithful when reproducedScore ≥ championScore − tolerance.
1967
- * Default 0.05. */
1968
- tolerance?: number;
1969
- };
1970
- /** Endurance: write the run state after every completed phase; with `resume`, a
1971
- * restart skips completed phases (authored modules re-imported from their files).
1972
- * Worst case after a mid-run death is re-paying ONE phase, never the run. */
1973
- checkpoint?: {
1974
- path: string;
1975
- resume?: boolean;
1976
- };
1977
- /** Called before each benchmark phase (gen0, gen1…, band-screen, holdout, reproduce).
1978
- * The seam for environment recycling — no artifacts span phases, so a runner may
1979
- * recreate a wedge-prone environment container here. */
1980
- onPhase?: (phase: string) => Promise<void>;
1981
- onTask?: (phase: string, row: BenchmarkTaskRow, done: number, total: number) => void;
1982
- hooks?: RuntimeHooks;
1983
- }
1984
- interface ChampionPick {
1985
- name: string;
1986
- score: number;
1987
- usd: number;
1988
- }
1989
- interface EvolutionCandidate {
1990
- name: string;
1991
- file?: string;
1992
- gzipBits?: number;
1993
- codeChars?: number;
1994
- /** Present when this author attempt failed (recorded, never silent). */
1995
- error?: string;
1996
- }
1997
- interface EvolutionGeneration {
1998
- generation: number;
1999
- candidates: EvolutionCandidate[];
2000
- report: BenchmarkReport;
2001
- champion: ChampionPick;
2002
- }
2003
- interface EvolutionArchiveNode {
2004
- name: string;
2005
- source: 'baseline' | 'authored';
2006
- generation: number;
2007
- /** The champion whose tournament losses this candidate was authored from. */
2008
- parent?: string;
2009
- gzipBits?: number;
2010
- file?: string;
2011
- /** Latest measured tournament result — 0 until the node's first tournament settles
2012
- * (an authored node is created before its generation's benchmark runs). */
2013
- score: number;
2014
- usd: number;
2015
- }
2016
- interface ReproductionCheck {
2017
- /** The compressed strategy description the reproducer implemented from. */
2018
- summary: string;
2019
- reproducedName: string;
2020
- file?: string;
2021
- championHoldoutScore: number;
2022
- reproducedHoldoutScore: number;
2023
- /** champion − reproduced (positive = the reproduction fell short). */
2024
- gap: number;
2025
- /** reproducedScore ≥ championScore − tolerance. A failed reproduction is an
2026
- * overfitting signal: the champion's win did not fit through the summary. */
2027
- reproducible: boolean;
2028
- /** Infra failure during reproduction (distinct from a semantic reproduction failure). */
2029
- error?: string;
2030
- }
2031
- interface EvolutionBandInfo {
2032
- /** Tasks screened by the reference on the holdout pool. */
2033
- screened: number;
2034
- /** Tasks kept (reference score ≤ maxRefScore) before truncating to holdoutN. */
2035
- inBand: number;
2036
- /** Reference scores per screened task (the screening record). */
2037
- refScores: Array<{
2038
- taskId: string;
2039
- score: number;
2040
- }>;
2041
- }
2042
- interface EvolutionReport {
2043
- gen0: BenchmarkReport;
2044
- gen0Champion: ChampionPick;
2045
- generations: EvolutionGeneration[];
2046
- archive: EvolutionArchiveNode[];
2047
- finalChampion: ChampionPick;
2048
- holdout: BenchmarkReport;
2049
- verdict: PromotionVerdict;
2050
- /** Present when band screening ran — the verdict's estimand is then "paired lift on
2051
- * headroom tasks" (band membership fixed by the reference screen, pre-registered). */
2052
- band?: EvolutionBandInfo;
2053
- /** Present when reproducerCheck ran (final champion was authored). */
2054
- reproduction?: ReproductionCheck;
2055
- /** SEARCH TELEMETRY, not evidence: each entry is that generation's own train-slice
2056
- * re-measurement, so cross-generation deltas mix true drift with run-to-run variance
2057
- * (entries are unpaired across generations). The only evidence-grade comparison in
2058
- * this report is `verdict` — both finalists measured fresh, paired, on the holdout. */
2059
- trajectory: Array<{
2060
- generation: number;
2061
- champion: string;
2062
- score: number;
2063
- usd: number;
2064
- }>;
2065
- }
2066
- /** Strategy means recomputed over the DISCRIMINATING tasks only — tasks where the field
2067
- * strategies did not all score identically. Zero-spread tasks (everyone 1.0, everyone
2068
- * 0.0, everyone tied) carry no selection information; averaging over them dilutes real
2069
- * differences toward zero. Search-side denoising only — the gate never uses this. */
2070
- declare function discriminatingMeans(report: BenchmarkReport, fieldOrder: string[]): Record<string, {
2071
- score: number;
2072
- usd: number;
2073
- }> | null;
2074
- /** The champion pick over a means table. 'score' takes the best mean score (ties →
2075
- * field order). 'costAware' treats scores within `epsilon` of the best as tied and
2076
- * takes the cheapest — the (score, $) Pareto rule collapsed to one pick. */
2077
- declare function pickChampion(means: Record<string, {
2078
- score: number;
2079
- usd: number;
2080
- }>, fieldOrder: string[], policy: ChampionPolicy, epsilon: number): ChampionPick;
2081
- /** Search-side champion selection over a tournament report. */
2082
- declare function selectChampion(report: BenchmarkReport, fieldOrder: string[], policy: ChampionPolicy, epsilon: number): ChampionPick;
2083
- declare function runStrategyEvolution(cfg: StrategyEvolutionConfig): Promise<EvolutionReport>;
2084
-
2085
- /**
2086
- * @experimental
2087
- *
2088
- * The supervisor's intelligence is AUTHORING the agents it spawns — not pressing buttons.
2089
- *
2090
- * Every agent here is three things: instructions (system prompt), tools, and a model — its
2091
- * `AgentProfile`. The supervisor's job is to WRITE those profiles: read the task, decompose it,
2092
- * and for each sub-task author a tailored worker recipe. `supervisorSkill` is the how-to the
2093
- * supervisor reads (its system prompt); `authoredWorker` builds a worker AGENT from a profile the
2094
- * supervisor authored — the authored systemPrompt + model shape the worker's call.
2095
- *
2096
- * The skill is the single OPTIMIZABLE surface: edit it → the supervisor designs better agents.
2097
- * That is the self-improvement lever (the prompt/skill lever), not the execution plumbing.
2098
- */
2099
-
2100
- /** What the supervisor AUTHORS per sub-task — a worker recipe (a partial `AgentProfile`). */
2101
- interface AuthoredProfile {
2102
- name: string;
2103
- /** The rich, task-specific instructions the supervisor wrote for THIS worker. */
2104
- systemPrompt: string;
2105
- /** The model the supervisor chose for this sub-task (falls back to the run default). */
2106
- model?: string;
2107
- }
2108
- /** Narrow an untyped `spawn_worker` profile argument to an `AuthoredProfile`, or null if the
2109
- * supervisor failed to author one (empty/placeholder profile — a skill violation worth catching). */
2110
- declare function asAuthoredProfile(raw: unknown): AuthoredProfile | null;
2111
- /** The supervisor SKILL — the how-to the supervisor reads (its system prompt). THE optimizable
2112
- * surface: editing this changes how the supervisor designs every agent it spawns. */
2113
- declare function supervisorSkill(opts?: {
2114
- goal?: string;
2115
- }): string;
2116
- /** Build a worker AGENT from a profile the supervisor authored: the authored `systemPrompt` +
2117
- * `model` shape the worker's one model call; the deliverable gates settlement (valid ⟺ delivered). */
2118
- declare function authoredWorker(profile: AuthoredProfile, opts: {
2119
- cfg: RouterConfig;
2120
- taskPrompt: string;
2121
- deliverable: DeliverableSpec;
2122
- temperature?: number;
2123
- }): Agent<unknown, unknown>;
2124
-
2125
- /**
2126
- * @experimental
2127
- *
2128
- * The conserved budget reservation pool — the invariant the whole instrument
2129
- * rests on (critique M5/B3). One root `Budget` becomes a conserved pool of three
2130
- * quantities (tokens, usd, iterations) plus an absolute deadline. Children reserve
2131
- * atomically at spawn and reconcile at settle:
2132
- *
2133
- * total ≡ free + reserved + committed (invariant, always)
2134
- *
2135
- * `reserve` moves a child's whole ceiling from `free` → `reserved` and fails closed
2136
- * when `free` can't cover it (never read-then-spawn overcommit, so `Σk(treatment) ≡
2137
- * Σk(blind)` by construction). `reconcile` releases the reservation, commits ACTUAL
2138
- * spend, and refunds the unspent remainder to `free`. Tokens and usd are separate
2139
- * channels (`LoopTokenUsage` has no `usd`); iterations are conserved alongside them.
2140
- *
2141
- * Pure and deterministic: `now()` is injected, there is no I/O, and no wall-clock or
2142
- * RNG read. A `reserve`/`reconcile` ticket is single-use (fail-loud on double or
2143
- * unknown reconcile) so a child can never refund twice.
2144
- */
2145
-
2146
- /** Opaque, single-use reservation handle returned by `reserve` and consumed by
2147
- * `reconcile`. Carries the reserved ceilings so reconciliation needs no lookup. */
2148
- interface ReservationTicket {
2149
- readonly id: number;
2150
- readonly reserved: {
2151
- readonly tokens: number;
2152
- readonly usd: number;
2153
- readonly iterations: number;
2154
- };
2155
- }
2156
- /** Post-reservation pool readout — the shape `Scope.budget` exposes. `tokensLeft`,
2157
- * `usdLeft`, and `reservedTokens` reflect committed-but-unsettled reservations;
2158
- * `deadlineMs` is the ABSOLUTE wall-clock deadline (0 when the root set none).
2159
- * `usdCapped` distinguishes a real `usdLeft <= 0` exhaustion from an uncapped pool (which always
2160
- * reads `usdLeft: 0`) — the in-loop guard needs it to bound a usd-capped driver. */
2161
- type BudgetReadout = Readonly<{
2162
- tokensLeft: number;
2163
- usdLeft: number;
2164
- usdCapped: boolean;
2165
- deadlineMs: number;
2166
- reservedTokens: number;
2167
- }>;
2168
- interface BudgetPool {
2169
- /**
2170
- * Atomically reserve a child's full ceiling from the free balance. Fails closed
2171
- * ({ ok: false }) when the pool can't cover tokens, usd, or iterations — the
2172
- * caller inspects `ok` before `ticket`.
2173
- */
2174
- reserve(b: Budget): {
2175
- ok: true;
2176
- ticket: ReservationTicket;
2177
- } | {
2178
- ok: false;
2179
- reason: 'budget-exhausted';
2180
- };
2181
- /**
2182
- * Release a reservation: commit the actual `spent`, refund the unspent remainder
2183
- * to the free pool. Throws on an unknown or already-reconciled ticket (fail loud —
2184
- * a double refund would silently break conservation).
2185
- */
2186
- reconcile(ticket: ReservationTicket, spent: Spend): void;
2187
- /** Fold a normalized `UsageEvent` stream (or array) into a `Spend`. Tokens via
2188
- * `addTokenUsage`, usd on its own channel, iterations from `'iteration'` events.
2189
- * `ms` is left zero — wall-clock duration is the caller's to record, not the pool's. */
2190
- spendFrom(events: AsyncIterable<UsageEvent> | UsageEvent[]): Promise<Spend>;
2191
- /** The current readout, reflecting all outstanding reservations. */
2192
- readout(): BudgetReadout;
2193
- /**
2194
- * Record OBSERVED spend that did NOT go through reserve/reconcile — the driver's OWN inference
2195
- * (its chat turns), which is real compute but not a spawned child. A direct `free → committed`
2196
- * debit, so `total ≡ free + reserved + committed` is preserved: equal-k counts the driver's
2197
- * tokens and the in-loop budget guard (`readout().tokensLeft`) sees them. `free` may go negative
2198
- * when a run overspends — that is honest (the readout then signals exhaustion). It never throws:
2199
- * the spend already happened, so accounting records reality; the in-loop guard prevents MORE.
2200
- * The DURABLE record is the journal's `metered` event (written by `Scope.meter`); this debit
2201
- * only makes the live `readout()` reflect driver inference for the in-loop guard.
2202
- */
2203
- observe(spend: Spend): void;
2204
- /** Fail loud if any reservation is still open — the conserved-pool leak detector. Called at the
2205
- * supervisor's join barrier: once every child has settled, no ticket may remain (a leaked
2206
- * reservation would silently break `total ≡ free + reserved + committed`). */
2207
- assertNoOpenTickets(): void;
2208
- }
2209
- /** Fold a normalized `UsageEvent` array into a `Spend`. Tokens and usd are separate
2210
- * channels; iterations come from `'iteration'` events. Pure; `ms` stays zero (the
2211
- * pool does not read wall-clock). */
2212
- declare function spendFromUsageEvents(events: UsageEvent[]): Spend;
2213
- /**
2214
- * Create a conserved reservation pool from a root `Budget`. `now()` is injected so the
2215
- * deadline readout is deterministic; defaults to `Date.now` for non-test callers. The
2216
- * absolute deadline is fixed at construction (`now() + budget.deadlineMs`) so the
2217
- * readout's `deadlineMs` is a stable wall-clock instant, not a shrinking remainder.
2218
- */
2219
- declare function createBudgetPool(root: Budget, now?: () => number): BudgetPool;
2220
-
2221
- /**
2222
- * @experimental
2223
- *
2224
- * `coordinationDriverAgent` — the driver's BRAIN.
2225
- *
2226
- * The recursive driver-executor (`driver-executor.ts`) runs a driver `Agent.act` inside a
2227
- * nested `Scope`; this is the intelligent `act`: it mounts the coordination MCP verbs
2228
- * (`createCoordinationTools`) over that scope and runs an LLM tool-loop, so the driver
2229
- * REASONS — spawn / observe / steer / await / stop — about how to drive its children,
2230
- * instead of running a fixed script. Each turn: ask the driver LLM for tool calls, run them
2231
- * against the live scope, fold the results back, repeat until the driver stops (no tool
2232
- * calls) or the turn cap forces a keep-best finalize.
2233
- *
2234
- * Recursion composes through `makeWorkerAgent`: `spawn_worker` resolves a `profile` to a
2235
- * worker LEAF or — when the profile is a driver — a `driverChild` wrapping ANOTHER
2236
- * `coordinationDriverAgent` over its own nested scope (see `driver-executor.ts`). So an agent
2237
- * drives an agent that drives an agent, each an LLM tool-loop, all on one conserved-budget
2238
- * tree.
2239
- *
2240
- * Two seams are INJECTED so the loop runs offline with no creds and stays decoupled:
2241
- * - `chat` (`DriverChat`) — one driver-LLM turn; a test drives a scripted mock, production
2242
- * adapts the router's tool-calling.
2243
- * - `systemPrompt` — the driver's stance (the agent-eval worker-driver prompt / the prompt
2244
- * generator). Injected, never hardcoded — the prompt is a pluggable role.
2245
- */
2246
-
2247
- /** One tool call the driver LLM asks for this turn. */
2248
- interface DriverToolCall {
2249
- readonly id?: string;
2250
- readonly name: string;
2251
- readonly arguments: Record<string, unknown>;
2252
- }
2253
- /** A turn in the driver↔tools conversation. Tool results ride back as `role: 'tool'`. */
2254
- interface DriverMessage {
2255
- readonly role: 'user' | 'assistant' | 'tool';
2256
- readonly content: string;
2257
- readonly toolCalls?: ReadonlyArray<DriverToolCall>;
2258
- readonly toolCallId?: string;
2259
- readonly name?: string;
2260
- }
2261
- /** What the driver LLM returns each turn. No `toolCalls` => the driver is finished. */
2262
- interface DriverTurn {
2263
- readonly toolCalls?: ReadonlyArray<DriverToolCall>;
2264
- /** The driver's natural-language output — the answer when there are no tool calls. */
2265
- readonly content?: string;
2266
- /** The driver LLM's OWN token usage for THIS turn — metered against the conserved pool so the
2267
- * driver's inference counts toward equal-k AND the in-loop budget guard. Omit for a scripted/
2268
- * mock turn (no real inference); production `routerDriverChat` forwards it from the router. */
2269
- readonly usage?: {
2270
- readonly input: number;
2271
- readonly output: number;
2272
- };
2273
- /** The turn's inference cost (usd), when the provider priced it. */
2274
- readonly costUsd?: number;
2275
- }
2276
- /** The injected driver-LLM seam: one turn over the conversation + the coordination tool specs. */
2277
- interface DriverChat {
2278
- next(input: {
2279
- readonly system: string;
2280
- readonly messages: ReadonlyArray<DriverMessage>;
2281
- readonly tools: ReadonlyArray<{
2282
- name: string;
2283
- description: string;
2284
- parameters: unknown;
2285
- }>;
2286
- }): Promise<DriverTurn>;
2287
- }
2288
- interface CoordinationDriverOptions {
2289
- readonly name: string;
2290
- /** The driver-LLM seam (scripted mock offline; router tool-calling in production). */
2291
- readonly chat: DriverChat;
2292
- /** Shared blob store — `observe_worker` reads settled outputs through it. */
2293
- readonly blobs: ResultBlobStore;
2294
- /** Resolve a spawned `profile` to a worker LEAF or a driver child (the recursion seam). */
2295
- readonly makeWorkerAgent: MakeWorkerAgent;
2296
- /** Per-child budget reserved from the conserved pool on each spawn. */
2297
- readonly perWorker: Budget;
2298
- /** The driver's stance — a string, or built from the task (the worker-driver prompt /
2299
- * the generator). INJECTED so the prompt is a pluggable, optimizable role. */
2300
- readonly systemPrompt: string | ((task: unknown) => string);
2301
- /** Max driver turns before the loop force-finalizes on the best settled child. Default 16.
2302
- * `0` lifts the turn-COUNT cap: the loop is bounded instead by the conserved budget pool,
2303
- * an absolute deadline, the driver's own stop, and abort (checked in-loop). A finite
2304
- * anti-runaway tripwire still guards a degenerate driver that loops on a no-spawn tool. */
2305
- readonly maxTurns?: number;
2306
- /** Injected clock for the in-loop absolute-deadline guard — keeps the deadline check
2307
- * deterministic in tests. Defaults to `Date.now`. */
2308
- readonly now?: () => number;
2309
- }
2310
- /**
2311
- * Build the intelligent recursive driver. Its `act` is the LLM tool-loop; spawn it as a
2312
- * `driverChild` (`driver-executor.ts`) to run it inside a nested scope, recursively.
2313
- */
2314
- declare function coordinationDriverAgent(opts: CoordinationDriverOptions): Agent<unknown, unknown>;
2315
-
2316
- /**
2317
- * @experimental
2318
- *
2319
- * Serve the coordination verbs (spawn_worker / await_event / observe_worker / steer_worker / stop)
2320
- * as a real HTTP MCP server over a LIVE `Scope`. This is the keystone that lets a coding-harness
2321
- * agent (opencode via the cli-bridge, claude-code, codex) BE the supervisor: it mounts this MCP
2322
- * (`mcp.mcpServers.coordination`) and calls `spawn_worker` as a native tool, which lands on
2323
- * `Scope.spawn` — a real box driving real boxes, not emulated function-tools.
2324
- *
2325
- * Transport: JSON-RPC over HTTP POST (the MCP streamable-HTTP shape — `application/json` for a
2326
- * single response). The server is created INSIDE an agent's `act(task, scope)` so it fronts that
2327
- * agent's live scope; tear it down when the act returns.
2328
- */
2329
-
2330
- interface CoordinationMcpHandle {
2331
- /** The URL an in-box harness mounts as `mcp.mcpServers.coordination.url`. */
2332
- readonly url: string;
2333
- readonly port: number;
2334
- /** The coordination tools' settled-worker ledger (for the driver's finalize). */
2335
- settled(): ReadonlyArray<{
2336
- status: string;
2337
- score?: number;
2338
- valid?: boolean;
2339
- outRef?: string;
2340
- }>;
2341
- isStopped(): boolean;
2342
- /** The full ordered bus-event log — observability audit + replay trail. */
2343
- history: CoordinationTools['history'];
2344
- /** Bus throughput counters for live dashboards. */
2345
- stats: CoordinationTools['stats'];
2346
- /** Raise a `finding` on the bus from an online detector watching a worker's live pipe. */
2347
- raiseFinding: CoordinationTools['raiseFinding'];
2348
- close(): Promise<void>;
2349
- }
2350
- /** Stand up the coordination MCP over a live scope. The HOST address is `127.0.0.1` (the bridge runs
2351
- * opencode locally, same host); pass `host` to bind elsewhere when the harness is remote. */
2352
- declare function serveCoordinationMcp(opts: {
2353
- scope: Scope<unknown>;
2354
- blobs: ResultBlobStore;
2355
- makeWorkerAgent: MakeWorkerAgent;
2356
- perWorker: Budget;
2357
- port?: number;
2358
- host?: string;
2359
- /** Trace-analyst lenses the driver can run (`run_analyst`) or auto-fire on settle. */
2360
- analysts?: AnalystRegistry;
2361
- /** Analyst kinds to auto-run when a worker settles `done` — findings flow up the bus. */
2362
- analyzeOnSettle?: ReadonlyArray<string>;
2363
- /** Pass-through subscriber for every bus event (settled / question / finding). */
2364
- onEvent?: (event: CoordinationEvent) => void | Promise<void>;
2365
- questionPolicy?: QuestionPolicy;
2366
- }): Promise<CoordinationMcpHandle>;
2367
-
2368
- /**
2369
- * @experimental
2370
- *
2371
- * `TraceSource` — the ONE substrate-agnostic source of a worker's tool-call trace. The online
2372
- * detectors and the settle-time analyzers consume agent-eval `ToolSpan`s from here, regardless of
2373
- * whether the worker is:
2374
- * - an OWNED tool loop (router-tools, cli-bridge tool dispatch) → push spans as we dispatch them;
2375
- * - a SANDBOX / fleet box → read the harness's tool calls off the session (`streamPrompt` parts
2376
- * live, `session.messages()` / `findCompletedTurn` at settle).
2377
- *
2378
- * The common currency is agent-eval's `ToolSpan` (so the same detectors + `buildTrajectory`/
2379
- * `stuckLoopView`/`toolWasteView` run over any source). A source exposes two lanes:
2380
- * - `onSpan` — live spans for ONLINE detection (best-effort; a black-box box may only collect).
2381
- * - `collect` — the full span set at settle for the BATCH analyzers (always available).
2382
- *
2383
- * This module imports NO substrate SDK — it decodes generic message parts / OpenAI tool-call shapes.
2384
- * The sandbox wiring (`sandboxSessionTraceSource`) is the thin adapter that feeds box session parts in.
2385
- */
2386
-
2387
- interface ToolStepInput {
2388
- readonly toolName: string;
2389
- readonly args: unknown;
2390
- readonly status?: 'ok' | 'error';
2391
- readonly result?: unknown;
2392
- /** Stable id of the tool call — used to de-duplicate the repeated state transitions a harness
2393
- * streams for one call (opencode emits pending→running→completed, plus a `raw`-wrapped copy). */
2394
- readonly callId?: string;
2395
- }
2396
- interface TraceSource {
2397
- /** Subscribe to tool spans as they are produced (ONLINE). Returns an unsubscribe. A source that
2398
- * only exposes its trace at the end registers nothing and returns a no-op. */
2399
- onSpan(handler: (span: ToolSpan) => void): () => void;
2400
- /** The full set of tool spans for the run (SETTLE / batch). Always available. */
2401
- collect(): Promise<ToolSpan[]>;
2402
- }
2403
- /** Project a normalized tool step into the canonical agent-eval `ToolSpan`. */
2404
- declare function toToolSpan(input: ToolStepInput, runId: string, seq: number, at: number): ToolSpan;
2405
- /** Decode one harness message part into a tool step, or `undefined` if it is not a (completed) tool
2406
- * call. ONE adapter per harness family — each owns its real wire shape; the flow downstream is
2407
- * identical. Add a harness = add a decoder + register it; no other code changes. */
2408
- type ToolPartDecoder = (part: Record<string, unknown>) => ToolStepInput | undefined;
2409
- /** opencode parts ARE agent-interface's canonical `ToolPart` (`{ type:'tool', tool, callID?,
2410
- * state: ToolState }`) — the shape every adc sdk-provider normalizes its harness output into. We
2411
- * decode against that published type (single source of truth) rather than a re-derived shape; the
2412
- * `ToolState` union drives the status mapping, so a status that adc adds/renames is a compile error
2413
- * here, not a silent miss. The same call streams pending→running→terminal; only a terminal state
2414
- * (`completed` / `error` / `failed`) is a finished call. */
2415
- declare const decodeOpencodePart: ToolPartDecoder;
2416
- /** Anthropic / claude-code (also kimi's tool_use variant): a `{ type:'tool_use', id|tool_use_id,
2417
- * name|tool, input }` content block. CONFIRMED against the cli-bridge's authoritative parsers
2418
- * (claude.ts reads `block.type==='tool_use', block.id, block.name, block.input`; kimi.ts adds the
2419
- * `tool_use_id`/`tool` fallbacks) — the canonical readers of these harnesses' real native output. */
2420
- declare const decodeAnthropicPart: ToolPartDecoder;
2421
- /** OpenAI-compatible (router / kimi's top-level form / glm): `{ type:'function'|'tool_call', id,
2422
- * function:{ name, arguments:<JSON string> } }`. CONFIRMED against the cli-bridge (kimi.ts surfaces
2423
- * kimi's top-level `tool_calls:[{type:'function', id, function:{name,arguments}}]` exactly this way).
2424
- * NOTE: codex does NOT emit structured tool calls (the bridge's codex.ts never yields `tool_calls` —
2425
- * it runs shell internally and surfaces only text), so per-tool detection is unavailable for codex
2426
- * from any path — a harness property, not a decoder gap. */
2427
- declare const decodeOpenAiPart: ToolPartDecoder;
2428
- /** The harness → decoder registry. Add a harness by adding one entry. */
2429
- declare const toolPartDecoders: Record<string, ToolPartDecoder>;
2430
- /** Decode a part with a specific harness's adapter when known, else try every registered adapter
2431
- * (the composite — robust to mixed/unknown streams). Never throws. */
2432
- declare function decodeToolPart(part: unknown, harness?: string): ToolStepInput | undefined;
2433
- /** A push source for OWNED tool loops (router-tools / cli-bridge tool dispatch): the loop calls
2434
- * `record(step)` for each tool call; it becomes a span, fan-out to live subscribers + buffered for
2435
- * `collect`. */
2436
- declare function createPushTraceSource(opts?: {
2437
- runId?: string;
2438
- now?: () => number;
2439
- }): {
2440
- source: TraceSource;
2441
- record: (input: ToolStepInput) => ToolSpan;
2442
- };
2443
- /** A source backed by harness message PARTS (sandbox session, cli-bridge). `collect` reads the full
2444
- * part list and decodes the tool calls; `subscribe`, when given, streams parts live for online
2445
- * detection. The caller supplies how to get parts (e.g. `box.session(id).messages()` flat-mapped to
2446
- * parts) — keeping this module free of any substrate SDK. */
2447
- declare function createPartsTraceSource(opts: {
2448
- collectParts: () => Promise<ReadonlyArray<unknown>>;
2449
- subscribeParts?: (onPart: (part: unknown) => void) => () => void;
2450
- /** The harness whose decoder to use (e.g. 'opencode'); omit to try every registered adapter. */
2451
- harness?: string;
2452
- runId?: string;
2453
- now?: () => number;
2454
- }): TraceSource;
2455
- /** A harness session message carrying parts (the shape `box.messages()` returns). Structurally typed
2456
- * so this works with the real `@tangle-network/sandbox` box AND a test double, no SDK import. */
2457
- interface SessionMessageLike {
2458
- readonly parts?: ReadonlyArray<unknown>;
2459
- }
2460
- /** The minimal box surface this needs: list a session's messages (incl. mid-turn partials). */
2461
- interface SessionTraceBox {
2462
- messages(opts: {
2463
- sessionId: string;
2464
- }): Promise<ReadonlyArray<SessionMessageLike>>;
2465
- }
2466
- /** The SANDBOX / fleet trace source: read a box session's message parts and decode the harness's tool
2467
- * calls into spans. `collect` (settle) is the solid path — `box.messages({sessionId})` → parts → spans;
2468
- * black-box harnesses aren't mid-step interruptible, so online steering is the owned-loop's job and a
2469
- * live `subscribe` is opt-in (pass `subscribeParts` from `streamPrompt` when the harness streams parts). */
2470
- declare function sandboxSessionTraceSource(box: SessionTraceBox, sessionId: string, opts?: {
2471
- /** The box's harness (e.g. 'opencode', 'claude-code') → selects its decoder adapter. */
2472
- harness?: string;
2473
- subscribeParts?: (onPart: (part: unknown) => void) => () => void;
2474
- runId?: string;
2475
- now?: () => number;
2476
- }): TraceSource;
2477
-
2478
- /**
2479
- * @experimental
2480
- *
2481
- * The ONLINE analyst: watch a `TraceSource` and fold each tool span through agent-eval's published
2482
- * streaming detector kernel (`repeatedActionDetector`/`errorStreakDetector` — the SAME kernel the
2483
- * control loop folds), firing `onSignal` the moment a worker loops or error-storms. Substrate-
2484
- * agnostic: it consumes spans from any source (owned router/bridge loop OR a sandbox box session),
2485
- * never the raw tool seam. Detection logic + the failure taxonomy live in agent-eval; not reimplemented.
2486
- */
2487
-
2488
- interface WatchTraceOptions {
2489
- /** The detectors to run online. Defaults to a stuck-loop + error-streak panel. */
2490
- readonly detectors?: ReadonlyArray<StreamingDetector>;
2491
- /** Fired for each signal a detector raises — the seam that raises a `finding` on the bus. */
2492
- readonly onSignal?: (signal: DetectorSignal, span: ToolSpan) => void | Promise<void>;
2493
- }
2494
- /** The default online panel for a tool-call pipe: a worker repeating the same call, or hammering
2495
- * consecutive errors. (No-progress needs a domain progress-probe, so it is opt-in, not default.)
2496
- *
2497
- * Coverage note: `repeated-action` works for EVERY harness (it needs only tool name + args, which
2498
- * every adapter provides). `error-streak` needs per-call status — opencode carries it inline
2499
- * (`state.status`, VALIDATED live), but claude-code/codex tool-call parts do NOT (their errors live
2500
- * in separate result blocks not yet decoded), so error-streak is silent for those until result-block
2501
- * decoding is added + live-validated. It is in the panel because it is correct where status exists. */
2502
- declare function defaultToolDetectors(): StreamingDetector[];
2503
- /** Subscribe to a `TraceSource` and run the streaming detectors over its live spans. Returns an
2504
- * unsubscribe. A defensive `argHash` failure (circular args) never throws out of the side-channel. */
2505
- declare function watchTrace(source: TraceSource, opts?: WatchTraceOptions): () => void;
2506
-
2507
- /**
2508
- * @experimental
2509
- *
2510
- * The recursive driver-executor — the seam that lets a SPAWNED child be a DRIVER, so
2511
- * agents drive agents drive agents over the one keystone atom.
2512
- *
2513
- * A spawned child resolves through the open registry to an `Executor`; the built-in
2514
- * executors (router/inline, sandbox, cli) are LEAVES — `execute(task, signal)` runs the
2515
- * work and settles. This executor is the recursive case: on `execute`, it mounts a NESTED
2516
- * `Scope` (the scope hands it the mount via the `nested-scope` seam) over the SAME
2517
- * conserved pool + shared journal/blobs + the same open registry, one `depth` deeper, then
2518
- * runs the wrapped driver `Agent.act(task, nestedScope)`. The driver spawns its own
2519
- * children into that nested scope; each resolves to EITHER a leaf executor (a worker child)
2520
- * OR this same driver-executor (a driver child) — recursively. So a driver spawns a driver
2521
- * spawns a worker, all on one budget-conserving tree.
2522
- *
2523
- * Why this preserves every keystone invariant (the scope owns the sharing; this executor
2524
- * only runs the driver over what the scope mounts):
2525
- * - Conserved budget: the nested scope reserves from the SAME `BudgetPool` the root owns
2526
- * (the scope mounts it over `args.pool`), so `Σk` is conserved ACROSS depth by
2527
- * construction — a deep tree cannot overspend the root ceiling (reserve-on-spawn fails
2528
- * closed at any depth).
2529
- * - Journal: the nested scope writes to its OWN tree key (`${journalRoot}/${nodeId}`) so
2530
- * its cursor `seq`s never collide with the parent's in the per-tree uniqueness guard,
2531
- * while every nested tree shares the one `SpawnJournal` — the whole recursion is one
2532
- * journal, queryable tree by tree.
2533
- * - Settlement bubbling: the driver child settles into its PARENT scope with the conserved
2534
- * spend summed off its nested tree's settled events, so the parent's pool reconcile +
2535
- * the supervisor's `spentTotal` see the whole sub-tree's spend rolled up — settlements
2536
- * bubble to the root.
2537
- * - Depth ceiling: the nested scope runs at `depth+1`, so the supervisor's `maxDepth`
2538
- * (paired with the conserved pool per R3) fails a spawn closed once the recursion is too
2539
- * deep — exactly as it does for a flat tree.
2540
- *
2541
- * Layering: pure keystone composition. It reuses the scope's `NestedScopeSeam` + the shared
2542
- * `SpawnJournal`; it builds NO new budget, journal, or selection logic. The recursion rides
2543
- * the existing atom.
2544
- */
2545
-
2546
- /** The runtime tag the registry maps a driver child to. */
2547
- declare const driverRuntime: "driver";
2548
- /** A driver child's spec carries the `Agent` to run inside the nested scope. */
2549
- interface DriverSpec extends AgentSpec {
2550
- readonly driver: Agent<unknown, unknown>;
2551
- /** The shared journal the nested tree is one tree key inside (so the executor can
2552
- * begin its nested tree + sum its spend off the same record). */
2553
- readonly journal: SpawnJournal;
2554
- }
2555
- /**
2556
- * Mark + carry a driver `Agent` so the recursive registry resolves it to the
2557
- * driver-executor. The returned agent is SPAWNED (never run directly): its
2558
- * `executorSpec` is marked `role: 'driver'` and carries the driver agent + the shared
2559
- * journal so the executor can run its `act` inside a nested scope. `act` fails loud if
2560
- * called directly — a driver child runs THROUGH its nested-scope executor, never as a root.
2561
- */
2562
- declare function driverChild<Out>(name: string, driver: Agent<unknown, Out>, journal: SpawnJournal): Agent<unknown, Out>;
2563
- /** True when a spec is a driver child (carries the role marker + a driver Agent). */
2564
- declare function isDriverSpec(spec: AgentSpec): spec is DriverSpec;
2565
- /**
2566
- * The recursive driver-executor factory. `withDriverExecutor` routes a child marked
2567
- * `role: 'driver'` here; any other child resolves to a leaf built-in. On `execute`, it
2568
- * reads the `nested-scope` seam the SCOPE seeded, mounts a nested `Scope` one `depth`
2569
- * deeper over the shared pool/journal/blobs/registry, runs the driver
2570
- * `Agent.act(task, nestedScope)`, and reports the conserved spend summed off the nested
2571
- * tree's settled events — so the parent scope's reconcile rolls the whole sub-tree's spend
2572
- * into the conserved total.
2573
- *
2574
- * A `down` from the nested driver (a thrown `act` or an aborted scope) propagates as a
2575
- * thrown executor, which the parent scope types into a `down` settlement — the same
2576
- * fail-loud-into-typed-down discipline a leaf gets.
2577
- */
2578
- declare const driverExecutorFactory: ExecutorFactory<unknown>;
2579
- /**
2580
- * Register the driver-executor so a child marked `role: 'driver'` resolves to it. The base
2581
- * registry resolves by harness alone (it does not read `role`), so a recursive run needs a
2582
- * registry that routes the driver tag here FIRST. Returns a registry decorator: a
2583
- * driver-role spec → the driver-executor; everything else → the base registry's resolution
2584
- * (leaf built-ins + BYO).
2585
- */
2586
- declare function withDriverExecutor(base: ExecutorRegistry): ExecutorRegistry;
2587
-
2588
- /**
2589
- * @experimental
2590
- *
2591
- * The worker-side receive end of the down-leg: a per-worker inbox an executor exposes as
2592
- * `Executor.deliver`. The driver's `steer_worker` / `answer_question` land here,
2593
- * and the worker's agent loop drains them at two points (Drew's two delivery modes):
2594
- *
2595
- * - QUEUED (default): the message accumulates and is FLUSHED at the next step boundary — folded
2596
- * into the conversation before the next think. A worker is also forced to flush BEFORE it may
2597
- * settle, so it can never finish while a steer/answer it never read is still pending.
2598
- * - FORCEFUL (`interrupt: true`): trips `freshInterrupt()`'s signal so the loop can abort its
2599
- * in-flight turn immediately, then re-plan with the message folded in — breaking the worker out
2600
- * of a wrong path mid-task instead of waiting for it to finish the step.
2601
- *
2602
- * `deliver` never throws — a malformed message is ignored, per the `Executor.deliver` contract.
2603
- */
2604
- interface InboxMessage {
2605
- readonly kind: 'steer' | 'answer';
2606
- readonly text: string;
2607
- /** Forceful messages abort the in-flight turn; queued ones wait for the boundary flush. */
2608
- readonly interrupt: boolean;
2609
- /** Present for an `answer` — the question id it resolves. */
2610
- readonly questionId?: string;
2611
- }
2612
- interface Inbox {
2613
- /** The `Executor.deliver` implementation — accept a raw down-message from `Scope.send`. */
2614
- deliver(msg: unknown): void;
2615
- /** Remove and return all pending messages (the flush). */
2616
- drain(): InboxMessage[];
2617
- pending(): number;
2618
- /** Open a fresh per-turn interrupt signal; a later forceful `deliver` aborts it. The loop links
2619
- * this into the signal it passes to its inference call, then re-plans when it fires. */
2620
- freshInterrupt(): AbortSignal;
2621
- /** Render drained messages as ONE operator turn to fold into the worker's conversation. */
2622
- fold(messages: ReadonlyArray<InboxMessage>): string;
2623
- }
2624
- declare function createInbox(): Inbox;
2625
-
2626
- /**
2627
- * The router-backed driver-LLM seam: adapt the OpenAI-compatible router's tool-calling chat
2628
- * to the `DriverChat` port a `coordinationDriverAgent` drives. Tests script a mock `DriverChat`;
2629
- * production passes `routerDriverChat(cfg)` so the driver's spawn/observe/steer/await/stop turns
2630
- * are real router tool-calls. This is the one turnkey piece a consumer needs to run an in-process
2631
- * supervisor (the driver brain) — everything else is `createSupervisor().run(...)`.
2632
- */
2633
-
2634
- declare function routerDriverChat(c: RouterConfig, opts?: {
2635
- temperature?: number;
2636
- }): DriverChat;
2637
-
2638
- /**
2639
- * @experimental
2640
- *
2641
- * `createInMemoryRunContext` — the one-call bundle of the in-memory stores a
2642
- * `createSupervisor().run(root, task, opts)` needs: a fresh `InMemorySpawnJournal`
2643
- * (the event-sourced spawn log), a fresh `InMemoryResultBlobStore` (the
2644
- * content-addressed `outRef` payload store the driver's `observe`/`finalize` reads
2645
- * settled outputs through), and a fresh `createExecutorRegistry()` (the open
2646
- * `AgentSpec → Executor` resolver).
2647
- *
2648
- * It exists to kill the boilerplate every offline/local supervised run repeats by
2649
- * hand — three constructors threaded into `SupervisorOpts` — and to single-source the
2650
- * ONE wiring invariant that is easy to get wrong: when the root is the recursive
2651
- * `coordinationDriverAgent` LLM-driver brain AND it may spawn DRIVER children (agents
2652
- * driving agents), the registry MUST be wrapped with `withDriverExecutor` so a
2653
- * `role: 'driver'` child resolves to the nested-scope executor — and that SAME blob
2654
- * store MUST be the one passed to `coordinationDriverAgent({ blobs })`, or the driver
2655
- * reads from a different store than the scope writes to. Pass `{ withDriver: true }`
2656
- * and reuse the returned `blobs` for both.
2657
- *
2658
- * The spread shape matches `SupervisorOpts` exactly, so the call site reads:
2659
- * const run = createInMemoryRunContext()
2660
- * await createSupervisor().run(root, task, { budget, runId, ...run })
2661
- */
2662
-
2663
- /** Options for the in-memory run context. */
2664
- interface InMemoryRunContextOptions {
2665
- /**
2666
- * Wrap the executor registry with `withDriverExecutor` so a spawned child marked
2667
- * `role: 'driver'` resolves to the recursive driver-executor (agents driving agents
2668
- * over a nested `Scope` on the same conserved pool). Leave `false` for a flat tree of
2669
- * leaf workers. Default `false`.
2670
- */
2671
- readonly withDriver?: boolean;
2672
- }
2673
- /**
2674
- * The bundle of stores a supervised run needs, shaped to spread into `SupervisorOpts`.
2675
- * The fields are exactly `SupervisorOpts`' `journal` / `blobs` / `executors`.
2676
- */
2677
- interface InMemoryRunContext {
2678
- readonly journal: SpawnJournal;
2679
- readonly blobs: ResultBlobStore;
2680
- readonly executors: ExecutorRegistry;
2681
- }
2682
- /**
2683
- * Build a fresh in-memory run context. Every call returns NEW stores (no shared global
2684
- * state between runs), so two runs never cross-contaminate their journals/blobs.
2685
- */
2686
- declare function createInMemoryRunContext(opts?: InMemoryRunContextOptions): InMemoryRunContext;
2687
-
2688
- /**
2689
- * @experimental
2690
- *
2691
- * The leaf runtime — the built-in `Executor` IMPLEMENTATIONS behind the ONE
2692
- * open interface frozen in `./types`, plus the open resolver/registry that maps
2693
- * an `AgentSpec` to one of them OR accepts a bring-your-own executor verbatim.
2694
- *
2695
- * The interface is the extension point, not a closed `inline|sandbox|cli` union:
2696
- * - router/inline : a direct OpenAI-compatible Router call, no box (one-shot).
2697
- * - sandbox : COMPOSES the existing `runLoop` kernel as a single-task
2698
- * leaf and surfaces its token/cost usage as `UsageEvent`s;
2699
- * forwards PR #150's optional `lineage` passthrough WITHOUT
2700
- * reinventing checkpoint/fork (streaming).
2701
- * - cli : a Halo/RLM subprocess; `budgetExempt` (no token accounting),
2702
- * excluded from the equal-k arms by construction (streaming).
2703
- * Every metered runtime reports through the SAME normalized `UsageEvent` channel
2704
- * so the conserved budget pool meters them identically. A user's own agent is
2705
- * first-class the moment it implements `Executor` — register it by name or
2706
- * pass it as `AgentSpec.executor`.
2707
- *
2708
- * Layering: `estimateCost`/`isModelPriced` are substrate primitives from
2709
- * `@tangle-network/agent-eval`; `runLoop`/`acquireSandbox` are runtime kernels
2710
- * from this package. No per-vendor adapters live here.
2711
- */
2712
-
2713
- /**
2714
- * Router/inline connection seam. A direct OpenAI-compatible Router endpoint —
2715
- * the cheapest leaf, no box, no tools. `model` overrides the profile's model
2716
- * hint when present; otherwise the profile's `model.default` is required.
2717
- */
2718
- interface RouterSeam {
2719
- routerBaseUrl: string;
2720
- routerKey: string;
2721
- model?: string;
2722
- }
2723
- /**
2724
- * Sandbox executor seam. The `sandboxClient` the composed `runLoop` creates
2725
- * boxes through, plus the optional trace/run/lineage wiring forwarded into the
2726
- * loop. `lineage` is opaque here (PR #150's `RunLoopOptions.lineage`): forwarded
2727
- * forward-compatibly, never inspected — this executor does NOT reinvent
2728
- * checkpoint/fork.
2729
- */
2730
- interface SandboxSeam {
2731
- sandboxClient: SandboxClient;
2732
- /** Forwarded into the composed `runLoop`'s `ctx` (trace emitter, run handle, etc.). */
2733
- loopCtx?: Partial<Omit<ExecCtx, 'sandboxClient' | 'signal'>>;
2734
- /** PR #150 `RunLoopOptions.lineage` passthrough — opaque; forwarded, not parsed. */
2735
- lineage?: unknown;
2736
- /** Hard cap on the composed loop's iterations. The budget pool reserves against
2737
- * the spawn `Budget.maxIterations`; this is the leaf's own ceiling. Default 1. */
2738
- maxIterations?: number;
2739
- }
2740
- /** CLI subprocess seam. `bin` + `args` describe the Halo/RLM process to spawn. */
2741
- interface CliSeam {
2742
- bin: string;
2743
- args?: string[];
2744
- /** Extra environment for the subprocess (merged over `process.env`). */
2745
- env?: Record<string, string>;
2746
- /** Working directory for the subprocess. */
2747
- cwd?: string;
2748
- }
2749
- /**
2750
- * cli-worktree seam. A supervisor-authored `AgentProfile` driving a local coding-harness CLI
2751
- * (claude / codex / opencode) on its own git worktree — the leaf `createWorktreeCliExecutor`
2752
- * named as data. `harness` + `repoRoot` + `taskPrompt` are required; the authored
2753
- * `profile.prompt.systemPrompt` + `profile.model.default` reach the harness via the §1.5
2754
- * `harnessInvocation` mapper. Everything else mirrors `WorktreeCliExecutorOptions`.
2755
- */
2756
- interface CliWorktreeSeam {
2757
- repoRoot: string;
2758
- harness: LocalHarness;
2759
- taskPrompt: string;
2760
- runId?: string;
2761
- baseRef?: string;
2762
- harnessTimeoutMs?: number;
2763
- }
2764
- /**
2765
- * cli-bridge seam. A local OpenAI-compatible bridge that fronts harness CLIs
2766
- * (claude-code / opencode / kimi / pi) behind one HTTP surface; `model` doubles
2767
- * as the harness selector (e.g. `claude-code/sonnet`, `opencode/<provider>/<model>`).
2768
- * `agentProfile` is the bridge-dialect profile (metadata.disallowedTools, mcp)
2769
- * forwarded verbatim per request — how an arm disables native tools or injects
2770
- * a provider search MCP.
2771
- *
2772
- * The executor opens a RESUMABLE cli-bridge session — structurally identical to the
2773
- * sandbox executor's persistent box, just local. `sessionId` is the stable
2774
- * caller-owned id cli-bridge maps to the harness's internal conversation id; a
2775
- * follow-up steer/resume on the SAME id continues the SAME harness session (opencode
2776
- * `-s`, claude `--resume`, …). Omit it and the executor mints a stable one per spawn.
2777
- */
2778
- interface BridgeSeam {
2779
- bridgeUrl: string;
2780
- bridgeBearer: string;
2781
- model: string;
2782
- agentProfile?: Record<string, unknown>;
2783
- timeoutMs?: number;
2784
- /** Stable, caller-owned cli-bridge session id for harness-side resume. Defaults
2785
- * to a freshly minted per-spawn id so each worker is its own resumable session. */
2786
- sessionId?: string;
2787
- /** Per-resume-turn inference cap before the worker settles on its last output.
2788
- * Mirrors `routerToolsInlineExecutor.maxTurns`; default 200 (runaway backstop). */
2789
- maxTurns?: number;
2790
- }
2791
- /**
2792
- * Router seam WITH tool use — the tool-using router backend. Same direct
2793
- * OpenAI-compatible endpoint as `RouterSeam`, but each turn passes `tools`; when
2794
- * the model emits tool_calls they run via `executeToolCall` ON THIS HOST and the
2795
- * results fold back as `tool` messages, repeating until the model answers without
2796
- * a tool or `maxTurns` is hit. A real agentic loop, OFF-BOX — no sandbox, so it
2797
- * is unaffected by a box's egress allowlist. One turn = one completion = the
2798
- * equal-compute unit. `executeToolCall` receives the task so per-task tool
2799
- * surfaces (e.g. a gym keyed by task) can dispatch correctly.
2800
- */
2801
- interface RouterToolsSeam {
2802
- routerBaseUrl: string;
2803
- routerKey: string;
2804
- model?: string;
2805
- tools: ReadonlyArray<ToolSpec>;
2806
- executeToolCall: (name: string, args: Record<string, unknown>, task: unknown) => Promise<string>;
2807
- /** Online observer of each tool step — the seam a `DetectorMonitor` taps to watch the live pipe
2808
- * (raise a `finding` when the worker loops/errors). Called after every tool call resolves. */
2809
- onToolStep?: (step: {
2810
- toolName: string;
2811
- args: Record<string, unknown>;
2812
- status: 'ok' | 'error';
2813
- }) => void;
2814
- /** Max inference turns. Default 200 (runaway backstop — set far above any
2815
- * legitimate workflow). For tighter per-workflow limits use a cost budget
2816
- * or wall-clock deadline at the call site. */
2817
- maxTurns?: number;
2818
- }
2819
- /**
2820
- * The leaf `createWorktreeCliExecutor` as a backend-as-data factory: a supervisor-authored
2821
- * `AgentProfile` driving claude / codex / opencode on its own worktree. `budgetExempt` like
2822
- * the other CLI leaves; the authored systemPrompt + model reach the harness via §1.5.
2823
- */
2824
- declare const cliWorktreeExecutor: ExecutorFactory<unknown>;
2825
- /**
2826
- * The single built-in executor entrypoint. The backend is DATA — the cost dial a
2827
- * profile, an experiment config, or a replay journal can name — not an import
2828
- * choice. Injects the matching seam and delegates to the built-in implementation;
2829
- * the port stays OPEN: bring-your-own agents implement `Executor` directly and
2830
- * never pass through here.
2831
- */
2832
- type ExecutorConfig = ({
2833
- backend: 'router';
2834
- } & RouterSeam) | ({
2835
- backend: 'router-tools';
2836
- } & RouterToolsSeam) | ({
2837
- backend: 'bridge';
2838
- } & BridgeSeam) | ({
2839
- backend: 'cli';
2840
- } & CliSeam) | ({
2841
- backend: 'cli-worktree';
2842
- } & CliWorktreeSeam) | ({
2843
- backend: 'sandbox';
2844
- harness?: BackendType;
2845
- } & SandboxSeam);
2846
- declare function createExecutor(config: ExecutorConfig): ExecutorFactory<unknown>;
2847
- /**
2848
- * The open resolver/registry. Pre-registers the three built-ins under their
2849
- * runtime tags (`'router'`, `'sandbox'`, `'cli'`) and accepts `register(name,
2850
- * factory)` for any additional runtime — and a BYO `AgentSpec.executor` resolves
2851
- * without touching the registry at all. NOT a closed switch; registration + BYO
2852
- * ARE the extension points.
2853
- *
2854
- * `resolve` precedence (frozen in `ExecutorRegistry`): a BYO `spec.executor` →
2855
- * `harness === null` → the `'router'` factory; else a registered factory for the
2856
- * harness-derived runtime (`'sandbox'` for any `BackendType`); else fail loud.
2857
- */
2858
- declare function createExecutorRegistry(): ExecutorRegistry;
2859
-
2860
- /**
2861
- * @experimental
2862
- *
2863
- * The reactive `Scope` impl (KEYSTONE, build step 4 + the step-8 adapter).
2864
- *
2865
- * An `Agent.act` runs inside a `Scope`. It `spawn`s children dynamically and reacts to
2866
- * them via `next()`. The scope owns ONE in-memory nursery — the authoritative live set —
2867
- * and is the single place that drives a child's lifecycle: reserve budget atomically,
2868
- * resolve a `Executor` through the open registry, run it (one-shot OR streaming),
2869
- * fold its normalized `UsageEvent`s into a conserved `Spend`, reconcile the reservation
2870
- * (refunding the unspent remainder), persist the result blob + journal records, and
2871
- * deliver the `Settled` through the `next()` cursor.
2872
- *
2873
- * Three invariants this impl enforces by construction:
2874
- * - `next()` is a ray.wait n=1 cursor over THIS scope's live set; it assigns the
2875
- * monotonic `seq` (the recorded cursor order) at the moment it yields a settlement, so
2876
- * replay re-delivers in the identical order — `seq` is never wall-clock.
2877
- * - Budget is reserved at spawn and reconciled at settle through the shared `BudgetPool`,
2878
- * so `spawn` fails CLOSED on an exhausted pool and total ≡ free + reserved + committed.
2879
- * - `view` reads the in-memory nursery, never the journal — O(live), synchronous.
2880
- *
2881
- * The settle path is the only writer of journal `settled` events; the spawn path the only
2882
- * writer of `spawned` events. The result blob is `put` BEFORE the journal `settled` record
2883
- * references its `outRef`, so a crash can never leave a journaled ref with no blob.
2884
- */
2885
-
2886
- /** Construction args for `createScope`. The supervisor threads the shared pool, journal,
2887
- * blob store, and executor registry through; `depth`/`maxDepth` pair the runtime
2888
- * recursion ceiling with the conserved pool (R3). */
2889
- interface ScopeArgs {
2890
- /** This scope's owning node id — children get `${parentId}:s${seq}` ids. */
2891
- readonly parentId: NodeId;
2892
- /** Journal/blob root key the supervisor `beginTree`'d. */
2893
- readonly root: NodeId;
2894
- /** The shared conserved reservation pool (one per supervised run). */
2895
- readonly pool: BudgetPool;
2896
- /** Append-only spawn journal; this scope writes `spawned` + `settled` records. */
2897
- readonly journal: SpawnJournal;
2898
- /** Content-addressed result store backing `outRef` rehydration. */
2899
- readonly blobs: ResultBlobStore;
2900
- /** The open executor resolver (BYO → router/inline → registered harness factory). */
2901
- readonly executors: ExecutorRegistry;
2902
- /** Per-spawn executor-construction seams (sandbox client, router config, cli bin). */
2903
- readonly seams: Readonly<Record<string, unknown>>;
2904
- /** This scope's recursion depth (root = 0). */
2905
- readonly depth: number;
2906
- /** Runtime recursion-depth ceiling — a spawn past it fails closed `depth-exceeded`. */
2907
- readonly maxDepth?: number;
2908
- /** Abort signal for this scope; an abort cascades into every live child's executor. */
2909
- readonly signal: AbortSignal;
2910
- /** Injected clock — keeps the journal `at` timestamp deterministic in tests. */
2911
- readonly now?: () => number;
2912
- /** Lifecycle stream sink. `spawn` emits `agent.spawn`, `next` emits `agent.child` — the
2913
- * SAME stream `runLoop`/`tool-loop` feed, so the recursive tree is ONE observable stream
2914
- * (the topology viewer reads it). Undefined ⇒ the journal stays the only record. */
2915
- readonly hooks?: RuntimeHooks;
2916
- }
2917
- /**
2918
- * The recursion seam key. A `Scope` seeds a value of this on each child's
2919
- * `ExecutorContext.seams` so a child whose executor is a DRIVER can mount a NESTED `Scope`
2920
- * over the SAME conserved pool at `depth+1`. A leaf executor never reads it. Single-sourced
2921
- * here so the scope and the driver-executor agree on the seam without a circular import.
2922
- */
2923
- declare const nestedScopeSeamKey = "nested-scope";
2924
- /**
2925
- * The recursion seam value: mount a nested `Scope` for a driver child. `parentId` is the
2926
- * driver child's own node id (so its children get `${nodeId}:s${ordinal}` ids and its
2927
- * nested journal tree is namespaced under it); `root` is the journal tree key for the
2928
- * nested tree (distinct from the parent's so cursor seqs never collide in the per-tree
2929
- * guard). `depth` is `parent.depth + 1`. The nested scope shares the parent's `pool`
2930
- * (conserved budget across depth), `journal`/`blobs` (one record), and `executors` (a
2931
- * nested child resolves to leaf-or-driver through the same open registry).
2932
- */
2933
- interface NestedScopeSeam {
2934
- /** This scope's recursion depth — a nested scope runs at `depth + 1`. */
2935
- readonly depth: number;
2936
- /** The runtime recursion-depth ceiling, paired with the conserved pool (R3). */
2937
- readonly maxDepth?: number;
2938
- /** The journal tree key the parent scope writes to (used to namespace nested trees). */
2939
- readonly journalRoot: NodeId;
2940
- /** Mount a nested scope rooted at `nestedRoot`, parented at this driver child's node id. */
2941
- mount(nestedRoot: NodeId, signal: AbortSignal): Scope<unknown>;
2942
- }
2943
- declare function createScope<Out>(args: ScopeArgs): Scope<Out>;
2944
- /**
2945
- * The step-8 merge-boundary adapter (M4): rehydrate a `Settled.done` into the kernel's
2946
- * `Iteration` shape so `defaultSelectWinner` stays single-sourced — the supervisor selects
2947
- * across settled children with the SAME argmax the loop kernel uses, not a forked copy.
2948
- *
2949
- * `index` is the cursor `seq` (the recorded, replay-stable order); `output`/`verdict`/
2950
- * `tokenUsage`/`costUsd` are read straight off the settlement (already rehydrated from the
2951
- * `outRef` blob by `next()`). Events are empty — a settled child is an opaque leaf result,
2952
- * not a sandbox event stream — and the timing/cost fields project its conserved `Spend`.
2953
- * Fail loud on a `down` settlement: only a `done` child is an iteration.
2954
- */
2955
- declare function settledToIteration<Out>(settled: Settled<Out>): Iteration<unknown, Out>;
2956
-
2957
- /**
2958
- * @experimental
2959
- *
2960
- * The `Supervisor` impl (KEYSTONE, build step 5).
2961
- *
2962
- * Owns the four things a free-running recursive `act` cannot own itself: the GLOBAL
2963
- * conserved budget pool, the event-sourced spawn log, the abort cascade over the whole
2964
- * live tree, and the OTP intensity breaker. `run` builds the root `Scope` over those,
2965
- * runs the root `Agent.act`, and returns a TYPED `SupervisedResult` — a no-winner is
2966
- * never coerced into a best-effort `Out`.
2967
- *
2968
- * Three lifecycle invariants this impl enforces by construction:
2969
- * - Join barrier: when `act()` settles (resolve OR reject), every still-live child is
2970
- * torn down before `run` returns — the generalization of the kernel's
2971
- * `finally{ Promise.allSettled(destroy) }` barrier (run-loop.ts) from boxes to the
2972
- * whole sub-tree. A teardown failure is `allSettled`'d and journaled as a
2973
- * `cancelled` event; it NEVER masks act()'s own outcome. act()'s rejection is the
2974
- * PRIMARY error (the kernel's firstError precedence), so a teardown throw during the
2975
- * barrier can never overwrite the real failure.
2976
- * - Abort cascade: a root abort (caller signal, `RootHandle.abort`, a tripped breaker,
2977
- * or pool exhaustion) aborts ONE internal controller whose signal is the root scope's
2978
- * signal. The scope cascades that into every live child's executor abort — which, for
2979
- * an `acquiring` child, chains into the `acquireSandbox` signal and reaps the
2980
- * find-by-name orphan box (M1). The supervisor never reaps children directly.
2981
- * - The supervisor NEVER re-enters a child (m3): the kernel/`acquireSandbox` already
2982
- * retried at the leaf, and a driver re-spawns through `scope.spawn`. The breaker only
2983
- * COUNTS `down` settlements within the intensity window and trips to a typed
2984
- * no-winner; it does not restart anything.
2985
- *
2986
- * Selection lives in the driver, not here (selector≠judge): `act` returns the synthesized
2987
- * winner `Out`. The supervisor content-addresses that `Out` for its replay `outRef`, reads
2988
- * `spentTotal` off the journal (`settled` child work + `metered` driver inference), and wraps
2989
- * it as a typed `winner` — it does not re-rank children behind the driver's back.
2990
- */
2991
-
2992
- declare function createSupervisor<Task, Out>(): Supervisor<Task, Out>;
2993
- /**
2994
- * Mint a `RootHandle` plus its supervisor-private control. The handle is the substrate a
2995
- * chat/pi-viz client attaches to (Q2): `view()` reads the live tree, `signal()` delivers
2996
- * an out-of-band message, `abort()` cascades. Before `run` binds it (and after `run`
2997
- * unbinds it) the handle is fail-loud: a client that talks to a handle that is not
2998
- * driving a live run gets a typed error, never a silent no-op.
2999
- */
3000
- declare function createRootHandle<Out>(): RootHandle<Out>;
3001
-
3002
- /**
3003
- * @experimental
3004
- *
3005
- * The SETTLE-time analyst: when a worker finishes, collect its tool spans from a `TraceSource` and run
3006
- * agent-eval's PUBLISHED batch analyzers over them — `buildTrajectory` (structured run summary),
3007
- * `stuckLoopView` (full-run repeated-call view, complementing the online consecutive detector), and
3008
- * `toolWasteView`. Substrate-agnostic: the spans come from any source (an owned loop's buffer OR a
3009
- * sandbox box session). No analysis reimplemented — this is the thin bridge into agent-eval's analyzers.
3010
- */
3011
-
3012
- interface TrajectoryAnalysis {
3013
- /** Structured run summary (tool-call count, step order). Steps carry a single timestamp, so per-span
3014
- * duration is 0; loop/waste detection keys on call PATTERNS + cross-span windows, not durations. */
3015
- readonly trajectory: Awaited<ReturnType<typeof buildTrajectory>>;
3016
- /** Full-run repeated-call view (total occurrences + window) — catches a loop the online consecutive
3017
- * detector interleaves past. */
3018
- readonly stuckLoop: Awaited<ReturnType<typeof stuckLoopView>>;
3019
- /** Wasted-vs-total tool-call ratio for the run. */
3020
- readonly toolWaste: Awaited<ReturnType<typeof toolWasteView>>;
3021
- }
3022
- /** Collect the source's spans and run the agent-eval batch analyzers over them under one `runId`. */
3023
- declare function analyzeTrace(source: TraceSource, runId?: string): Promise<TrajectoryAnalysis>;
3024
-
3025
- /**
3026
- * createVerifierEnvironment — ANY checkable task as an `Environment`, no tool surface
3027
- * required. The generalization piece: EOPS/commit0-style domains have tools that mutate
3028
- * an external artifact, but math problems, legal drafts, creative briefs, GTM copy, and
3029
- * QA tasks have a different shape — the artifact IS the worker's answer, and the domain
3030
- * is defined by one function: the deployable check over that answer.
3031
- *
3032
- * const gsm8k = createVerifierEnvironment({
3033
- * name: 'gsm8k',
3034
- * check: (task, answer) => ({
3035
- * passes: extractFinalNumber(answer) === task.meta?.answer ? 1 : 0,
3036
- * total: 1,
3037
- * errored: 0,
3038
- * }),
3039
- * })
3040
- * await runBenchmark({ environment: gsm8k, tasks, worker }) // sample vs refine on math
3041
- *
3042
- * The worker gets one built-in tool — `submit_answer` — plus any read-only domain tools
3043
- * the caller adds (a calculator, a retrieval call, a style guide lookup). Every
3044
- * submission is kept; `score()` checks the BEST submission (keep-best is the measured
3045
- * law: workers reach correct answers then revise past them). The refine strategy's
3046
- * critic reads the submission trajectory like any other trace, so iterate-with-feedback
3047
- * works unchanged on answer domains.
3048
- *
3049
- * The check can be graded (passes/total expresses partial credit — rubric points,
3050
- * sub-answers, unit-test counts), and MUST be deployable (computable without an oracle
3051
- * at serve time): exact/numeric match, schema validation, a compiled rubric — not a
3052
- * peek at held-out labels the production system wouldn't have.
3053
- */
3054
-
3055
- interface VerifierEnvironmentOptions {
3056
- name: string;
3057
- /** The deployable check over a submitted answer. Graded via passes/total. */
3058
- check(task: AgenticTask, answer: string): Promise<SurfaceScore> | SurfaceScore;
3059
- /** Extra domain tools (read-only helpers: calculator, retrieval, style lookup). */
3060
- extraTools?: AgenticTool[];
3061
- /** Executes the extra tools. Required when `extraTools` is set. */
3062
- callExtra?(task: AgenticTask, name: string, args: Record<string, unknown>): Promise<string> | string;
3063
- }
3064
- declare function createVerifierEnvironment(opts: VerifierEnvironmentOptions): Environment;
3065
-
3066
- /** Command runner seam. Host code can use `localShell`; sandbox code can wrap `box.exec`. */
3067
- type Shell = (args: ReadonlyArray<string>, cwd?: string) => Promise<{
3068
- stdout: string;
3069
- stderr: string;
3070
- code: number;
3071
- }>;
3072
- type WorkspaceCommit = {
3073
- readonly ok: true;
3074
- readonly rev: string;
3075
- } | {
3076
- readonly ok: false;
3077
- readonly conflict: string;
3078
- };
3079
- interface Workspace {
3080
- readonly ref: string;
3081
- materialize(dir: string): Promise<void>;
3082
- commit(dir: string, message: string): Promise<WorkspaceCommit>;
3083
- head(): Promise<string>;
3084
- }
3085
- declare function localShell(): Shell;
3086
- interface GitWorkspaceOptions {
3087
- readonly ref: string;
3088
- readonly shell?: Shell;
3089
- readonly branch?: string;
3090
- readonly noHooks?: boolean;
3091
- }
3092
- declare function gitWorkspace(opts: GitWorkspaceOptions): Workspace;
3093
- /** A jj-backed `Workspace` (Jujutsu, colocated with git for the durable remote).
3094
- * Same port, same `Shell` — a drop-in for `gitWorkspace`. jj suits agent loops:
3095
- * no staging area, and a first-class operation log (native resume/undo). Live use
3096
- * requires `jj` on the `Shell`'s host. */
3097
- declare function jjWorkspace(opts: GitWorkspaceOptions): Workspace;
3098
- interface WorkspaceRun<T> {
3099
- readonly valid: boolean;
3100
- readonly value: T;
3101
- /** Present when a commit was attempted (valid, or `commitOnInvalid`). */
3102
- readonly commit?: WorkspaceCommit;
3103
- }
3104
- /**
3105
- * Run a worker `body` inside a FRESH clone of a shared `Workspace`, then commit its work back
3106
- * so the next worker (or the supervisor) builds on it. This is the seam that turns isolated
3107
- * per-worker cwds into one compounding artifact — `body` gets a real materialized dir, its
3108
- * delivery is committed to the shared ref iff it's valid (a conflict is returned, never thrown).
3109
- * The clone is removed after; durable state lives only in the ref.
3110
- */
3111
- declare function runInWorkspace<T>(ws: Workspace, body: (cwd: string) => Promise<{
3112
- valid: boolean;
3113
- value: T;
3114
- message?: string;
3115
- }>, opts?: {
3116
- tmpPrefix?: string;
3117
- commitOnInvalid?: boolean;
3118
- }): Promise<WorkspaceRun<T>>;
3119
-
3120
- export { Agent, AgentRunSpec, AgentSpec, type AgenticOptions, type AgenticRunResult, type AgenticSurface, type AgenticTask, type AgenticTool, type AnytimeReport, type AnytimeStrategySummary, type AnytimeTaskCurve, type ArtifactHandle, AssertTraceDerivedFindings, type AuditIntentInput, type AuditIntentOptions, type AuthorStrategyOptions, type AuthoredProfile, type AuthoredStrategy, type BenchmarkCell, type BenchmarkConfig, type BenchmarkLift, type BenchmarkReport, type BenchmarkStrategySummary, type BenchmarkTaskRow, type BridgeSeam, Budget, type BudgetPool, type BudgetReadout, type ChampionPick, type ChampionPolicy, type CheckpointCapableBox, type CliSeam, type CliWorktreeSeam, CombinatorShape, type CompletionAnalyst, type CompletionEvidence, type CompletionPolicy, type CompletionVerdict, type CoordinationDriverOptions, type CoordinationMcpHandle, Corpus, CorpusFilter, CorpusRecord, type CreateScopeAnalystOptions, type CriuCapableClient, DefinePersonaInput, type Deliverable, DeliverableSpec, type DriverChat, type DriverMessage, type DriverToolCall, type DriverTurn, type Environment, EqualKArm, EqualKOnCostOptions, EqualKVerdict, type EvolutionArchiveNode, type EvolutionAuthor, type EvolutionBandInfo, type EvolutionCandidate, type EvolutionGeneration, type EvolutionReport, ExecCtx, type ExecutorConfig, ExecutorFactory, ExecutorRegistry, FanoutOptions, FanoutWinnerSelector, FileCorpus, FileResultBlobStore, FileSpawnJournal, type ForkCapableBox, type GitWorkspaceOptions, type HarvestCorpusOptions, type HarvestFailure, type HarvestReport, InMemoryCorpus, InMemoryResultBlobStore, type InMemoryRunContext, type InMemoryRunContextOptions, InMemorySpawnJournal, type Inbox, type InboxMessage, type IntentAudit, Iteration, type LoopDispatchOptions, type LoopOptionsForDispatch, LoopResult, LoopShape, LoopTokenUsage, LoopUntilSpec, type McpEndpoint, type McpEnvironmentOptions, type NestedScopeSeam, NodeId, type Observation, type ObserveInput, type ObserveOptions, type OpenSandboxRunOptions, Outcome, PanelSpec, Persona, PipelineStage, type PromotionGateOptions, type PromotionVerdict, type RegistryAnalyzeProjection, RenderCorpusToInstructionsOptions, type ReservationTicket, ResultBlobStore, RootHandle, RouterConfig, type RouterSeam, type RouterToolsSeam, type RunAgenticOptions, RunLoopOptions, RunPersonifiedOptions, type SandboxCapabilities, SandboxClient, type SandboxLineage, type SandboxLineageHandle, type SandboxRun, type SandboxSeam, Scope, ScopeAnalyst, ScopeAnalyzeInput, ScopeWidenGate, type SessionCapableBox, type SessionMessageLike, type SessionTraceBox, Settled, ShapeRegistry, type Shell, type ShotPersona, type ShotSpec, SpawnEvent, SpawnJournal, Spend, SteerContext, type Strategy, type StrategyCtx, type StrategyEvolutionConfig, type StrategyResult, SupervisedResult, Supervisor, type SurfaceScore, type ToolPartDecoder, ToolSpec, type ToolStepInput, type TraceSource, type TrajectoryAnalysis, TrajectoryReport, TrajectoryReportOptions, TreeView, type TurnResult, UsageEvent, type UsageSink, type VerifierEnvironmentOptions, VerifySpec, type WatchTraceOptions, type WaterfallCollector, type WaterfallReport, type WaterfallSpan, WidenSpec, WinnerStrategy, type Workspace, type WorkspaceCommit, type WorkspaceRun, acquireSandbox, adaptiveRefine, analyzeTrace, anytimeReport, asAuthoredProfile, assertStrategyContract, assertTraceDerivedFindings, auditIntent, authorStrategy, authoredWorker, breadthDriver, buildSteerContext, builtinShapes, cliWorktreeExecutor, completionAuthorizes, contentAddress, coordinationDriverAgent, createBudgetPool, createExecutor, createExecutorRegistry, createInMemoryRunContext, createInbox, createMcpEnvironment, createPartsTraceSource, createPushTraceSource, createRootHandle, createSandboxLineage, createScope, createScopeAnalyst, createShapeRegistry, createSupervisor, createVerifierEnvironment, createWaterfallCollector, decodeAnthropicPart, decodeOpenAiPart, decodeOpencodePart, decodeToolPart, defaultAnalystInstruction, defaultAuditorInstruction, defaultToolDetectors, definePersona, defineStrategy, depthDriver, deterministicCompletion, discriminatingMeans, driverChild, driverExecutorFactory, driverRuntime, equalKOnCost, extractLlmCallEvent, fanout, flatWidenGate, gitWorkspace, harvestCorpus, inlineSandboxClient, isDriverSpec, jjWorkspace, localShell, loopDispatch, loopUntil, mapSandboxEvent, materializeTreeView, nestedScopeSeamKey, observe, openSandboxRun, panel, pickChampion, pipeline, printBenchmarkReport, probeSandboxCapabilities, promotionGate, refine, registerShape, registryScopeAnalyst, renderAnytimeTable, renderCorpusToInstructions, renderReport, replaySpawnTree, reportLoopUsage, routerDriverChat, runAgentic, runBenchmark, runInWorkspace, runPersonified, runStrategyEvolution, sample, sampleThenRefine, sandboxSessionTraceSource, selectChampion, selectValidWinner, sentinelCompletion, serveCoordinationMcp, settledToIteration, spendFromUsageEvents, stopSentinel, strategyAuthorContract, supervisorSkill, toToolSpan, toolPartDecoders, trajectoryReport, verify, watchTrace, widen, withDriverExecutor };