@tangle-network/agent-runtime 0.79.2 → 0.79.4

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 (48) hide show
  1. package/README.md +50 -327
  2. package/dist/agent.js +5 -5
  3. package/dist/analyst-loop.js +2 -2
  4. package/dist/{chunk-TSDKBFZP.js → chunk-44CX5JU6.js} +414 -113
  5. package/dist/chunk-44CX5JU6.js.map +1 -0
  6. package/dist/{chunk-T2HVQVB4.js → chunk-4J6RBI3K.js} +15 -1
  7. package/dist/chunk-4J6RBI3K.js.map +1 -0
  8. package/dist/{chunk-75V2XXYJ.js → chunk-AHZ3YBL6.js} +524 -12
  9. package/dist/chunk-AHZ3YBL6.js.map +1 -0
  10. package/dist/{chunk-IODKUOBA.js → chunk-C2FZ6GR6.js} +2 -2
  11. package/dist/{chunk-Z3RRRPRB.js → chunk-H7IBHAFT.js} +23 -14
  12. package/dist/chunk-H7IBHAFT.js.map +1 -0
  13. package/dist/{chunk-VMNEQHJR.js → chunk-IPEQ3ERC.js} +17 -2
  14. package/dist/chunk-IPEQ3ERC.js.map +1 -0
  15. package/dist/{chunk-PBE35ULD.js → chunk-NXZEVWKP.js} +2 -2
  16. package/dist/{chunk-SONQUREI.js → chunk-RUJZK6VH.js} +2 -2
  17. package/dist/{chunk-2DS6T46I.js → chunk-U6AP535M.js} +4 -4
  18. package/dist/{coordination-BoEPhGas.d.ts → coordination-BFVtgRax.d.ts} +30 -4
  19. package/dist/environment-provider.d.ts +1 -1
  20. package/dist/environment-provider.js +1 -1
  21. package/dist/index.d.ts +4 -4
  22. package/dist/index.js +8 -8
  23. package/dist/intelligence.d.ts +30 -2
  24. package/dist/intelligence.js +33 -23
  25. package/dist/intelligence.js.map +1 -1
  26. package/dist/{loop-runner-bin-DCr5OMe5.d.ts → loop-runner-bin-CTVja8e0.d.ts} +2 -2
  27. package/dist/loop-runner-bin.d.ts +4 -4
  28. package/dist/loop-runner-bin.js +7 -7
  29. package/dist/loops.d.ts +283 -13
  30. package/dist/loops.js +26 -6
  31. package/dist/mcp/bin.js +5 -5
  32. package/dist/mcp/index.d.ts +6 -6
  33. package/dist/mcp/index.js +7 -7
  34. package/dist/{router-client-CMAWGv1h.d.ts → router-client-Ak2IGuXq.d.ts} +33 -1
  35. package/dist/{types-C1sozrte.d.ts → types-DYW0tloU.d.ts} +2 -2
  36. package/dist/{worktree-fanout-CtQrRDME.d.ts → worktree-fanout-DaUDwCA_.d.ts} +5 -74
  37. package/dist/{worktree-CpptK3oF.d.ts → worktree-harness-Dt6s_m3z.d.ts} +74 -2
  38. package/package.json +1 -1
  39. package/skills/build-with-agent-runtime/SKILL.md +53 -23
  40. package/dist/chunk-75V2XXYJ.js.map +0 -1
  41. package/dist/chunk-T2HVQVB4.js.map +0 -1
  42. package/dist/chunk-TSDKBFZP.js.map +0 -1
  43. package/dist/chunk-VMNEQHJR.js.map +0 -1
  44. package/dist/chunk-Z3RRRPRB.js.map +0 -1
  45. /package/dist/{chunk-IODKUOBA.js.map → chunk-C2FZ6GR6.js.map} +0 -0
  46. /package/dist/{chunk-PBE35ULD.js.map → chunk-NXZEVWKP.js.map} +0 -0
  47. /package/dist/{chunk-SONQUREI.js.map → chunk-RUJZK6VH.js.map} +0 -0
  48. /package/dist/{chunk-2DS6T46I.js.map → chunk-U6AP535M.js.map} +0 -0
package/README.md CHANGED
@@ -1,371 +1,94 @@
1
1
  # @tangle-network/agent-runtime
2
2
 
3
- The shared task-lifecycle skeleton for agents. It runs an agent (a chat turn, a one-shot task, or a multi-attempt loop), captures every run as a trace, and feeds those traces into eval-gated self-improvement.
3
+ The engine Tangle's AI agents run on. It runs an agent a **chat turn**, a **one-shot task**, or a **team of agents** working toward a goal — records every run, and uses those records to **measure and improve** agents against real pass/fail checks.
4
4
 
