@tangle-network/agent-runtime 0.89.0 → 0.90.1

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 (69) hide show
  1. package/README.md +51 -18
  2. package/dist/agent.d.ts +53 -2
  3. package/dist/agent.js +168 -6
  4. package/dist/agent.js.map +1 -1
  5. package/dist/analyst-loop.d.ts +1 -1
  6. package/dist/chunk-5QOB7H74.js +387 -0
  7. package/dist/chunk-5QOB7H74.js.map +1 -0
  8. package/dist/{chunk-4WXGK6GV.js → chunk-7ON74BQO.js} +2 -2
  9. package/dist/{chunk-7LO5GMAO.js → chunk-CMYMTRGA.js} +14 -5
  10. package/dist/chunk-CMYMTRGA.js.map +1 -0
  11. package/dist/{chunk-UD4BHQMI.js → chunk-IVGYLCFH.js} +2 -2
  12. package/dist/{chunk-UD4BHQMI.js.map → chunk-IVGYLCFH.js.map} +1 -1
  13. package/dist/{chunk-VNOOH22O.js → chunk-OOL3675H.js} +85 -4
  14. package/dist/chunk-OOL3675H.js.map +1 -0
  15. package/dist/{chunk-5AVV7KAH.js → chunk-QK4DV5PR.js} +2 -2
  16. package/dist/chunk-R2VAJGR3.js +214 -0
  17. package/dist/chunk-R2VAJGR3.js.map +1 -0
  18. package/dist/{chunk-N7EJV7N3.js → chunk-RYBVU4M3.js} +3592 -3562
  19. package/dist/chunk-RYBVU4M3.js.map +1 -0
  20. package/dist/{chunk-BQPFZE2C.js → chunk-WRUSWK4F.js} +5 -5
  21. package/dist/{chunk-PIPPLSOF.js → chunk-ZV4LXYCJ.js} +451 -24
  22. package/dist/chunk-ZV4LXYCJ.js.map +1 -0
  23. package/dist/{worktree-harness-CX_McRLp.d.ts → completion-gate-DkAnUmpb.d.ts} +45 -45
  24. package/dist/{coordination-CuDLO8wj.d.ts → coordination-rRj5hjJK.d.ts} +415 -227
  25. package/dist/environment-provider.d.ts +3 -3
  26. package/dist/index.d.ts +255 -16
  27. package/dist/index.js +463 -72
  28. package/dist/index.js.map +1 -1
  29. package/dist/intelligence.d.ts +228 -84
  30. package/dist/intelligence.js +259 -166
  31. package/dist/intelligence.js.map +1 -1
  32. package/dist/knowledge.d.ts +120 -0
  33. package/dist/knowledge.js +28 -0
  34. package/dist/knowledge.js.map +1 -0
  35. package/dist/lifecycle.d.ts +2 -2
  36. package/dist/lifecycle.js +2 -2
  37. package/dist/{local-harness-sI0S_XNA.d.ts → local-harness-dcD5WTTr.d.ts} +3 -0
  38. package/dist/{loop-runner-bin-hQDUL8Ld.d.ts → loop-runner-bin-DTbZVGfM.d.ts} +2 -2
  39. package/dist/loop-runner-bin.d.ts +6 -6
  40. package/dist/loop-runner-bin.js +7 -7
  41. package/dist/loops.d.ts +34 -369
  42. package/dist/loops.js +31 -5
  43. package/dist/mcp/bin.js +5 -5
  44. package/dist/mcp/index.d.ts +10 -11
  45. package/dist/mcp/index.js +10 -10
  46. package/dist/{mcp-serve-verifier-FL7-ZEb_.d.ts → mcp-serve-verifier-XsX8rkB9.d.ts} +12 -1
  47. package/dist/{openai-tools-BnrOmGjN.d.ts → openai-tools-C4ZfUD4L.d.ts} +1 -1
  48. package/dist/profiles.d.ts +196 -5
  49. package/dist/profiles.js +8 -0
  50. package/dist/profiles.js.map +1 -1
  51. package/dist/{router-client-r8y_VFVM.d.ts → router-client-DJImUDlm.d.ts} +1 -1
  52. package/dist/structural-rollout-MwlpgQ-6.d.ts +446 -0
  53. package/dist/supervise-DPmYPk0j.d.ts +131 -0
  54. package/dist/{types-ESeMOj94.d.ts → types-SyuwunY_.d.ts} +1 -1
  55. package/dist/{types-Driepl87.d.ts → types-eMNgWgFi.d.ts} +2 -2
  56. package/dist/{worktree-fanout-D6xR2CIA.d.ts → worktree-fanout-BDFQIO-Y.d.ts} +234 -234
  57. package/package.json +12 -4
  58. package/skills/agent-runtime-adoption/SKILL.md +74 -24
  59. package/skills/build-with-agent-runtime/SKILL.md +23 -20
  60. package/skills/loop-writer/SKILL.md +6 -5
  61. package/skills/supervise/SKILL.md +2 -2
  62. package/dist/chunk-7LO5GMAO.js.map +0 -1
  63. package/dist/chunk-N7EJV7N3.js.map +0 -1
  64. package/dist/chunk-PIPPLSOF.js.map +0 -1
  65. package/dist/chunk-VNOOH22O.js.map +0 -1
  66. package/dist/otel-export-BKmNwiCb.d.ts +0 -180
  67. /package/dist/{chunk-4WXGK6GV.js.map → chunk-7ON74BQO.js.map} +0 -0
  68. /package/dist/{chunk-5AVV7KAH.js.map → chunk-QK4DV5PR.js.map} +0 -0
  69. /package/dist/{chunk-BQPFZE2C.js.map → chunk-WRUSWK4F.js.map} +0 -0
