@tangle-network/agent-runtime 0.88.0 → 0.90.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 (56) hide show
  1. package/README.md +51 -18
  2. package/dist/agent.d.ts +53 -2
  3. package/dist/agent.js +166 -4
  4. package/dist/agent.js.map +1 -1
  5. package/dist/analyst-loop.d.ts +1 -1
  6. package/dist/{chunk-22HPUH77.js → chunk-4IBAMGBE.js} +170 -269
  7. package/dist/chunk-4IBAMGBE.js.map +1 -0
  8. package/dist/chunk-5QOB7H74.js +387 -0
  9. package/dist/chunk-5QOB7H74.js.map +1 -0
  10. package/dist/chunk-74SBMDTO.js +214 -0
  11. package/dist/chunk-74SBMDTO.js.map +1 -0
  12. package/dist/{chunk-HBE77SWV.js → chunk-MHK62APK.js} +5 -5
  13. package/dist/{chunk-LRNRPJAV.js → chunk-QSNSMJSZ.js} +2622 -2842
  14. package/dist/chunk-QSNSMJSZ.js.map +1 -0
  15. package/dist/{chunk-VNOOH22O.js → chunk-XMOU4WEY.js} +74 -3
  16. package/dist/chunk-XMOU4WEY.js.map +1 -0
  17. package/dist/{chunk-JHULWWQD.js → chunk-XQEISTK2.js} +2 -2
  18. package/dist/{worktree-harness-CX_McRLp.d.ts → completion-gate-BDaBP8dd.d.ts} +44 -44
  19. package/dist/{coordination-CuDLO8wj.d.ts → coordination-DxHduZg7.d.ts} +414 -226
  20. package/dist/environment-provider.d.ts +3 -3
  21. package/dist/index.d.ts +156 -13
  22. package/dist/index.js +316 -70
  23. package/dist/index.js.map +1 -1
  24. package/dist/intelligence.d.ts +2 -2
  25. package/dist/knowledge.d.ts +120 -0
  26. package/dist/knowledge.js +28 -0
  27. package/dist/knowledge.js.map +1 -0
  28. package/dist/lifecycle.js +1 -1
  29. package/dist/{loop-runner-bin-hQDUL8Ld.d.ts → loop-runner-bin-D3GmB2Ru.d.ts} +2 -2
  30. package/dist/loop-runner-bin.d.ts +5 -5
  31. package/dist/loop-runner-bin.js +5 -5
  32. package/dist/loops.d.ts +46 -324
  33. package/dist/loops.js +3 -15
  34. package/dist/mcp/bin.js +2 -2
  35. package/dist/mcp/index.d.ts +8 -9
  36. package/dist/mcp/index.js +7 -7
  37. package/dist/{openai-tools-BnrOmGjN.d.ts → openai-tools-D0xmzo0y.d.ts} +1 -1
  38. package/dist/profiles.d.ts +196 -5
  39. package/dist/profiles.js +8 -0
  40. package/dist/profiles.js.map +1 -1
  41. package/dist/{router-client-r8y_VFVM.d.ts → router-client-DJImUDlm.d.ts} +1 -1
  42. package/dist/supervise-BpCdssu0.d.ts +131 -0
  43. package/dist/{types-Driepl87.d.ts → types-DAJQRIUD.d.ts} +2 -2
  44. package/dist/{types-ESeMOj94.d.ts → types-Dnk189QA.d.ts} +1 -1
  45. package/dist/{worktree-fanout-D6xR2CIA.d.ts → worktree-fanout-CfRXYmgV.d.ts} +3 -3
  46. package/package.json +12 -4
  47. package/skills/agent-runtime-adoption/SKILL.md +74 -24
  48. package/skills/build-with-agent-runtime/SKILL.md +3 -9
  49. package/skills/loop-writer/SKILL.md +6 -78
  50. package/skills/supervise/SKILL.md +2 -2
  51. package/dist/chunk-22HPUH77.js.map +0 -1
  52. package/dist/chunk-LRNRPJAV.js.map +0 -1
  53. package/dist/chunk-VNOOH22O.js.map +0 -1
  54. package/dist/otel-export-BKmNwiCb.d.ts +0 -180
  55. /package/dist/{chunk-HBE77SWV.js.map → chunk-MHK62APK.js.map} +0 -0
  56. /package/dist/{chunk-JHULWWQD.js.map → chunk-XQEISTK2.js.map} +0 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tangle-network/agent-runtime",