5
- It owns the lifecycle, the loop kernel, and the **optimization suite** `Environment` + `Strategy` +
6
- `runBenchmark` + `runStrategyEvolution`, the published surface for measuring and evolving how an agent
7
- spends compute against a deployable check. It delegates domain behavior (models, tools, knowledge) to
8
- adapters, scoring statistics and the ship gate to [`@tangle-network/agent-eval`](https://www.npmjs.com/package/@tangle-network/agent-eval), and sandboxed long-running execution to [`@tangle-network/sandbox`](https://www.npmjs.com/package/@tangle-network/sandbox).
5
+ One loop, used three ways. Domain behavior (models, tools, knowledge) plugs in as adapters; the scoring statistics and the ship decision come from [`@tangle-network/agent-eval`](https://www.npmjs.com/package/@tangle-network/agent-eval); sandboxed execution from [`@tangle-network/sandbox`](https://www.npmjs.com/package/@tangle-network/sandbox).
9
6
 
10
7
  ```bash
11
8
  pnpm add @tangle-network/agent-runtime @tangle-network/agent-eval @tangle-network/sandbox
12
9
  ```
13
10
 
14
- ## The model
15
-
16
- One recursive `Agent` atom, run at two timescales, over many tasks. `docs/architecture.md` is the canonical spine. The short version:
17
-
18
- 1. **One atom.** `driver`, `worker`, `selector`, and `coordinator` are not separate types. They are what a single `Agent` returns from `act`. The recursion bottoms out at execution.
19
- 2. **Two timescales, one machinery.** The same loop runs at inference time (steer a worker over k attempts) and at optimization time (search the steer or the prompt with GEPA, gated on a held-out split).
20
- 3. **A benchmark is an adapter.** A new task is a loader plus a worker plus a judge. The loop, the drivers, the corpus, and the selector are the shared spine, written once.
21
- 4. **The selector is not the judge.** At inference time the selector picks which answer to return without seeing the judge's verdict. The judge is write-only. A steer may read the trace but never the verdict (the firewall that keeps the loop from gaming its own score).
22
-
23
- ## Getting started
24
-
25
- Three entry points, by what you're doing:
11
+ ## What you do with it
26
12
 
27
13
  | You want to… | Call |
28
14
  |---|---|
29
- | Run one **product chat turn** (gtm/legal/tax/creative run this in prod) | `handleChatTurn(...)` |
30
- | Have a **supervisor drive a team of agents** to a goal — any harness, any number of sandboxes | `supervise(profile, task, { budget, backend? })` |
31
- | **Self-improve** an agent, certified on a held-out gate | `improve(profile, findings, { surface, gate, … })` |
15
+ | Run a **chat turn** what every product agent does in production | `handleChatTurn(...)` |
16
+ | Have one agent **supervise a team of agents** toward a goal | `supervise(profile, task, opts)` |
17
+ | **Improve** an agent and prove the gain on fresh tasks | `improve(profile, findings, opts)` |
32
18
 
33
19
  ### Run a chat turn
34
20
 
35
- Every product agent is a `handleChatTurn` call inside a route what the gtm, creative, legal, and tax products run in production:
21
+ A product agent is one `handleChatTurn` call inside a route. You give it how to produce the response and how to persist it; it streams, traces, and persists.
36
22
 
37
23
  ```ts
38
24
  import { handleChatTurn } from '@tangle-network/agent-runtime'
39
25
 
40
- export async function POST({ request, env, ctx }: { request: Request; env: Env; ctx: ExecutionContext }) {
41
- const { workspaceId, threadId, userMessage } = await request.json()
42
- const box = await ensureWorkspaceSandbox(workspaceId)
43
-
44
- const result = handleChatTurn({
45
- identity: { tenantId: workspaceId, sessionId: threadId, userId: 'demo', turnIndex: 0 },
46
- hooks: {
47
- produce: () => ({
48
- stream: box.streamPrompt(userMessage),
49
- finalText: () => box.lastResponse(),
50
- }),
51
- persistAssistantMessage: async ({ identity, finalText }) => env.db.insertMessage(identity, finalText),
52
- traceFlush: () => env.traceSink.flush(),
53
- },
54
- waitUntil: ctx.waitUntil.bind(ctx),
55
- })
56
- return new Response(result.body, { headers: { 'content-type': result.contentType } })
57
- }
26
+ const result = handleChatTurn({
27
+ identity: { tenantId, sessionId: threadId, userId, turnIndex: 0 },
28
+ hooks: {
29
+ produce: () => ({ stream: box.streamPrompt(userMessage), finalText: () => box.lastResponse() }),
30
+ persistAssistantMessage: async ({ identity, finalText }) => db.insertMessage(identity, finalText),
31
+ },
32
+ waitUntil,
33
+ })
34
+ return new Response(result.body, { headers: { 'content-type': result.contentType } })
58
35
  ```
59
36
 
60
- That is the common case for a single product agent. The other two entry points are below.
37
+ ### Supervise a team of agents
61
38
 
62
- ### Run a supervisor (one call)
63
-
64
- A supervisor authors and drives a team of workers to a goal. The brain is resolved from the profile's `harness`: `null` → an in-process router tool-loop; `'claude-code'`/`'opencode'`/`'codex'` → a sandboxed coding harness driving the coordination verbs. The scaffolding (blobs / per-worker budget / journal / executors / depth) is defaulted.
39
+ One supervisor spawns and steers workers toward a goal. Where the workers run (an in-process loop, or a sandboxed coding harness) is one data value; the budget, journaling, and stopping are handled for you.
65
40
 
66
41
  ```ts
67
42
  import { supervise } from '@tangle-network/agent-runtime/loops'
68
43
 
69
44
  const result = await supervise(
70
45
  { name: 'supervisor', harness: null, systemPrompt: 'Delegate to workers; do not solve the task yourself.' },
71
- 'Implement the feature and make CI green.',
72
- { budget, router, backend }, // `backend` = where the workers run (one data value: router-tools | sandbox+harness | bridge)
46
+ 'Implement the feature and make the tests pass.',
47
+ { budget, router, backend }, // backend = where workers run: router-tools | sandbox+harness | bridge
73
48
  )
74
49
  ```
75
50
 
76
- See `examples/supervise/` for the full one-call entry; `examples/supervisor-loop/` for the per-backend seams.
77
-
78
- ### Self-improve an agent
51
+ ### Improve an agent
79
52
 
80
- `improve` is the one pluggable RSI verb: it optimizes a `surface` of the profile (prompt / skills / code) with the generator defaulted from the surface (GEPA for prompts, skillOpt for skills, or bring your own), certified on a frozen holdout.
53
+ `improve` optimizes one part of an agent (its prompt, skills, or code) and **only ships a change if it beats the current agent on tasks it never practiced on** so registering an agent for self-improvement can never make it worse.
81
54
 
82
55
  ```ts
83
56
  import { improve } from '@tangle-network/agent-runtime'
84
57
 
85
58
  const { profile, shipped, lift } = await improve(baseProfile, findings, {
86
- surface: 'prompt',
87
- gate: 'holdout', // certified on a held-out split, never the training set
88
- scenarios, judge, agent, // how to MEASURE the profile under a candidate surface
89
- })
90
- ```
91
-
92
- Everything below is the substrate these three sit on: multi-attempt loops, delegation, optimization, and the telemetry that makes them auditable.
93
-
94
- ### The system in plain language
95
-
96
- The internal docs use the project's own vocabulary; this is the same thing without it, for a colleague meeting the project cold. Five sentences:
97
-
98
- 1. We have tasks with **automatic pass/fail checks** — tests you can run, answer keys you can verify mechanically.
99
- 2. An AI attempts each task a fixed number of times under different **retry policies**: "try 3 times, keep the best", "try, get feedback, try again", and so on.
100
- 3. We compare policies **fairly**: identical tasks, identical attempt budgets, paired statistics, judged on fresh tasks no tuning step ever saw.
101
- 4. The distinctive part: the AI also **writes new retry policies itself**, as short programs, and they enter the same tournament under the same rules as human-written ones.
102
- 5. Every dollar and second is metered, so "better" can also mean "**equally good but cheaper**" — and that claim is statistically testable, not vibes.
103
-
104
- The load-bearing core is six pieces: task-with-check · retry policy · the tournament runner · the AI policy-writer · the statistical promotion gate · crash-resume. Everything else is a **fairness rule** or an **experiment on the menu** (a configuration, not a machine part).
105
-
106
- | Project term | Plain English | Standard concept |
107
- |---|---|---|
108
- | `Environment` | a task domain: open it, act with tools, check the result | RL environment / gym |
109
- | shot | one attempt | — |
110
- | steering / `refine` | feedback injected between attempts | self-refinement |
111
- | `authorStrategy` | the AI writes a new retry policy as a program | program synthesis |
112
- | evolution / generations | write candidates → tournament → keep the champion | evolutionary search |
113
- | harness-verified scoring | never trust a policy's self-reported score; recompute it from the attempts actually run | measurement hygiene |
114
- | selector ≠ judge (the firewall) | the feedback-giver never sees the answer key or the score | no reward leakage |
115
- | conserved budget pool | every policy gets exactly the same attempt budget; overspending is structurally impossible | compute-matched comparison |
116
- | holdout / fresh slice | final judging happens on tasks no tuning step ever touched | train/test split |
117
- | `promotionGate` | a seeded paired bootstrap must show the win is real before anything is "better" | inferential statistics |
118
- | non-inferiority mode | prove "not worse on quality AND significantly cheaper" | clinical-trials statistics |
119
- | reproducer certificate | a fresh AI re-builds the winner from a short description; a failed rebuild means the win was memorization, not method | description-length test |
120
- | waterfall | a per-step timeline of the run: seconds, dollars, tokens per step | distributed tracing |
121
-
122
- **Honest weaknesses:** mostly one domain family per claim so far (cross-domain replication is configuration, not new code); small holdouts (12–16 tasks) mean only effects ≳6pp are detectable; and the homegrown vocabulary is heavier than the machine it names — hence this section.
123
-
124
- ## Which entry point do I reach for?
125
-
126
- | You want to | Reach for | Subpath |
127
- |---|---|---|
128
- | Run a production chat turn (most products) | `handleChatTurn` | root |
129
- | Declare an agent (profile, surfaces, adapters) | `defineAgent` | `/agent` |
130
- | Run a one-shot task with verification and eval | `runAgentTask` | root |
131
- | Compare optimization strategies on YOUR domain (5 hooks) | `runBenchmark` + `defineStrategy` | `/loops` |
132
- | Let the system author + evolve its own strategies, gated | `runStrategyEvolution` · `authorStrategy` · `promotionGate` | `/loops` |
133
- | Run a multi-attempt loop with a custom driver | `runLoop` + an inline `Driver` | `/loops` |
134
- | Drive one agent profile from another (the canonical driver) | `createCoordinationTools` over `Supervisor` (`/loops`) | `/mcp` |
135
- | Delegate a disciplined loop by mode (code, research, ...) | `runDelegatedLoop` or `agent-runtime-loop` | root |
136
- | Build code reliably (reviewed, gated) | `createDefaultCoderDelegate` | `/mcp` |
137
- | Grow a knowledge base with only grounded facts | `createKbGate` | `/mcp` |
138
- | Improve a prompt safely (identity-gated) | `selfImprove` | `@tangle-network/agent-eval/contract` |
139
- | Ship loop traces to a GenAI viewer | `buildLoopOtelSpans` plus `createOtelExporter` | root |
140
- | Expose delegation as MCP tools to a sandbox agent | `createMcpServer` or `agent-runtime-mcp` | `/mcp` |
141
- | Persist a run plus its cost ledger | `startRuntimeRun` | root |
142
-
143
- ## The optimization suite
144
-
145
- The canonical surface. A domain is an `Environment` (five hooks: `open`/`tools`/`call`/`score`/`close`);
146
- a **strategy** is how a compute budget is spent to beat the domain's own deployable check. Two
147
- built-ins (`sample` = best-of-N, `refine` = critique-and-continue) plus `defineStrategy` to compose
148
- your own from two steps — and `authorStrategy`, where the system writes new strategies from its own
149
- per-task losses:
150
-
151
- ```ts
152
- import { defineStrategy, runBenchmark, sample, refine } from '@tangle-network/agent-runtime/loops'
153
-
154
- const doubleCheck = defineStrategy('double-check', async ({ shot, critique }) => {
155
- const first = await shot()
156
- const steer = first ? await critique(first.messages) : null
157
- const second = steer ? await shot({ messages: first?.messages, steer }) : null
158
- const score = Math.max(first?.score ?? 0, second?.score ?? 0)
159
- return { score, resolved: score >= 1, completions: 2, progression: [first?.score ?? 0, score], shots: 2 }
160
- })
161
-
162
- const report = await runBenchmark({ environment, tasks, worker, strategies: [sample, refine, doubleCheck], budget: 3 })
163
- report.perTask // the losses table an author/optimizer consumes
164
- report.pareto // the (score, $) frontier
165
- ```
166
-
167
- The measurement invariants are structural, not advisory: every strategy spends through a conserved
168
- budget pool (equal compute by construction), the deliverable score is **harness-verified** from the
169
- shots actually brokered (a body cannot fabricate a win), and the critic is firewalled from the check
170
- (selector ≠ judge). `runStrategyEvolution` runs the multi-generation search — populations of authored
171
- candidates, cost-aware champion selection, a phase ledger with resume, and ONE promotion decision via
172
- `promotionGate` (seeded paired bootstrap) on a holdout slice the search never touched.
173
- `createVerifierEnvironment` adapts answer-shaped domains (one `check` function); `createMcpEnvironment`
174
- adapts any MCP server. The consumer surface — loops as a service with a CLI, detached runner, and MCP
175
- server — lives in the [`loops`](https://github.com/drewstone/loops) repo; the experiment harness and
176
- evidence ledger live in [`bench/HARNESS.md`](./bench/HARNESS.md).
177
-
178
- ## The loop kernel
179
-
180
- `runLoop` is a topology-agnostic kernel. Each iteration spawns a sandbox on an `AgentRunSpec`, decodes the output, validates it, and asks a driver what to do next. The driver owns topology. The validator owns scoring. The kernel owns iteration accounting, concurrency, cost and token aggregation, and trace emission.
181
-
182
- ```ts
183
- import { runLoop, type Driver } from '@tangle-network/agent-runtime/loops'
184
-
185
- const driver: Driver<Task, Output, 'pick-winner' | 'fail'> = {
186
- plan: async (task, history) => (history.length === 0 ? [task, task] : []), // fan out, then stop
187
- decide: (history) => (history.some((i) => i.verdict?.valid) ? 'pick-winner' : 'fail'),
188
- }
189
-
190
- const result = await runLoop({
191
- driver, // the driver owns topology; the kernel owns accounting
192
- agentRuns: [claudeSpec, codexSpec, glmSpec], // heterogeneous: one harness per branch
193
- output, // events to typed Output
194
- validator, // Output to { valid, score }
195
- task,
196
- ctx: { sandboxClient: sandbox },
59
+ surface: 'prompt', // what to optimize: prompt | skills | code
60
+ gate: 'holdout', // certified on a held-back exam, never the practice set
61
+ scenarios, judge, agent, // how to measure a candidate
197
62
  })
198
- result.winner // highest-scoring valid attempt
199
- ```
200
-
201
- A `Driver` is `plan` (emit the round's `Task[]` — `[]` ends the loop) plus `decide` (the terminal
202
- `Decision` over the history). Topology is orthogonal to harness: the driver never names a backend,
203
- and the kernel's `agentRuns` decide which harness runs each branch. See `examples/coder-loop` for a
204
- fixed-shape inline `Driver`. For recursive work prefer the **agent-driver** — an `AgentProfile`
205
- driving another via `createCoordinationTools` (`/mcp`) over the budget-conserving `Scope`/`Supervisor`
206
- core (`/runtime`) — plus the `personify` combinators (`fanout`, `loopUntil`, `panel`, `pipeline`) and
207
- `runPersonified` on that same core.
208
-
209
- ## Self-improvement
210
-
211
- The same machinery, run at the optimization timescale.
212
-
213
- The one entry point is agent-eval's **`selfImprove`** (`@tangle-network/agent-eval/contract`). It runs a closed loop over any text/config surface, identity-gated by construction: it evaluates, proposes candidates (default `gepaDriver`), and a held-out gate ships a winner only if it beats the baseline. `result.winner.surface` is the baseline unless `result.gateDecision === 'ship'`, so registering a surface for optimization can never regress it.
214
-
215
- ```ts
216
- import { selfImprove } from '@tangle-network/agent-eval/contract'
217
-
218
- const result = await selfImprove({
219
- baselineSurface: CURRENT_SYSTEM_PROMPT,
220
- agent: (surface, scenario, ctx) => runYourThing(surface, scenario),
221
- scenarios,
222
- judge,
223
- budget: { holdoutScenarios, generations: 3 },
224
- llm: { baseUrl, apiKey, model: 'claude-sonnet-4-6' },
225
- })
226
- // result.winner.surface is the safe one — the baseline unless gateDecision === 'ship'
227
- ```
228
-
229
- agent-runtime contributes the runtime-specific pieces: the **CODE-surface `improvementDriver`**
230
- (`/improvement`) — a git-worktree mutator you pass to `selfImprove` as `driver` to optimize code
231
- instead of a string — and **`runStrategyEvolution`** (`/loops`), the multi-generation search over
232
- STRATEGY space: the system reads its own per-task losses, authors candidate strategies as code,
233
- plays them against the incumbent at equal budget, and a seeded statistical gate decides promotion
234
- on a never-touched holdout slice.
235
-
236
- `runAnalystLoop` (`/analyst-loop`) mines real run traces into findings; `createAnalystDriverHook` feeds those findings to a dynamic-driver planner via `PlannerContext.analyses`, with a firewall (`assertTraceDerivedFindings`) that rejects any finding derived from a judge verdict. Production intake — turning real run traces into the corpus `selfImprove` optimizes against — is agent-eval's `analyzeRuns` / `partitionRunsByAuthoringModel` (`/contract`).
237
-
238
- ## Delegated loops
239
-
240
- `runDelegatedLoop` is one entrypoint a worker agent or a scheduled routine calls to run a disciplined loop in a chosen mode, over the hardened engines below. It fails loud on an unwired mode; a thrown engine is captured as `{ ok: false }`, so unattended runs record rather than crash.
241
-
242
- ```ts
243
- import { runDelegatedLoop, worktreeLoopRunner, researchLoopRunner, type DelegatedLoopRegistry } from '@tangle-network/agent-runtime'
244
-
245
- const registry: DelegatedLoopRegistry = {
246
- code: worktreeLoopRunner({ repoRoot: '/repo', taskPrompt: 'fix the flaky retry test', harnesses, budget }),
247
- research: researchLoopRunner({ research, gate: { selfArtifactKinds: ['spec'] }, maxRounds: 3 }),
248
- }
249
- const result = await runDelegatedLoop('code', registry)
250
- ```
251
-
252
- Modes: `code`, `review`, `research`, `audit`, `self-improve`, `dynamic`. The `agent-runtime-loop` bin runs the registry from a cron or routine and exits 0 (ok), 1 (recorded failure), or 2 (usage or config error).
253
-
254
- `worktreeLoopRunner` (`code` mode, the generic recursive path) authors one `AgentProfile` per harness and runs them as a `worktreeFanout` (each leaf `gateOnDeliverable`), winner by the shared valid-only selector. The sandbox-session counterpart is `detachedSessionDelegate` (`/mcp`): it drives the in-box harness over a `SandboxClient` to a mechanically-validated patch, with default-on safety gates — no-op rejection, an always-on secret-path floor (`.env`, keys, wallets), an optional `reviewer` gate, and a `winnerSelection` policy. Its worker profile is a parameter the caller authors (`workerProfile`); omit it for a minimal model-only default.
255
-
256
- The knowledge-base gate (`createKbGate`, `/mcp`) is fail-closed: a fact's `verbatimPassage` must appear in its `sourceText`, the asserted value must be in the passage, and citations cannot point at self-generated artifacts. `researchLoopRunner` wraps it with a correct-on-veto loop that re-researches the vetoed gaps up to `maxRounds`, then returns the unverified ones rather than dropping them.
257
-
258
- ## Tracing
259
-
260
- `runLoop` emits a structured event stream. `buildLoopOtelSpans` turns it into a nested, real-duration span tree that any GenAI trace viewer (Phoenix, Langfuse, Grafana Tempo, Tangle Intelligence) renders natively. Attributes follow the current GenAI semantic conventions (`gen_ai.operation.name`, `gen_ai.agent.name`, `gen_ai.usage.input_tokens`, `gen_ai.usage.output_tokens`) plus a `tangle.loop.*` extension for the topology (move kind and rationale, edge lineage, verdict, placement, cost).
261
-
262
- ```ts
263
- import { buildLoopOtelSpans, createOtelExporter } from '@tangle-network/agent-runtime'
264
-
265
- const exporter = createOtelExporter() // reads OTEL_EXPORTER_OTLP_ENDPOINT
266
- for (const span of buildLoopOtelSpans(loopEvents, traceId)) exporter?.exportSpan(span)
267
- await exporter?.flush()
268
- ```
269
-
270
- The shape: `loop` to `loop.round` (move plus rationale) to `loop.iteration` (agent, usage, verdict, cost, parent edge).
271
-
272
- ## MCP delegation server
273
-
274
- Expose the delegation tools to a sandbox coding agent: the generic `delegate` verb (one intent → a supervisor that authors + drives its own worker, returns the delivered output with its real spend) plus the queue-bound `delegate_feedback`, `delegation_status`, `delegation_history` (and `delegate_ui_audit` when a UI-audit runner is wired). Mount the canonical server instead of forking delegation logic.
275
-
276
- ```ts
277
- import { createMcpServer } from '@tangle-network/agent-runtime/mcp'
278
-
279
- const server = createMcpServer({ delegateSupervisor: { router, backend, deliverable } })
280
- ```
281
-
282
- Or mount the `agent-runtime-mcp` stdio bin on a production `AgentProfile.mcp` with `MCP_ENABLE_DELEGATE=1`.
283
-
284
- Delegation state is in-memory by default — a server restart drops pending delegations and history. Set `AGENT_RUNTIME_DELEGATION_STATE_FILE=/path/state.json` on the bin (or construct via `DelegationTaskQueue.restore({ store: new FileDelegationStore({ filePath }) })`) to persist records across restarts: `delegation_status`/`delegation_history` keep answering for prior runs, idempotency keys dedupe resubmissions, and in-flight records either resume through the `resumeDelegate` seam (when submitted with a `detachedSessionRef`) or settle as failed with an explicit driver-restart error. A corrupt state file refuses to load (`DelegationStateCorruptError`); `AGENT_RUNTIME_DELEGATION_STATE_RECOVER=1` archives it and starts empty. `AGENT_RUNTIME_DELEGATION_RETAIN_TERMINAL=<n>` caps retained terminal records.
285
-
286
- ## The experiment harness (bench/)
287
-
288
- `bench/` is the internal harness; [`bench/HARNESS.md`](./bench/HARNESS.md) is its map — read that
289
- first. The canonical path is the optimization suite (`runBenchmark`/`runStrategyEvolution` over real
290
- domains: the EnterpriseOps gym, commit0, answer-shaped math). The live evidence ledger is
291
- `.evolve/current.json` — results never live in this README.
292
-
293
- The recursive diverse-vs-blind gate runs through the keystone: `gate-cli.mts` →
294
- `runGate` composes a `Persona` + the generic `fanout` combinator over the budget-conserving
295
- `Supervisor`, with each child solved via the router and graded by the benchmark's own deployable
296
- `adapter.judge` (selector ≠ oracle). Each rollout is written to a durable canonical corpus; the deep
297
- statistics (paired bootstrap with Benjamini-Hochberg correction, selector replay) come from
298
- `corpus-report.mts` and `corpus-replay.mts` over that corpus, computed once and offline. See
299
- `bench/HARNESS.md` and `docs/learning-flywheel.md`.
300
-
301
- ## Defaults
302
-
303
- | Knob | Default | Override |
304
- |---|---|---|
305
- | Backend model | `gpt-4o-mini` (via `createOpenAICompatibleBackend`) | `model` option or `MODEL_NAME` env |
306
- | Backend provider | `openai-compat` when `TANGLE_API_KEY`, else `openai` if `OPENAI_API_KEY` | `MODEL_PROVIDER` env |
307
- | Router base URL | `https://router.tangle.tools/v1` | `TANGLE_ROUTER_BASE_URL` env |
308
- | Sandbox base URL | `https://sandbox.tangle.tools` | `SANDBOX_API_URL` env |
309
- | Loop iteration cap | 10 (`runLoop`) | `runLoop({ maxIterations })` |
310
- | Driver | none, required by `runLoop` | an inline `Driver` (`plan`/`decide`) |
311
- | Strategy budget (suite) | 3 rollouts/shots per strategy per task | `runBenchmark({ budget })` |
312
- | Winner selection (coder delegate) | `highest-score` | `winnerSelection` option |
313
- | KB gate min passage | 12 chars | `createKbGate({ minPassageChars })` |
314
- | `selfImprove` gate | held-out gate (default) | pass `gate: defaultProductionGate` for red-team hardening |
315
- | OTEL export | off | set `OTEL_EXPORTER_OTLP_ENDPOINT` |
316
- | Loop-runner mode failure | recorded as `{ ok: false }` | `runDelegatedLoop` never crashes on a thrown engine |
317
-
318
- ## Composition with the stack
319
-
320
63
  ```
321
- agent-runtime handleChatTurn, runLoop + drivers, runProgram, runDelegatedLoop, createMcpServer,
322
- improvementDriver, createKbGate, buildLoopOtelSpans, defineAgent
323
64
 
324
- agent-eval selfImprove (the optimization entry point), runEvalCampaign,
325
- runImprovementLoop (gepaDriver), heldOutGate, runAgentMatrix, analyzeRuns.
326
- Consumes runtime traces, scores, gates promotion. agent-runtime depends on it,
327
- never the reverse.
65
+ ## How it works (the short version)
328
66
 
329
- agent-knowledge proposeKnowledgeWrites, applyKnowledgeWriteBlocks. The analyst loop produces
330
- these; the runtime and createKbGate consume them.
67
+ - **One agent, run two ways.** The same agent runs at "do the task" speed and at "get better at the task" speed. "Driver", "worker", and "coordinator" aren't separate types — they're roles one agent plays.
68
+ - **Everything is measured.** Every run is a trace: tokens, dollars, time, and a pass/fail score from a real check. "Better" is a number with a denominator, not a vibe — and "equally good but cheaper" is a result you can prove.
69
+ - **Improvement is gated.** A change ships only after it beats the current agent on fresh tasks no tuning step ever saw, with a statistical test — not a single lucky run.
70
+ - **The grader is honest.** Whatever gives feedback never sees the answer key, and scores are recomputed from the attempts actually run — an agent can't fabricate its own win.
331
71
 
332
- sandbox AgentProfile, Sandbox.create, streamPrompt, exportTraceBundle. The harness
333
- execution surface every loop runs on.
334
- ```
335
-
336
- ## Subpath exports
72
+ ## Examples
337
73
 
338
- Six subpaths — the public surface:
74
+ Runnable, grouped by what they show copy the one nearest your task:
339
75
 
340
- | Import | Owns |
76
+ | Do this | Example |
341
77
  |---|---|
342
- | `@tangle-network/agent-runtime` | chat turns, delegated loop-runner, OTEL export, errors, model resolution |
343
- | `.../agent` | `defineAgent` plus surface and outcome adapters |
344
- | `.../loops` | **the optimization suite** (`Environment`, `defineStrategy`, `runBenchmark`, `runStrategyEvolution`, `authorStrategy`, `promotionGate`) + the recursive atom (`Supervisor`/`Scope`, `createExecutor`), the `runLoop` kernel, the `Driver` type, `loopDispatch` |
345
- | `.../profiles` | `coderTaskToPrompt` (the coder task formatter), the `uiAuditorProfile` presets + the UI-audit workspace I/O helpers |
346
- | `.../intelligence` | `withTangleIntelligence`, `createIntelligenceClient`Observe + the provable-OFF billing boundary |
347
- | `.../mcp` | `createMcpServer`, `createDefaultCoderDelegate`, `createKbGate`, the `agent-runtime-mcp` bin |
348
-
349
- Bins: `agent-runtime-mcp` (delegation MCP server), `agent-runtime-loop` (schedulable delegated loop-runner).
350
-
351
- ## Teaching an agent to build on this
352
-
353
- Two agent-consumable skills live in the [`loops`](https://github.com/drewstone/loops) repo:
354
- **`skills/loop-builder`** (domain → `Environment` → loop → gate → operator surface, with the
355
- measured foot-gun list) and **`skills/loop-author`** (authoring a strategy body from losses;
356
- read the contract with `loops contract`). The runnable on-ramp is [`examples/`](./examples/README.md)
357
- a learning progression from the production chat turn through the strategy suite to the recursive
358
- supervisor. For the broader pipeline (trace sink, analyst loop, scorecard, CI), see the
359
- `agent-eval-adoption` and `agent-stack-adoption` skills.
360
-
361
- ## Stability, tests, docs
362
-
363
- Every public export is annotated `@stable` or `@experimental`. `@stable` exports do not change shape inside a minor version; `@experimental` ones may, and require a deliberate consumer bump.
364
-
365
- ```bash
366
- pnpm test # kernel, drivers, MCP, delegate hardening, kb-gate, loop-runner, backends
367
- pnpm typecheck
368
- pnpm build
369
- ```
370
-
371
- Deeper docs: [`docs/architecture.md`](./docs/architecture.md) (the canonical spine), [`docs/canonical-api.md`](./docs/canonical-api.md) (the anti-reinvention decision table), [`docs/learning-flywheel.md`](./docs/learning-flywheel.md) (the self-improvement thesis and the open gate), [`docs/concepts.md`](./docs/concepts.md) (mental model), [`docs/agent-bus-protocol.md`](./docs/agent-bus-protocol.md) (cross-gateway header contract), [`docs/durability-adapters.md`](./docs/durability-adapters.md) (SQL-backed `ConversationJournal`).
78
+ | Run a product chat turn | [`chat-handler`](./examples/chat-handler) |
79
+ | Drive a team of agents to a goal | [`supervise`](./examples/supervise) · [`recursive-supervisor`](./examples/recursive-supervisor) |
80
+ | Benchmark strategies on your own domain | [`coding-benchmark`](./examples/coding-benchmark) |
81
+ | Benchmark **harnesses × models** over a real task suite (the real WebCode dataset) | [`webcode-matrix`](./examples/webcode-matrix) |
82
+ | Render a **multi-profile leaderboard**ranked board + score matrix + SVG/HTML charts, any domain | `leaderboard(records)` → `renderLeaderboardMarkdown` / `Svg` / `Html` |
83
+ | Trace + bill + effort-gate the WebCode benchmark (the Intelligence SDK) | [`intelligence-webcode`](./examples/intelligence-webcode) |
84
+ | Self-improve an agent, gated on a held-out set | [`improve`](./examples/improve) · [`self-improving-coder`](./examples/self-improving-coder) |
85
+ | Study coordination vs raw compute | [`ablation-suite`](./examples/ablation-suite) |
86
+
87
+ All 28 live in [`examples/`](./examples).
88
+
89
+ ## Where to go next
90
+
91
+ - New here? [`docs/concepts.md`](./docs/concepts.md) the mental model in plain terms.
92
+ - [`docs/canonical-api.md`](./docs/canonical-api.md) — find the primitive: "I want to ___ use ___".
93
+ - [`docs/architecture.md`](./docs/architecture.md) the design, end to end.
94
+ - [`bench/HARNESS.md`](./bench/HARNESS.md) the experiment harness and how to run a benchmark.
package/dist/agent.js CHANGED
@@ -1,12 +1,12 @@
1
- import "./chunk-75V2XXYJ.js";
1
+ import "./chunk-AHZ3YBL6.js";
2
2
  import {
3
3
  createSandboxForSpec
4
- } from "./chunk-TSDKBFZP.js";
5
- import "./chunk-VMNEQHJR.js";
6
- import "./chunk-Z3RRRPRB.js";
4
+ } from "./chunk-44CX5JU6.js";
5
+ import "./chunk-IPEQ3ERC.js";
6
+ import "./chunk-H7IBHAFT.js";
7
7
  import {
8
8
  mapSandboxEvent
9
- } from "./chunk-T2HVQVB4.js";
9
+ } from "./chunk-4J6RBI3K.js";
10
10
  import "./chunk-5JAUQZQA.js";
11
11
  import "./chunk-NBV35BR6.js";
12
12
  import "./chunk-WIR4HOOJ.js";
@@ -1,8 +1,8 @@
1
1
  import {
2
2
  iterationsToTraceStore,
3
3
  runAnalystLoop
4
- } from "./chunk-IODKUOBA.js";
5
- import "./chunk-T2HVQVB4.js";
4
+ } from "./chunk-C2FZ6GR6.js";
5
+ import "./chunk-4J6RBI3K.js";
6
6
  import "./chunk-NBV35BR6.js";
7
7
  import "./chunk-DGUM43GV.js";
8
8
  export {