@@ -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.
@@ -92,14 +92,14 @@ to its native default (`HARNESS_NATIVE_MODEL`) — never silently dropped.
92
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 |
93
93
  | **Sandbox coding rollout** (fresh box/round, or persistent+resume) | `runLoop(options)` / `openSandboxRun(client, opts, deliverable)` — `/loops` | canonical-api §3.1 |
94
94
  | **Optimize a CODE surface** in a gated loop | `improvementDriver({ worktree, generator })` — root `.` | canonical-api §3.4 |
95
- | **Optimize a PROMPT/config surface** (one call) | `selfImprove({ agent, scenarios, judge, baselineSurface })` `agent-eval/contract` | canonical-api §3.4 |
96
- | **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 |
97
97
  | **Gate: ship/hold from a `BenchmarkReport`** (per-task cells) | `promotionGate({ report, incumbent, candidate })` — `/loops` | canonical-api §3.4 |
98
98
  | **Run the full multi-generation flywheel + certify** | `runStrategyEvolution(config)` — `/loops` | canonical-api §3.4 |
99
99
  | **Observe a run** (cost/time waterfall, OTLP) | `createWaterfallCollector()` — `/loops`; `createOtelExporter` attached via `composeRuntimeHooks(...)` — root `.` | canonical-api §2 |
100
100
  | **State any A/B claim** | `pairedLift` (bench) over `pairedBootstrap`/`heldoutSignificance` (substrate) | canonical-api §3.5 |
101
- | **Observe/ship with billing-boundary** | `withTangleIntelligence(agent, { project, effort })` — `/intelligence` | canonical-api §2 |
102
- | **Pull the certified profile from the Intelligence plane** (pull-by-default delivery: fold the gate-certified prompt onto the base surface) | `pullCertified` / `withCertifiedDelivery` / `composeCertifiedPrompt` — `/intelligence` | `src/intelligence/delivery.ts` |
101
+ | **Observe + deliver on a live agent with billing-boundary** | `withIntelligence(agent, { project, target, effort })` — `/intelligence` | canonical-api §2 |
102
+ | **Pull the certified profile from the Intelligence plane** (pull-by-default delivery: fold the gate-certified prompt onto the base surface) | `pullCertified` / `withIntelligence` / `composeCertifiedPrompt` — `/intelligence` | `src/intelligence/delivery.ts` |
103
103
 