3
- "version": "0.88.0",
3
+ "version": "0.90.0",
4
4
  "description": "Shared task-lifecycle skeleton for agents: a recursive loop kernel for chat turns, one-shot tasks, and multi-attempt loops, with trace capture and eval-gated self-improvement. Domain behavior lives in adapters; scoring and ship-gates in @tangle-network/agent-eval.",
5
5
  "homepage": "https://github.com/tangle-network/agent-runtime#readme",
6
6
  "repository": {
@@ -49,6 +49,11 @@
49
49
  "import": "./dist/lifecycle.js",
50
50
  "default": "./dist/lifecycle.js"
51
51
  },
52
+ "./knowledge": {
53
+ "types": "./dist/knowledge.d.ts",
54
+ "import": "./dist/knowledge.js",
55
+ "default": "./dist/knowledge.js"
56
+ },
52
57
  "./profiles": {
53
58
  "types": "./dist/profiles.d.ts",
54
59
  "import": "./dist/profiles.js",
@@ -94,9 +99,9 @@
94
99
  },
95
100
  "devDependencies": {
96
101
  "@biomejs/biome": "^2.4.15",
97
- "@tangle-network/agent-eval": "^0.103.2",
98
- "@tangle-network/agent-interface": ">=0.14.0 <1.0.0",
99
- "@tangle-network/sandbox": ">=0.8.0 <1.0.0",
102
+ "@tangle-network/agent-eval": "^0.108.1",
103
+ "@tangle-network/agent-interface": "^0.19.0",
104
+ "@tangle-network/sandbox": "^0.9.7",
100
105
  "@types/node": "^25.9.3",
101
106
  "playwright": "^1.61.0",
102
107
  "tsup": "^8.0.0",
@@ -138,5 +143,8 @@
138
143
  "playwright": {
139
144
  "optional": true
140
145
  }
146
+ },
147
+ "dependencies": {
148
+ "@tangle-network/agent-knowledge": "^1.11.1"
141
149
  }
142
150
  }
@@ -54,10 +54,12 @@ Topology is the **one recursive agent tree**: each round an agent decides to ref
54
54
  construction; the body is harness-re-verified, so an authored strategy can't
55
55
  fabricate a win. Use when the right shape is task-dependent (scout-then-fanout,
56
56
  refine-then-branch, decompose).
57
- - **`createCoordinationTools`** the agent-driving-agent loop: a driver agent
58
- spawns / steers / awaits child agents (and sub-drivers) through MCP verbs over a
59
- live `Scope`, recursively. Use when a driver should reason about and orchestrate
60
- its workers in natural language.
57
+ - **`createCoordinationTools`** (from `@tangle-network/agent-runtime/mcp`) the
58
+ agent-driving-agent loop: a driver agent spawns / steers / awaits child agents
59
+ (and sub-drivers) through MCP verbs over a live `Scope`, recursively. Use when a
60
+ driver should reason about and orchestrate its workers in natural language. From
61
+ `/loops` the equivalent surfaces are `serveCoordinationMcp` (the verbs as an HTTP
62
+ MCP over a live `Scope`) and the offline `driverAgent`.
61
63
 
62
64
  Topology is **orthogonal to harness** — a strategy decides the shape; the executor
63
65
  decides which harness (claude-code / codex / opencode / pi / router) runs each
@@ -100,6 +102,12 @@ agent-driving-agent loop), expose `createCoordinationTools` over a live `Scope`
100
102
 