104
104
  ## Do-NOT-reinvent — the traps this skill exists to stop
105
105
 
@@ -110,7 +110,7 @@ holds the load-bearing invariant the parallel breaks:
110
110
  `loopUntil` + `runPersonified` (threads executor seams; equal-k; selector≠judge
111
111
  firewall; journal/replay — a parallel runner silently fails to wire the seams).
112
112
  - "skill optimizer" / "topology mutator" that opens branches + applies patches
113
- **≈** `improvementDriver` (code surface) or `selfImprove`/`gepaDriver` (prompt
113
+ **≈** `improvementDriver` (code surface) or `selfImprove`/`gepaProposer` (prompt
114
114
  surface) — both gated on a frozen holdout.
115
115
  - "profile-seam" / agent-config wrapper carrying model+prompt+tools+role **≈**
116
116
  `AgentProfile` (it IS that bundle) + `definePersona` (the run record);
@@ -177,26 +177,29 @@ sandbox coding rollouts against external benchmarks. The full when-which map is
177
177
 
178
178
  ## Observe / ship with the Intelligence SDK
179
179
 
180
- One line wraps any agent with trace + billing boundary:
181
- `withTangleIntelligence(agent, { project, effort })`, `effort ∈
180
+ One hook wraps any agent with send + receive + billing boundary:
181
+ `withIntelligence(agent, { project, target, effort })`, `effort ∈
182
182
  off|eco|standard|thorough|max` (`'off'` is the provable passthrough floor —
183
- intelligence spend clamped to 0). It builds on `createOtelExporter` +
184
- `loopEventToOtelSpan` don't hand-roll a trace-wrapper or effort/tier config.
185
- Verify the live subpath against `src/intelligence/index.ts`.
183
+ intelligence spend clamped to 0). It SENDs a typed `RunRecord` and RECEIVEs the
184
+ certified profile, building on `createOtelExporter` + `buildLoopOtelSpans`
185
+ don't hand-roll a trace-wrapper, a second receive path, or effort/tier config.
186
+ Verify the live subpath against `src/intelligence/with-intelligence.ts`.
186
187
 
187
188
  Two operational facts every consumer must know:
188
189
 
189
- - **Export is a silent no-op without an endpoint.** The export leg only ships
190
- when `INTELLIGENCE_OTLP_ENDPOINT` (or `OTEL_EXPORTER_OTLP_ENDPOINT`) is set
191
- e.g. `https://intelligence.tangle.tools/v1/otlp`; absent, spans are dropped
192
- best-effort with no error. The client's `doctor().exportConfigured` is the
193
- check that export will actually ship.
190
+ - **Send is a silent no-op without a tenant apiKey.** The export leg only ships
191
+ when an `apiKey` (or `TANGLE_API_KEY`) is present the ingest requires the
192
+ tenant Bearer. One `baseUrl` (`TANGLE_INTELLIGENCE_URL`, default
193
+ `https://intelligence.tangle.tools`) drives both the OTLP send (`/v1/otlp`)
194
+ and the receive pull. The client's `doctor().exportConfigured` is the check
195
+ that a send will actually land.
194
196
  - **Delivery pulls the certified profile from the plane.** `pullCertified` /
195
- `withCertifiedDelivery` hit
196
- `GET {TANGLE_INTELLIGENCE_URL|https://intelligence.tangle.tools}/v1/profiles/:target/composed`
197
- with `Bearer TANGLE_API_KEY`; `withCertifiedDelivery` folds the certified
198
- prompt onto the base surface, refreshes at most every 5 minutes, and is
199
- fail-closed a failed pull runs the agent on its base surface.
197
+ `withIntelligence` hit
198
+ `GET {baseUrl}/v1/profiles/:target/composed` with `Bearer TANGLE_API_KEY`;
199
+ the pull deserializes the typed `agentProfileDiffs` (surfaced as PROPOSALS,
200
+ never auto-applied) alongside the certified prompt. `withIntelligence` folds
201
+ the certified prompt onto the base surface, refreshes at most every 5 minutes,
202
+ and is fail-closed — a failed pull runs the agent on its base surface.
200
203
 
201
204
  ## Final check
202
205
 
@@ -30,7 +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
- | 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) |
34
35
 
35
36
  If a fixed combinator solves it, do not use a dynamic driver.
36
37
 
@@ -110,9 +111,9 @@ const result = await createSupervisor<Task, Output>().run(driver, task, supervis
110
111
  ```
111
112
 
112
113
  When the driver lives in a sandbox, expose the same verbs through
113
- `createCoordinationTools`: `spawn_worker`, `await_event`, `observe_worker`,
114
- `steer_worker`, `list_questions`, `answer_question`, `ask_parent`, `stop`, and
115
- optional analyst tools.
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`).
116
117
 
117
118
  ## Role Boundaries
118
119
 
@@ -133,7 +134,7 @@ with unresolved `blocks-run` questions.
133
134
  Steer sparingly: only when an analyst finds a concrete mistake, a loop is
134
135
  duplicating work, a parent/Pi answers a blocker, or a verifier reveals a specific
135
136
  fix a running worker can still use. Delivery is through `Scope.send` or
136
- `steer_worker`; failed delivery means spawn a fresh corrected attempt.
137
+ `steer_agent`; failed delivery means spawn a fresh corrected attempt.
137
138
 
138
139
  ## Workspace Loops
139
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.
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/mcp/local-harness.ts"],"sourcesContent":["/**\n *\n * Subprocess wrappers for the local coding-harness CLIs installed in the\n * sandbox image (claude-code, codex, opencode). Used by the in-process\n * delegation executor (`createInProcessExecutor`) so a delegated coding task\n * spawns a real harness on a real git worktree instead of provisioning a\n * sibling sandbox.\n *\n * All harness invocations:\n * - run with `cwd` set to the worktree\n * - inherit env from the parent (the MCP server inside the sandbox has\n * the harness's auth already)\n * - capture stdout/stderr\n * - support cancellation via AbortSignal\n * - enforce a wall-clock timeout\n *\n * @experimental\n */\n\nimport { type ChildProcess, spawn } from 'node:child_process'\nimport type { AgentProfile } from '@tangle-network/agent-interface'\n\n/** Local coding harness available inside the sandbox. */\nexport type LocalHarness = 'claude' | 'codex' | 'opencode'\n\n/**\n * Default per-harness command + arg shape. `buildArgs` takes ONLY the task prompt and\n * emits the prompt-only invocation (no model, no system prompt) — the historical shape\n * the in-process executor's `streamPrompt` drives. `modelArgs` maps a resolved model to\n * the harness's selector flag (every supported harness takes `-m <model>`). The §1.5\n * profile-aware mapper `harnessInvocation` composes these to thread the full\n * supervisor-authored profile (systemPrompt + model) into argv.\n */\nconst HARNESS_INVOCATIONS: Record<\n LocalHarness,\n {\n command: string\n buildArgs: (taskPrompt: string) => string[]\n /** Map a resolved model to the harness's model-selector flag. */\n modelArgs: (model: string) => string[]\n }\n> = {\n claude: {\n command: 'claude',\n buildArgs: (taskPrompt) => ['--headless', '-p', taskPrompt],\n modelArgs: (model) => ['-m', model],\n },\n codex: {\n command: 'codex',\n buildArgs: (taskPrompt) => ['run', taskPrompt],\n modelArgs: (model) => ['-m', model],\n },\n opencode: {\n command: 'opencode',\n buildArgs: (taskPrompt) => ['run', taskPrompt],\n modelArgs: (model) => ['-m', model],\n },\n}\n\n/** Result of mapping an `AgentProfile` + task prompt onto a harness invocation. */\nexport interface HarnessInvocation {\n command: string\n args: string[]\n}\n\n/**\n * Map a supervisor-authored `AgentProfile` + the per-task prompt onto a concrete harness\n * `command` + `args` (the §1.5 fix). UNLIKE the prompt-only `HARNESS_INVOCATIONS.buildArgs`\n * — which drops both the authored model and the system prompt — this threads the FULL\n * profile payload into argv:\n *\n * - `profile.prompt.systemPrompt` → the PROMPT channel: a portable, harness-agnostic\n * default that prepends the system prompt above the task prompt (`<system>\\n\\n<task>`),\n * so the authored standing instructions reach EVERY harness (none of the three CLIs\n * expose a portable replace-system-prompt flag for a one-shot non-interactive run).\n * - `profile.model.default` → the harness's `-m <model>` selector.\n *\n * The task prompt alone is the floor; an empty/absent profile yields exactly the legacy\n * `buildArgs(taskPrompt)` shape so existing callers are byte-identical.\n */\nexport function harnessInvocation(\n harness: LocalHarness,\n profile: AgentProfile,\n taskPrompt: string,\n): HarnessInvocation {\n const invocation = HARNESS_INVOCATIONS[harness]\n if (!invocation) {\n throw new Error(`harnessInvocation: unknown harness ${String(harness)}`)\n }\n\n const systemPrompt = profile.prompt?.systemPrompt\n const composedPrompt =\n typeof systemPrompt === 'string' && systemPrompt.trim().length > 0\n ? `${systemPrompt}\\n\\n${taskPrompt}`\n : taskPrompt\n\n const args = invocation.buildArgs(composedPrompt)\n\n const model = profile.model?.default\n if (typeof model === 'string' && model.length > 0) {\n args.push(...invocation.modelArgs(model))\n }\n\n return { command: invocation.command, args }\n}\n\n/** @experimental */\nexport interface RunLocalHarnessOptions {\n harness: LocalHarness\n /** Working directory for the subprocess (typically a worktree path). */\n cwd: string\n /** Prompt forwarded as the harness CLI's task argument. */\n taskPrompt: string\n /**\n * Pre-built command + args (e.g. from `harnessInvocation` so the full authored\n * `AgentProfile` — systemPrompt + model — reaches the harness). When set it OVERRIDES the\n * default prompt-only `buildArgs(taskPrompt)` path; `command` defaults to the harness's\n * default binary when only `args` is supplied. When absent the legacy prompt-only shape\n * is used unchanged.\n */\n invocation?: { command?: string; args: ReadonlyArray<string> }\n /** Wall-clock kill deadline (ms). Default 5 min. Subprocess SIGTERMed on expiry. */\n timeoutMs?: number\n /** Caller cancellation. SIGTERM is sent on abort. */\n signal?: AbortSignal\n /** Override env (defaults to inheriting from the parent). */\n env?: NodeJS.ProcessEnv\n /**\n * Test seam — inject a custom spawner so unit tests can mock the\n * subprocess without touching the OS. Defaults to node's `child_process.spawn`.\n */\n spawn?: (\n command: string,\n args: ReadonlyArray<string>,\n opts: {\n cwd: string\n env: NodeJS.ProcessEnv\n stdio: 'pipe'\n },\n ) => ChildProcess\n}\n\n/** @experimental */\nexport interface LocalHarnessResult {\n /** OS exit code. `null` when killed before exit. */\n exitCode: number | null\n /** Concatenated stdout. */\n stdout: string\n /** Concatenated stderr. */\n stderr: string\n /** Set when the process exited via signal (timeout / abort). */\n killedBySignal: NodeJS.Signals | null\n /** Wall-clock duration ms (spawn → exit). */\n durationMs: number\n /** Set when timeoutMs elapsed before exit. */\n timedOut: boolean\n}\n\nconst DEFAULT_TIMEOUT_MS = 5 * 60 * 1000\n\n/**\n * Spawn a local coding harness CLI as a subprocess + collect its output.\n *\n * NOT responsible for parsing the harness's output or extracting a diff —\n * the in-process executor's `streamPrompt` orchestrates `git diff` against\n * the worktree after this resolves. This function is intentionally narrow:\n * spawn, wait, capture, return.\n *\n * Fails loud — throws when:\n * - `cwd` doesn't exist (subprocess emits ENOENT; surfaced as Error)\n * - the harness binary is not on PATH (ENOENT)\n *\n * Does NOT throw when:\n * - the subprocess exits non-zero (`result.exitCode` carries the code)\n * - the subprocess is aborted / timed out (`result.killedBySignal` /\n * `result.timedOut` carries the reason)\n *\n * @experimental\n */\nexport function runLocalHarness(options: RunLocalHarnessOptions): Promise<LocalHarnessResult> {\n const { harness, cwd, taskPrompt } = options\n const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS\n const env = options.env ?? process.env\n const spawnImpl = options.spawn ?? spawn\n\n const invocation = HARNESS_INVOCATIONS[harness]\n if (!invocation) {\n return Promise.reject(new Error(`runLocalHarness: unknown harness ${String(harness)}`))\n }\n\n const startedAt = Date.now()\n const command = options.invocation?.command ?? invocation.command\n const args = options.invocation ? [...options.invocation.args] : invocation.buildArgs(taskPrompt)\n\n return new Promise<LocalHarnessResult>((resolve, reject) => {\n let child: ChildProcess\n try {\n child = spawnImpl(command, args, { cwd, env, stdio: 'pipe' })\n } catch (err) {\n reject(err instanceof Error ? err : new Error(String(err)))\n return\n }\n\n // The harness takes its task as an argv arg, not on stdin. Leaving stdin\n // OPEN makes a non-TTY `opencode run` (and likely the other harnesses)\n // BLOCK forever waiting on input — zero output, SIGTERM at the wall cap,\n // empty patch -> \"no candidate passed validation\". Close stdin so the\n // subprocess sees EOF and proceeds (the `cliExecutor` leaf does the same).\n child.stdin?.end()\n\n let stdout = ''\n let stderr = ''\n let timedOut = false\n let settled = false\n\n const timer =\n timeoutMs > 0\n ? setTimeout(() => {\n timedOut = true\n if (!child.killed) child.kill('SIGTERM')\n }, timeoutMs)\n : null\n if (timer && typeof (timer as { unref?: () => void }).unref === 'function') {\n ;(timer as { unref: () => void }).unref()\n }\n\n const onAbort = () => {\n if (!child.killed) child.kill('SIGTERM')\n }\n if (options.signal) {\n if (options.signal.aborted) onAbort()\n else options.signal.addEventListener('abort', onAbort, { once: true })\n }\n\n child.stdout?.on('data', (chunk) => {\n stdout += String(chunk)\n })\n child.stderr?.on('data', (chunk) => {\n stderr += String(chunk)\n })\n\n const finalize = (result: LocalHarnessResult) => {\n if (settled) return\n settled = true\n if (timer) clearTimeout(timer)\n options.signal?.removeEventListener('abort', onAbort)\n resolve(result)\n }\n\n child.on('error', (err) => {\n if (settled) return\n settled = true\n if (timer) clearTimeout(timer)\n options.signal?.removeEventListener('abort', onAbort)\n reject(err)\n })\n\n child.on('close', (code, signal) => {\n finalize({\n exitCode: code,\n stdout,\n stderr,\n killedBySignal: signal,\n durationMs: Date.now() - startedAt,\n timedOut,\n })\n })\n })\n}\n"],"mappings":"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","names":[]}