101
103
  - `runLoop` validates `ctx.sandboxClient.create` exists or throws
102
104
  `ValidationError`. Never stub a `null` client.
105
+ - Build that client with `resolveSandboxClient({ backend })` (from
106
+ `@tangle-network/agent-runtime/loops`) — the one call that selects the sandbox /
107
+ bridge (cli-bridge) / router transport `runLoop` drives; do not hand-construct it.
108
+ Its sibling `resolveAgentBackend` is a DIFFERENT resolver — it resolves the CHAT
109
+ leg (`runChatThroughRuntime` / `runAgentTaskStream`) and returns an
110
+ `AgentExecutionBackend`, not a feeder for `resolveSandboxClient`.
103
111
  - The kernel emits `loop.started / iteration.dispatch / iteration.ended /
104
112
  decision / ended` via `ctx.traceEmitter`. Wire it to the same OTLP sink as the
105
113
  chat path so loop telemetry is queryable.
@@ -108,17 +116,19 @@ agent-driving-agent loop), expose `createCoordinationTools` over a live `Scope`
108
116
  - Dynamic driver: set the kernel's `runLoop` `maxIterations >=` the driver's so
109
117
  the driver's cap governs and the loop closes on a clean `'done'`.
110
118
 
111
- ## Campaign bridge — `loopDispatch`
119
+ ## Campaign bridge — `loopCampaignDispatch` / `loopDispatch`
112
120
 
113
121
  To run `runLoop` as an agent-eval campaign cell, do NOT hand-build the ExecCtx +
114
122
  forward trace + report usage every time (the third is silent — forgetting it
115
- yields a `{0,0}` cell `assertRealBackend` reads as a stub). Use the one bridge,
116
- `loopDispatch` (the old `loopCampaignDispatch` name was consolidated away; verify
117
- in `src/runtime/index.ts`):
123
+ yields a `{0,0}` cell `assertRealBackend` reads as a stub). Use the bridge. Both
124
+ are exported from `src/runtime/index.ts` and are distinct sibling adapters, NOT a
125
+ rename: `loopCampaignDispatch` returns a `DispatchFn` for plain `runCampaign` /
126
+ `runEvalCampaign`; `loopDispatch` returns a `ProfileDispatchFn` and is the
127
+ `runProfileMatrix` variant (it adds the profile axis).
118
128
 
119
129
  ```ts
120
- import { loopDispatch } from '@tangle-network/agent-runtime/loops'
121
- const dispatch = loopDispatch({
130
+ import { loopCampaignDispatch } from '@tangle-network/agent-runtime/loops'
131
+ const dispatch = loopCampaignDispatch({
122
132
  sandboxClient,
123
133
  toLoopOptions: (scenario, profile) => ({ driver, agentRun, output, validator, task: toTask(scenario) }),
124
134
  // toArtifact? — defaults to result.winner?.output
@@ -126,19 +136,59 @@ const dispatch = loopDispatch({
126
136
  // pass `dispatch` to runCampaign / runEvalCampaign; usage + trace are auto-forwarded
127
137
  ```
128
138
 
129
- `loopDispatch` doubles as the `runProfileMatrix` variant (the `profile` arg is an axis).
139
+ For the common shape — a fixed set of `cases` + a `prompt` builder + a `score`
140
+ fn, swept across profiles — prefer the declarative facade
141
+ `defineLeaderboard({ cases, prompt, score })` (from
142
+ `@tangle-network/agent-runtime/loops`). It composes
143
+ `expandProfileAxes × loopDispatch × naiveDriver` into one call, exposes
144
+ `.run(argv?)` (CLI-flag parsing + matrix) and `.toBenchmarkAdapter()`, and yields
145
+ a ranked leaderboard. Reach for raw `loopDispatch` only when a cell needs a custom
146
+ driver/validator.
130
147
 
131
- ## Identity-gated optimization — agent-eval's `selfImprove`
148
+ ## Identity-gated optimization — agent-runtime's `improve()` (facade over agent-eval's `selfImprove`)
132
149
 
133
- The optimization entry point is **`selfImprove`** (`@tangle-network/agent-eval/contract`),
134
- NOT agent-runtime agent-runtime contributes the code-surface `improvementDriver`
135
- (`/improvement`, the git-worktree path) you pass to it as `driver` to optimize CODE
136
- instead of a string. `selfImprove` optimizes any text/config surface (system /
137
- planner / judge rubric) and is **identity-gated by construction**: it runs evals,
138
- proposes candidates (default driver `gepaDriver`), and a held-out gate ships a winner
139
- only if it beats the baseline. `result.winner.surface` is the **baseline unless
140
- `result.gateDecision === 'ship'`** — so registering a surface for optimization can
141
- never regress it; it only improves when held-out data earns it.
150
+ **Start with `improve()`** the one pluggable RSI verb, exported at the
151
+ `@tangle-network/agent-runtime` package ROOT (its own header: "the ONE public,
152
+ surface-pluggable RSI verb. A thin facade over agent-eval's `selfImprove`"). Real
153
+ signature is 3-arg, NOT a single options object:
154
+
155
+ ```ts
156
+ improve<TScenario, TArtifact>(
157
+ profile: AgentProfile,
158
+ findings: unknown[],
159
+ opts: ImproveOptions,
160
+ ): Promise<ImproveResult>
161
+ ```
162
+
163
+ `opts`: `surface?: 'prompt'|'skills'|'tools'|'mcp'|'hooks'|'code'` (default
164
+ `'prompt'`), `scenarios`, `judge`, `agent`, `gate?: 'holdout'|'none'` (default
165
+ `'holdout'`; `'none'` forces `generations = 0`), plus `budget?` / `llm?` /
166
+ `generator?` / `code?` / `skills?` / `runDir?`. It picks the default proposer for
167
+ the surface (`gepaProposer` for `'prompt'`, `skillOptProposer` for `'skills'`;
168
+ `'code'`/`'tools'`/`'mcp'`/`'hooks'` throw `ConfigError` unless you pass
169
+ `opts.generator` or `opts.code`), extracts the baseline from the profile, runs
170
+ `selfImprove` with the held-out gate, and on a ship verdict writes the winner back
171
+ into the profile field. Returns `ImproveResult { profile, shipped, lift,
172
+ gateDecision, raw }` — deploy with `if (out.shipped) deploy(out.profile)`:
173
+
174
+ ```ts
175
+ import { improve } from '@tangle-network/agent-runtime'
176
+ const out = await improve(profile, findings, {
177
+ surface: 'prompt', scenarios, judge, agent, gate: 'holdout', llm,
178
+ })
179
+ if (out.shipped) deploy(out.profile)
180
+ ```
181
+
182
+ **Drop to `selfImprove`** (`@tangle-network/agent-eval/contract`) only when you
183
+ need finer control — a custom proposer/gate, or the code-surface git-worktree path
184
+ via agent-runtime's `improvementDriver` (`/improvement`), which you pass to it as
185
+ `proposer` to optimize CODE instead of a string. `selfImprove` optimizes any
186
+ text/config surface (system / planner / judge rubric) and is **identity-gated by
187
+ construction**: it runs evals, proposes candidates (default proposer
188
+ `gepaProposer`), and a held-out gate ships a winner only if it beats the baseline.
189
+ `result.winner.surface` is the **baseline unless `result.gateDecision === 'ship'`**
190
+ — so registering a surface for optimization can never regress it; it only improves
191
+ when held-out data earns it.
142
192
 
143
193
  ```ts
144
194
  import { selfImprove } from '@tangle-network/agent-eval/contract'
@@ -148,8 +198,8 @@ const result = await selfImprove({
148
198
  scenarios,
149
199
  judge,
150
200
  budget: { holdoutScenarios, generations: 3, populationSize: 2 },
151
- llm: { baseUrl, apiKey, model: REFLECTION_MODEL }, // drives the default gepaDriver
152
- // driver? — pass agent-runtime's improvementDriver to optimize CODE (worktree) instead of a string
201
+ llm: { baseUrl, apiKey, model: REFLECTION_MODEL }, // drives the default gepaProposer
202
+ // proposer? — pass agent-runtime's improvementDriver to optimize CODE (worktree) instead of a string
153
203
  // gate? — defaults to a held-out gate; pass defaultProductionGate for red-team hardening
154
204
  })
155
205
  // use result.winner.surface unconditionally: it's the baseline until a candidate genuinely wins
@@ -157,7 +207,7 @@ const result = await selfImprove({
157
207
 
158
208
  ### selfImprove gotchas — read before wiring
159
209
 
160
- - **`gepaDriver` mutates TEXT only**, and its only structural guard is `##` H2
210
+ - **`gepaProposer` mutates TEXT only**, and its only structural guard is `##` H2
161
211
  headings (`preserveSections`) + `maxSentenceEdits`. Make load-bearing sections
162
212
  of your prompt real `##` headings, and treat the output schema as fixed code —
163
213
  GEPA optimizes the prose, never the envelope/contract.
@@ -78,8 +78,6 @@ to its native default (`HARNESS_NATIVE_MODEL`) — never silently dropped.
78
78
  | **Produce-then-gate / multi-judge quorum / fixed chain** | `verify` / `panel` / `pipeline` — `/loops` | canonical-api §3.1 |
79
79
  | **Run depth-vs-breadth (or a custom strategy) over a stateful tool domain** | `runAgentic({ surface, task, mode\|strategy, budget })` — `/loops` | canonical-api §3.3 |
80
80
  | **Author a new topology/strategy compactly** | `defineStrategy(name, body)` w/ `ctx.shot()`+`ctx.critique()` — `/loops` | canonical-api §3.3 |
81
- | **Spawn a coded loop as a first-class atom** (bounded/gated/steerable, spawned + steered like a worker — the 3rd shape beside leaf worker + driver child) | `defineLoop(name, { maxRounds, round | agents, check })` + `loopChild`, wired via `createInMemoryRunContext({ withLoop: true })` — `/loops` — multi-agent = `agents: [proposer, verifier]` (declarative chain, not a bespoke `runTwoAgent…` fn); NOT a hand-driver looping in the model's head; the loop-executor owns maxRounds + conserved budget + gate + steer-between-rounds | canonical-api decision table |
82
- | **Have an LLM AUTHOR a loop at runtime** (codemode: the supervisor writes a `defineLoop` module for a novel goal) | `authorLoop({ chat, goal, maxRounds, outDir })` → a `LoopDef` for `loopChild` — `/loops` — NOT a per-product loop-code generator or a VM around authored code; the sibling of `authorStrategy`, safety is the conserved pool + gate + `assertStrategyContract` lint | `src/runtime/loop-author.ts` |
83
81
  | **Add a stateful tool-using domain** | implement `AgenticSurface` (5 hooks) — `/loops` | canonical-api §3.3 |
84
82
  | **Drive a team of agents over a graded `AgenticSurface` task** (workers settle on its check, driver self-improves from the failing tests) | `superviseSurface(profile, task, { surface, worker })` — `/loops` | canonical-api §2 |
85
83
  | **Benchmark: compare strategies + significance + Pareto on a domain** | `runBenchmark({ environment, tasks, worker, strategies })` — `/loops` | canonical-api §3.3 |
@@ -94,8 +92,8 @@ to its native default (`HARNESS_NATIVE_MODEL`) — never silently dropped.
94
92
  | **Spawn N coding agents on isolated git worktrees, keep the one whose patch passes checks** | `worktreeFanout` + `createWorktreeCliExecutor` + `gateOnDeliverable(DeliverableSpec)` over a raw `WorktreePatchArtifact`, winner via `selectValidWinner` — `/loops` — NOT a hand-rolled spawn-loop / "coder" role | canonical-api §3.1 / §5 |
95
93
  | **Sandbox coding rollout** (fresh box/round, or persistent+resume) | `runLoop(options)` / `openSandboxRun(client, opts, deliverable)` — `/loops` | canonical-api §3.1 |
96
94
  | **Optimize a CODE surface** in a gated loop | `improvementDriver({ worktree, generator })` — root `.` | canonical-api §3.4 |
97
- | **Optimize a PROMPT/config surface** (one call) | `selfImprove({ agent, scenarios, judge, baselineSurface })` `agent-eval/contract` | canonical-api §3.4 |
98
- | **Gate: ship/hold a candidate** (campaign ctx) | `defaultProductionGate` / `heldOutGate` / `composeGate` — `agent-eval/contract` | canonical-api §3.4 |
95
+ | **Optimize a PROMPT/config surface** (one call) — START HERE | `improve(profile, findings, { surface, gate })` — root `.` (the one pluggable RSI verb; picks the default proposer from `surface` — `gepaProposer` for prompt, `skillOptProposer` for skills — and wraps `selfImprove`; drop to `selfImprove({ agent, scenarios, judge, baselineSurface })` from `agent-eval/contract` only for the lower-level loop) | canonical-api §3.4 |
96
+ | **Gate: ship/hold a candidate** (campaign ctx) | `defaultProductionGate` / `heldOutGate` / `composeGate` — `agent-eval/contract`; `neutralizationGate` (footprint-matched PLACEBO gate — proves a held-out lift is CONTENT, not added prompt/mount footprint) — `agent-eval/campaign` | canonical-api §3.4 |
99
97
  | **Gate: ship/hold from a `BenchmarkReport`** (per-task cells) | `promotionGate({ report, incumbent, candidate })` — `/loops` | canonical-api §3.4 |
100
98
  | **Run the full multi-generation flywheel + certify** | `runStrategyEvolution(config)` — `/loops` | canonical-api §3.4 |
101
99
  | **Observe a run** (cost/time waterfall, OTLP) | `createWaterfallCollector()` — `/loops`; `createOtelExporter` attached via `composeRuntimeHooks(...)` — root `.` | canonical-api §2 |
@@ -112,7 +110,7 @@ holds the load-bearing invariant the parallel breaks:
112
110
  `loopUntil` + `runPersonified` (threads executor seams; equal-k; selector≠judge
113
111
  firewall; journal/replay — a parallel runner silently fails to wire the seams).
114
112
  - "skill optimizer" / "topology mutator" that opens branches + applies patches
115
- **≈** `improvementDriver` (code surface) or `selfImprove`/`gepaDriver` (prompt
113
+ **≈** `improvementDriver` (code surface) or `selfImprove`/`gepaProposer` (prompt
116
114
  surface) — both gated on a frozen holdout.
117
115
  - "profile-seam" / agent-config wrapper carrying model+prompt+tools+role **≈**
118
116
  `AgentProfile` (it IS that bundle) + `definePersona` (the run record);
@@ -150,10 +148,6 @@ holds the load-bearing invariant the parallel breaks:
150
148
  - a per-provider stream→event mapper for a single agent turn **≈**
151
149
  `streamAgentTurn` + `collectAgentTurn` (0.85+; one `RuntimeStreamEvent`
152
150
  contract over box / executor / chat, guaranteed terminal result+usage).
153
- - a supervisor `act` with a `for (round…)` loop that spawns + checks + continues
154
- in the model's reasoning **≈** `defineLoop` + `loopChild` (the loop-executor
155
- enforces the round ceiling, the conserved pool, the completion gate, and
156
- steer-between-rounds; a hand-driver loop enforces none of them).
157
151
 
158
152
  ## End-to-end recipe
159
153
 
@@ -30,8 +30,8 @@ The driver owns strategy.
30
30
  | Review from several lenses | `panel` |
31
31
  | Simulated user/product eval | `defineConversation` + `runConversation` |
32
32
  | Dynamic topology / drivers of drivers | `Scope` or sandbox driver + `createCoordinationTools` |
33
- | **A coded multi-round loop spawned + steered like a worker** | `defineLoop` + `loopChild` (the loop atom) |
34
- | Mutate a shared repo | git branch/clone loop with typed merge outcomes |
33
+ | Run N coding workers on isolated worktrees, gate each, pick best patch | `worktreeFanout` |
34
+ | Mutate a shared repo | git branch/clone loop with typed merge outcomes (`gitWorkspace` seam) |
35
35
 
36
36
  If a fixed combinator solves it, do not use a dynamic driver.
37
37
 
@@ -111,81 +111,9 @@ const result = await createSupervisor<Task, Output>().run(driver, task, supervis
111
111
  ```
112
112
 
113
113
  When the driver lives in a sandbox, expose the same verbs through
114
- `createCoordinationTools`: `spawn_worker`, `await_event`, `observe_worker`,
115
- `steer_worker`, `list_questions`, `answer_question`, `ask_parent`, `stop`, and
116
- optional analyst tools.
117
-
118
- ## The Loop Atom — a coded loop spawned like a worker
119
-
120
- When the loop itself is the reusable unit (a research loop, a verify loop, an
121
- evolve loop), make it a spawnable atom instead of a hand-driver. `defineLoop`
122
- authors the body; the runtime owns the round ceiling, the conserved budget, the
123
- gate, and steer-between-rounds. A supervisor spawns / observes / steers it with
124
- the SAME coordination verbs as a worker.
125
-
126
- Two ways to author the round. **`agents`** — a MULTI-AGENT loop as a declarative
127
- CHAIN (the common case): an ordered list of named agents piped each round,
128
- `task -> agents[0] -> agents[1] -> ... -> out`, each agent's return feeding the next
129
- as `prior`. "Two agents" is self-evident from the list — no bespoke `runTwoAgent...`
130
- function. **`round`** — freeform code for any other topology (fan-out, dynamic
131
- routing). Provide exactly one.
132
-
133
- ```ts
134
- // A two-agent research loop as a CHAIN: proposer drafts, verifier checks the draft.
135
- const research = defineLoop('research', {
136
- maxRounds: 3,
137
- agents: [
138
- { name: 'proposer', run: async ({ scope, steer }, _prior) => {
139
- const w = scope.spawn(researcher, { steer }, { budget: perRound, label: 'propose' })
140
- if (!w.ok) throw new Error(w.reason)
141
- return await scope.next() // the draft
142
- } },
143
- { name: 'verifier', run: async ({ scope }, draft) => {
144
- const w = scope.spawn(verifier, { draft }, { budget: perRound, label: 'verify' })
145
- if (!w.ok) throw new Error(w.reason)
146
- return await scope.next() // the verified result -> round out
147
- } },
148
- ],
149
- check: (out) => readinessPasses(out), // the deployable completion oracle
150
- })
151
-
152
- // Spawn it exactly like a worker (role:'loop' resolves to the loop-executor).
153
- const r = scope.spawn(loopChild(research, journal), task, { budget, label: 'research-loop' })
154
- // Wire once at the top: createInMemoryRunContext({ withDriver: true, withLoop: true }).
155
- ```
156
-
157
- TOPOLOGY: `agents` is a sequential CHAIN only. It is NOT parallel and NOT a graph —
158
- the loop's nested `scope.next()` is one shared queue, so parallel agents would steal
159
- each other's settlements. For fan-out (a panel of critics, best-of-N), dynamic
160
- routing, or any non-linear shape, use the freeform `round`: spawn every handle first,
161
- then drain N times, or branch on the data yourself. `fanout`/`panel`/`pipeline` cover
162
- the reactive-layer parallel case.
163
-
164
- Rules: the loop is code, not the model's judgment — that is what makes maxRounds,
165
- the budget, and the gate ENFORCED rather than hoped-for. Give the loop a real
166
- `check` (an executable oracle, never a self-judged score). Budget nests: the pool
167
- reserves each spawn's full ceiling until it settles, so pool > loop > per-round.
168
-
169
- ### Codemode — an LLM authors the loop at runtime
170
-
171
- When the supervisor should WRITE the loop for a novel goal (not pick a hand-built
172
- one), use `authorLoop` — the codemode seam over the atom, the sibling of
173
- `authorStrategy`. It shows the model `loopAuthorContract` (the exact `defineLoop`
174
- module shape, exported so a skill/GEPA pass can evolve it), extracts the fenced
175
- module, lints it with `assertStrategyContract` (only the loops import; no
176
- require/eval/fetch/process/node builtins), writes it, dynamic-imports it, and
177
- validates the default export is a `LoopDef` — ready for `loopChild`.
178
-
179
- ```ts
180
- const { loop } = await authorLoop({ chat, goal, maxRounds, outDir, fallbackModel })
181
- const spawned = scope.spawn(loopChild(loop, journal), task, { budget, label: 'authored-loop' })
182
- ```
183
-
184
- Safety is structural, same as `authorStrategy`: the authored body can be WRONG
185
- but cannot overspend (conserved pool), cannot skip the check (the runtime gates
186
- it), and cannot reach outside the loops surface (the lint). Do not build a
187
- per-product loop-code generator or a VM sandbox around authored loops — `authorLoop`
188
- is that seam.
114
+ `createCoordinationTools`: `spawn_agent`, `await_event`, `observe_agent`,
115
+ `steer_agent`, `list_questions`, `answer_question`, `ask_parent`, `stop`, and
116
+ optional analyst tools (`list_analysts`, `run_analyst`).
189
117
 
190
118
  ## Role Boundaries
191
119
 
@@ -206,7 +134,7 @@ with unresolved `blocks-run` questions.
206
134
  Steer sparingly: only when an analyst finds a concrete mistake, a loop is
207
135
  duplicating work, a parent/Pi answers a blocker, or a verifier reveals a specific
208
136
  fix a running worker can still use. Delivery is through `Scope.send` or
209
- `steer_worker`; failed delivery means spawn a fresh corrected attempt.
137
+ `steer_agent`; failed delivery means spawn a fresh corrected attempt.
210
138
 
211
139
  ## Workspace Loops
212
140
 
@@ -10,7 +10,7 @@ You are a supervisor. You do NOT do the work yourself — you design and drive s
10
10
  ## Loop
11
11
 
12
12
  1. **Decompose** the task into the smallest set of sub-tasks a single focused worker can each deliver.
13
- 2. **Author** a worker per sub-task by calling `spawn_worker` with a complete `profile`:
13
+ 2. **Author** a worker per sub-task by calling `spawn_agent` with a complete `profile`:
14
14
  - `name` — a short id.
15
15
  - `skills` — the skill files the worker should carry (by name), OR `systemPrompt` — rich, specific instructions for this sub-task.
16
16
  - `model` — the model best suited to this sub-task (optional).
@@ -21,4 +21,4 @@ You are a supervisor. You do NOT do the work yourself — you design and drive s
21
21
 
22
22
  ## Authoring sub-supervisors
23
23
 
24
- If a sub-task is itself too large for one worker, author it as a **sub-supervisor**: give its profile a `skills` list that includes `supervise`. It will decompose and drive its own workers one level deeper. This is not a special call — it is the same `spawn_worker`, just a profile that carries this skill.
24
+ If a sub-task is itself too large for one worker, author it as a **sub-supervisor**: give its profile a `skills` list that includes `supervise`. It will decompose and drive its own workers one level deeper. This is not a special call — it is the same `spawn_agent`, just a profile that carries this skill.