@tangle-network/agent-runtime 0.44.0 → 0.45.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 (89) hide show
  1. package/README.md +95 -203
  2. package/dist/agent.d.ts +5 -4
  3. package/dist/agent.js +5 -7
  4. package/dist/agent.js.map +1 -1
  5. package/dist/analyst-loop.d.ts +65 -4
  6. package/dist/analyst-loop.js +6 -1
  7. package/dist/audit.d.ts +93 -0
  8. package/dist/audit.js +312 -0
  9. package/dist/audit.js.map +1 -0
  10. package/dist/chunk-4B6U4CVQ.js +15 -0
  11. package/dist/chunk-4B6U4CVQ.js.map +1 -0
  12. package/dist/chunk-FK53TXOP.js +603 -0
  13. package/dist/chunk-FK53TXOP.js.map +1 -0
  14. package/dist/{chunk-SKUZZCHE.js → chunk-IJ6FGOPO.js} +5 -5
  15. package/dist/chunk-IJ6FGOPO.js.map +1 -0
  16. package/dist/{chunk-HVYOHJHK.js → chunk-IJGS6J7X.js} +2 -2
  17. package/dist/chunk-IJGS6J7X.js.map +1 -0
  18. package/dist/chunk-KEWO4KI6.js +3599 -0
  19. package/dist/chunk-KEWO4KI6.js.map +1 -0
  20. package/dist/{chunk-NRZOXCJK.js → chunk-KSMX62JF.js} +2 -2
  21. package/dist/{chunk-GFKVVRQ7.js → chunk-NYN5RTLP.js} +11 -10
  22. package/dist/chunk-NYN5RTLP.js.map +1 -0
  23. package/dist/chunk-PRX45WE2.js +264 -0
  24. package/dist/chunk-PRX45WE2.js.map +1 -0
  25. package/dist/{chunk-3HMHSN22.js → chunk-QR4UUC5P.js} +6 -6
  26. package/dist/chunk-QR4UUC5P.js.map +1 -0
  27. package/dist/chunk-WIR4HOOJ.js +27 -0
  28. package/dist/chunk-WIR4HOOJ.js.map +1 -0
  29. package/dist/{chunk-KDMRUD2P.js → chunk-Z2QXVBA6.js} +296 -8
  30. package/dist/chunk-Z2QXVBA6.js.map +1 -0
  31. package/dist/coder-CczgMqFx.d.ts +114 -0
  32. package/dist/dynamic-BvllHV6M.d.ts +221 -0
  33. package/dist/{improvement-adapter-BC4HhuAR.d.ts → improvement-adapter-CWegd3vw.d.ts} +1 -1
  34. package/dist/improvement.d.ts +2 -3
  35. package/dist/improvement.js +0 -5
  36. package/dist/improvement.js.map +1 -1
  37. package/dist/index.d.ts +123 -10
  38. package/dist/index.js +398 -10
  39. package/dist/index.js.map +1 -1
  40. package/dist/{kb-gate-D0ZIhFOU.d.ts → kb-gate-D9GBocLN.d.ts} +82 -5
  41. package/dist/{loop-runner-bin-BLMa8He3.d.ts → loop-runner-bin-CPrCoKqC.d.ts} +14 -10
  42. package/dist/loop-runner-bin.d.ts +9 -7
  43. package/dist/loop-runner-bin.js +6 -8
  44. package/dist/loops.d.ts +7 -393
  45. package/dist/loops.js +94 -25
  46. package/dist/mcp/bin.js +7 -7
  47. package/dist/mcp/bin.js.map +1 -1
  48. package/dist/mcp/index.d.ts +284 -11
  49. package/dist/mcp/index.js +341 -9
  50. package/dist/mcp/index.js.map +1 -1
  51. package/dist/{otel-export-wFDmmurL.d.ts → otel-export-Dy2DyUCU.d.ts} +1 -1
  52. package/dist/profiles.d.ts +385 -86
  53. package/dist/profiles.js +549 -4
  54. package/dist/profiles.js.map +1 -1
  55. package/dist/{run-loop-C4L1Sted.d.ts → run-loop--hSoIknW.d.ts} +35 -12
  56. package/dist/runtime-hooks-C7JwKb9E.d.ts +70 -0
  57. package/dist/runtime.d.ts +1860 -0
  58. package/dist/runtime.js +114 -0
  59. package/dist/runtime.js.map +1 -0
  60. package/dist/substrate-CUgk7F7s.d.ts +77 -0
  61. package/dist/topology.d.ts +73 -0
  62. package/dist/topology.js +111 -0
  63. package/dist/topology.js.map +1 -0
  64. package/dist/types-1HbsFa7H.d.ts +438 -0
  65. package/dist/{types-p8dWBIXL.d.ts → types-BtRLF2U3.d.ts} +1 -1
  66. package/dist/{types-DbJzz2uf.d.ts → types-DdzkffAm.d.ts} +95 -1
  67. package/dist/workflow.d.ts +3 -2
  68. package/dist/workflow.js +4 -5
  69. package/dist/workflow.js.map +1 -1
  70. package/package.json +26 -6
  71. package/skills/agent-runtime-adoption/SKILL.md +29 -26
  72. package/dist/chunk-3HMHSN22.js.map +0 -1
  73. package/dist/chunk-GFKVVRQ7.js.map +0 -1
  74. package/dist/chunk-HVYOHJHK.js.map +0 -1
  75. package/dist/chunk-KDMRUD2P.js.map +0 -1
  76. package/dist/chunk-PY6NMZYX.js +0 -52
  77. package/dist/chunk-PY6NMZYX.js.map +0 -1
  78. package/dist/chunk-S7JXV32P.js +0 -947
  79. package/dist/chunk-S7JXV32P.js.map +0 -1
  80. package/dist/chunk-SKUZZCHE.js.map +0 -1
  81. package/dist/chunk-SQSCRJ7U.js +0 -65
  82. package/dist/chunk-SQSCRJ7U.js.map +0 -1
  83. package/dist/chunk-VOX6Z3II.js +0 -90
  84. package/dist/chunk-VOX6Z3II.js.map +0 -1
  85. package/dist/chunk-XBUG326M.js +0 -261
  86. package/dist/chunk-XBUG326M.js.map +0 -1
  87. package/dist/dynamic-wUgp6UKs.d.ts +0 -108
  88. package/dist/optimize-prompt-D-urF2wW.d.ts +0 -129
  89. /package/dist/{chunk-NRZOXCJK.js.map → chunk-KSMX62JF.js.map} +0 -0
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/loops/drivers/dynamic.ts","../src/loops/drivers/planners.ts","../src/loops/drivers/refine.ts","../src/loops/drivers/sandbox-planner.ts","../src/loops/report-usage.ts","../src/loops/sandbox-acquire.ts","../src/loops/sandbox-events.ts","../src/loops/run-loop.ts","../src/loops/loop-dispatch.ts"],"sourcesContent":["/**\n * @experimental\n *\n * Dynamic driver — the agent authors the loop topology at runtime.\n *\n * Where `refine` and `fanout-vote` encode a fixed shape as a pure function of\n * history, this driver delegates the per-round shape to an injected\n * `TopologyPlanner`. Each round the planner inspects the task + iteration\n * history and emits one `TopologyMove`:\n * - `refine` → one task next round (optionally rewritten from the prior attempt)\n * - `fanout` → N tasks next round (the kernel round-robins `agentRuns`, so a\n * 2-harness fanout dispatches branch 0 to harness A and branch 1 to harness B)\n * - `stop` → terminate; the kernel selects the winner across all iterations\n *\n * The planner is the brain; this driver is the structure. It maps moves onto\n * the kernel's `plan`/`decide` contract, enforces the iteration + fanout caps,\n * and fails loud on a malformed move. The planner is injected exactly like\n * `refine`'s `refineTask` and `fanout-vote`'s `selector` — so a test can drive\n * a deterministic policy through the real kernel, and production can wire it to\n * an LLM via `createSandboxPlanner`.\n *\n * Topology is orthogonal to harness: the planner never names a backend. Which\n * harness runs a branch is decided by the `AgentRunSpec` the kernel round-robins\n * to, so one dynamic driver works across claude-code, codex, opencode, pi —\n * including fanning a single round across several at once.\n */\n\nimport { PlannerError, ValidationError } from '../../errors'\nimport type { Driver, Iteration } from '../types'\n\n/** Terminal once `decide` returns `'done'` (a kernel terminal decision). */\nexport type DynamicDecision = 'continue' | 'done'\n\n/**\n * One topology decision for the next round. `fanout` carries explicit tasks\n * rather than a count so the planner can issue heterogeneous branches (a\n * different sub-task per harness); pass N copies of one task for a homogeneous\n * fanout that relies on `agentRuns` diversity instead.\n *\n * @experimental\n */\nexport type TopologyMove<Task> =\n | { kind: 'refine'; task: Task; rationale?: string; parentIndex?: number }\n | { kind: 'fanout'; tasks: Task[]; rationale?: string; parentIndex?: number }\n | { kind: 'stop'; rationale?: string }\n\n/** @experimental */\nexport interface PlannerContext<Task, Output> {\n /** The root task the loop was invoked with — stable across rounds. */\n task: Task\n /** Every iteration so far, in dispatch order, with outputs + verdicts. */\n history: ReadonlyArray<Iteration<Task, Output>>\n /** `history.length` — iterations already spent. */\n iterationsSpent: number\n /** Iterations left before the driver's `maxIterations` cap forces a stop. */\n iterationsRemaining: number\n}\n\n/**\n * Chooses the next topology move from the task + history. Sync or async; an\n * async planner is where an LLM call goes (see `createSandboxPlanner`).\n *\n * @experimental\n */\nexport type TopologyPlanner<Task, Output> = (\n ctx: PlannerContext<Task, Output>,\n) => TopologyMove<Task> | Promise<TopologyMove<Task>>\n\n/** @experimental */\nexport interface CreateDynamicDriverOptions<Task, Output> {\n /** The agent-authored topology policy. Invoked once per round in `plan`. */\n planner: TopologyPlanner<Task, Output>\n /**\n * Hard safety cap on total iterations. When reached, the driver stops before\n * consulting the planner. Default 8. Set the kernel's `runLoop`\n * `maxIterations >= ` this so the driver's cap governs and the loop closes on\n * a clean `'done'` rather than a truncated `'continue'`.\n */\n maxIterations?: number\n /** Max branches a single `fanout` move may dispatch. Default 4. */\n maxFanout?: number\n /** Stable identifier surfaced in trace events. Default `'dynamic'`. */\n name?: string\n}\n\n/** @experimental */\nexport function createDynamicDriver<Task, Output>(\n options: CreateDynamicDriverOptions<Task, Output>,\n): Driver<Task, Output, DynamicDecision> {\n if (typeof options.planner !== 'function') {\n throw new ValidationError('createDynamicDriver: planner must be a function')\n }\n const maxIterations = options.maxIterations ?? 8\n if (!Number.isFinite(maxIterations) || maxIterations <= 0) {\n throw new ValidationError('createDynamicDriver: maxIterations must be > 0')\n }\n const maxFanout = options.maxFanout ?? 4\n if (!Number.isFinite(maxFanout) || maxFanout < 1) {\n throw new ValidationError('createDynamicDriver: maxFanout must be >= 1')\n }\n\n // The kernel calls plan(), runs the batch, then calls decide() — strictly\n // sequential, one driver instance per loop. Caching the move the planner\n // chose this round lets decide() report terminality without re-invoking the\n // planner (which would double every LLM call).\n let pending: TopologyMove<Task> | undefined\n\n return {\n name: options.name ?? 'dynamic',\n async plan(task, history) {\n if (history.length >= maxIterations) {\n pending = { kind: 'stop', rationale: `maxIterations (${maxIterations}) reached` }\n return []\n }\n const move = await options.planner({\n task,\n history,\n iterationsSpent: history.length,\n iterationsRemaining: maxIterations - history.length,\n })\n pending = validateMove(move, maxFanout)\n switch (pending.kind) {\n case 'refine':\n return [pending.task]\n case 'fanout':\n return pending.tasks\n case 'stop':\n return []\n }\n },\n decide() {\n // pending is set by the plan() call that immediately precedes every\n // decide(). Only a `stop` move terminates; refine/fanout keep looping so\n // plan() — and thus the planner — runs again next round.\n return pending?.kind === 'stop' ? 'done' : 'continue'\n },\n describePlan() {\n // Surface the move the planner just chose (kind + rationale + an optional\n // DECLARED branch source) so the kernel's loop.plan trace carries the\n // agent's intent — including faithful edge lineage when the planner\n // branched off a specific (non-winner) iteration. `pending` is the move\n // set by the preceding plan().\n if (!pending) return undefined\n const out: { kind: string; rationale?: string; parentIndex?: number } = { kind: pending.kind }\n if (pending.rationale !== undefined) out.rationale = pending.rationale\n if (pending.kind !== 'stop' && pending.parentIndex !== undefined) {\n out.parentIndex = pending.parentIndex\n }\n return out\n },\n }\n}\n\nfunction validateMove<Task>(move: TopologyMove<Task>, maxFanout: number): TopologyMove<Task> {\n if (!move || typeof move !== 'object' || typeof (move as { kind?: unknown }).kind !== 'string') {\n throw new PlannerError(`dynamic planner returned a non-move value: ${describe(move)}`)\n }\n switch (move.kind) {\n case 'refine':\n return move\n case 'stop':\n return move\n case 'fanout': {\n if (!Array.isArray(move.tasks) || move.tasks.length === 0) {\n throw new PlannerError('dynamic planner fanout move must carry a non-empty tasks[]')\n }\n if (move.tasks.length <= maxFanout) return move\n // Clamp rather than reject — over-fanning is a budget concern, not a\n // structural error. The clamp is recorded in the rationale for traces.\n return {\n kind: 'fanout',\n tasks: move.tasks.slice(0, maxFanout),\n rationale: `${move.rationale ?? ''} [clamped ${move.tasks.length}→${maxFanout}]`.trim(),\n }\n }\n default:\n throw new PlannerError(\n `dynamic planner returned unknown move kind: ${describe((move as { kind: unknown }).kind)}`,\n )\n }\n}\n\nfunction describe(value: unknown): string {\n try {\n return JSON.stringify(value) ?? String(value)\n } catch {\n return String(value)\n }\n}\n\n/**\n * Compact, planner-friendly view of iteration history — what an LLM planner\n * needs to choose the next move without the raw event streams. Output is\n * truncated so a long run's prompt stays bounded.\n *\n * @experimental\n */\nexport function summarizeHistory<Task, Output>(\n history: ReadonlyArray<Iteration<Task, Output>>,\n opts: { maxOutputChars?: number } = {},\n): Array<{\n index: number\n agentRunName: string\n valid?: boolean\n score?: number\n error?: string\n output?: string\n}> {\n const maxOutputChars = opts.maxOutputChars ?? 600\n return history.map((iter) => {\n const row: {\n index: number\n agentRunName: string\n valid?: boolean\n score?: number\n error?: string\n output?: string\n } = { index: iter.index, agentRunName: iter.agentRunName }\n if (iter.verdict) {\n row.valid = iter.verdict.valid\n if (typeof iter.verdict.score === 'number') row.score = iter.verdict.score\n }\n if (iter.error) row.error = iter.error.message\n if (iter.output !== undefined) {\n const serialized = describe(iter.output)\n row.output =\n serialized.length > maxOutputChars ? `${serialized.slice(0, maxOutputChars)}…` : serialized\n }\n return row\n })\n}\n","/**\n * @experimental\n *\n * Named driver policies — a registry, not a constructor zoo.\n *\n * A \"driver variant\" is just a `TopologyPlanner` chosen by name and run by the\n * one interpreter (`createDynamicDriver`). The agentic variants are sandboxed\n * agents (`createSandboxPlanner`) — an LLM/agent in a box that emits the next\n * move; this registry holds the *deterministic* ones a benchmark needs as\n * controls. Today that's `blind`: a single attempt, no steering — the baseline\n * you measure a real driver against.\n *\n * Adding a variant is a line here (or a sandboxed planner registered by name),\n * never a new `createXDriver` factory and never a spec schema.\n */\n\nimport type { TopologyMove, TopologyPlanner } from './dynamic'\n\n/** A driver policy over prompt-shaped (string) tasks. Output is consulted only\n * through the iteration's verdict, so it stays `unknown`. */\nexport type PromptPlanner = TopologyPlanner<string, unknown>\n\n/**\n * `blind` — one attempt, then stop. The no-driver control: a single worker run\n * with no steering, so a benchmark can isolate what a real driver adds.\n */\nexport const blind: PromptPlanner = ({ task, history }): TopologyMove<string> =>\n history.length === 0\n ? { kind: 'refine', task, rationale: 'blind: single attempt' }\n : { kind: 'stop', rationale: 'blind: one attempt only' }\n\n/** The registry. Pick a driver by name (e.g. `DRIVER=blind`); fail loud on an\n * unknown key. Sandboxed-agent planners can be registered here too. */\nexport const PROMPT_PLANNERS: Record<string, PromptPlanner> = {\n blind,\n}\n\n/** Resolve a planner by name; fail loud on an unknown variant. */\nexport function resolvePlanner(name: string): PromptPlanner {\n const planner = PROMPT_PLANNERS[name]\n if (!planner) {\n throw new Error(\n `planners: unknown driver '${name}' (have: ${Object.keys(PROMPT_PLANNERS).join(', ')})`,\n )\n }\n return planner\n}\n","/**\n * @experimental\n *\n * Refine driver — single task per iteration, validator-gated.\n *\n * `plan` returns `[task]` (possibly transformed via `refineTask`) until the\n * prior verdict is valid OR the local cap is hit, then `[]`.\n * `decide` returns `'stop'` once the latest verdict is valid OR the cap is\n * reached. The kernel's `maxIterations` is an orthogonal safety cap;\n * whichever is lower wins.\n */\n\nimport { ValidationError } from '../../errors'\nimport type { DefaultVerdict, Driver, Iteration } from '../types'\n\nexport type RefineDecision = 'continue' | 'stop'\n\n/** @experimental */\nexport interface CreateRefineDriverOptions<Task> {\n /** Hard cap on iterations. Default 5. */\n maxIterations?: number\n /**\n * Optional task transform applied each round based on the prior verdict.\n * When omitted, the same task is replayed and the agent is expected to\n * inspect the sandbox session state for prior attempts.\n */\n refineTask?: (task: Task, prior: DefaultVerdict) => Task\n /** Stable identifier surfaced in trace events. Default `'refine'`. */\n name?: string\n}\n\n/** @experimental */\nexport function createRefineDriver<Task, Output>(\n options: CreateRefineDriverOptions<Task> = {},\n): Driver<Task, Output, RefineDecision> {\n const maxIterations = options.maxIterations ?? 5\n if (!Number.isFinite(maxIterations) || maxIterations <= 0) {\n throw new ValidationError('createRefineDriver: maxIterations must be > 0')\n }\n const refineTask = options.refineTask\n return {\n name: options.name ?? 'refine',\n async plan(task, history) {\n if (history.length >= maxIterations) return []\n if (history.length === 0) return [task]\n const prior = history.at(-1)\n if (!prior) return [task]\n if (prior.verdict?.valid === true) return []\n // Worker error: replay the same task so the agent can self-correct.\n // The driver has no signal beyond `verdict`; only the validator\n // controls \"good enough\".\n if (!refineTask || !prior.verdict) return [prior.task]\n return [refineTask(prior.task, prior.verdict)]\n },\n decide(history) {\n const last = history.at(-1)\n if (!last) return 'continue'\n if (last.verdict?.valid === true) return 'stop'\n if (history.length >= maxIterations) return 'stop'\n return 'continue'\n },\n }\n}\n\n/**\n * Test helper: select the last-valid iteration (or the last attempt if\n * none passed). Mirrors the kernel's default selector ordering for refine\n * topologies — the most recent successful attempt wins.\n *\n * @experimental\n */\nexport function refineWinnerIndex<Task, Output>(\n iterations: ReadonlyArray<Iteration<Task, Output>>,\n): number | undefined {\n for (let i = iterations.length - 1; i >= 0; i -= 1) {\n if (iterations[i]?.verdict?.valid) return i\n }\n return iterations.length > 0 ? iterations.length - 1 : undefined\n}\n","/**\n * @experimental\n *\n * `createSandboxPlanner` — wire the dynamic driver's `TopologyPlanner` to a\n * real agent. Each round it spins a sandbox on `profile`, streams a prompt that\n * carries the history summary, and decodes the agent's chosen `TopologyMove`\n * from a JSON envelope it emits. This is the \"agent authors its own loop\n * topology\" path: the planner profile can be any harness (claude-code, codex,\n * opencode, pi) — its only job is to read what happened and emit the next move.\n *\n * The planner profile is deliberately distinct from the worker `agentRuns`: a\n * cheap fast model can steer topology while expensive workers do the labor, and\n * the planner never names which harness runs a branch — the kernel's\n * `agentRuns` round-robin decides that.\n *\n * Three execution modes, all the same code path:\n * - LLM call — a cheap-model `profile`; one prompt → one move.\n * - different sandbox (default) — a fresh planner-owned box per round.\n * - same sandbox — pass `reuseBox` to stream the move into the worker's\n * own box (a session against its live filesystem/state),\n * so the driver steers from what the worker actually did.\n *\n * Envelope contract the agent must emit (fenced ```json or a structured\n * `result`/`final` event payload):\n * { \"kind\": \"refine\" | \"fanout\" | \"stop\",\n * \"tasks\"?: [ <task>, ... ], // decoded via `decodeTask`\n * \"n\"?: number, // fanout shorthand: N copies of the root task\n * \"rationale\"?: string }\n *\n * A missing / unparseable / unknown-kind envelope throws `PlannerError` — the\n * loop never silently runs a topology the agent did not choose.\n */\n\nimport type {\n AgentProfile,\n CreateSandboxOptions,\n SandboxEvent,\n SandboxInstance,\n} from '@tangle-network/sandbox'\nimport { PlannerError, ValidationError } from '../../errors'\nimport type { AgentRunSpec, LoopSandboxClient } from '../types'\nimport type { PlannerContext, TopologyMove, TopologyPlanner } from './dynamic'\nimport { summarizeHistory } from './dynamic'\n\n/** Raw, pre-decode envelope an agent emits to choose the next move. */\nexport interface TopologyMoveEnvelope {\n kind: string\n tasks?: unknown[]\n n?: number\n rationale?: string\n}\n\n/** @experimental */\nexport interface CreateSandboxPlannerOptions<Task, Output> {\n /** Sandbox client — the planner calls `.create()` once per round. */\n client: LoopSandboxClient\n /** The planner agent. Steers topology; does not run the work. */\n profile: AgentProfile\n /**\n * Decode one raw task from the envelope's `tasks[]` into a domain `Task`.\n * Required because `Task` is opaque to this module — only the caller knows\n * its shape. Throw to reject a malformed task; the error surfaces as a\n * `PlannerError`.\n */\n decodeTask: (raw: unknown, ctx: PlannerContext<Task, Output>) => Task\n /** Override the default prompt (history summary + envelope contract). */\n buildPrompt?: (ctx: PlannerContext<Task, Output>) => string | Promise<string>\n /** Override envelope extraction from the event stream. */\n parseEnvelope?: (events: SandboxEvent[]) => TopologyMoveEnvelope | undefined\n /** Sandbox overrides for the planner sandbox (timeouts, env, etc.). */\n sandboxOverrides?: AgentRunSpec<Task>['sandboxOverrides']\n /** Cancellation for the planner's own LLM call. */\n signal?: AbortSignal\n /**\n * Same-sandbox mode. Return an existing box and the planner streams its move\n * INTO that box (a session against the worker's environment) instead of\n * spinning its own — so the driver can inspect the worker's real filesystem\n * and state, not just the history summary. The returned box's lifecycle is\n * the CALLER's: the planner neither creates nor deletes it. Return\n * `undefined` to fall back to the default (a fresh, planner-owned box =\n * different-sandbox mode). Omit entirely for the default.\n */\n reuseBox?: () => SandboxInstance | undefined | Promise<SandboxInstance | undefined>\n}\n\n/** @experimental */\nexport function createSandboxPlanner<Task, Output>(\n opts: CreateSandboxPlannerOptions<Task, Output>,\n): TopologyPlanner<Task, Output> {\n if (!opts.client || typeof opts.client.create !== 'function') {\n throw new ValidationError('createSandboxPlanner: client.create is required')\n }\n if (typeof opts.decodeTask !== 'function') {\n throw new ValidationError('createSandboxPlanner: decodeTask is required')\n }\n const buildPrompt = opts.buildPrompt ?? defaultBuildPrompt\n const parseEnvelope = opts.parseEnvelope ?? defaultParseEnvelope\n\n return async (ctx) => {\n // Same-sandbox mode: reuse the caller-owned box; else spin a planner-owned one.\n const reused = opts.reuseBox ? await opts.reuseBox() : undefined\n const box =\n reused ?? (await opts.client.create(buildSandboxOptions(opts.profile, opts.sandboxOverrides)))\n const plannerOwnsBox = reused === undefined\n try {\n const prompt = await buildPrompt(ctx)\n const events: SandboxEvent[] = []\n for await (const event of box.streamPrompt(prompt, { signal: opts.signal })) {\n events.push(event)\n }\n const envelope = parseEnvelope(events)\n if (!envelope) {\n throw new PlannerError('sandbox planner emitted no parseable topology-move envelope')\n }\n return envelopeToMove(envelope, ctx, opts.decodeTask)\n } finally {\n // Tear down only a box WE created (one per plan() round, so it doesn't\n // leak). A reused (same-sandbox) box belongs to the caller — never delete it.\n if (plannerOwnsBox) {\n try {\n if (typeof (box as { delete?: unknown }).delete === 'function') await box.delete()\n } catch {\n // ignore — platform reaps on expiry\n }\n }\n }\n }\n}\n\nfunction envelopeToMove<Task, Output>(\n envelope: TopologyMoveEnvelope,\n ctx: PlannerContext<Task, Output>,\n decodeTask: (raw: unknown, ctx: PlannerContext<Task, Output>) => Task,\n): TopologyMove<Task> {\n const kind = String(envelope.kind ?? '').toLowerCase()\n const rationale = typeof envelope.rationale === 'string' ? envelope.rationale : undefined\n if (kind === 'stop') {\n return { kind: 'stop', rationale }\n }\n if (kind === 'refine') {\n const raw = Array.isArray(envelope.tasks) ? envelope.tasks[0] : undefined\n // No new task → replay the root task; the worker self-corrects from its\n // own prior attempt in sandbox state, mirroring the refine driver default.\n const task = raw === undefined ? ctx.task : decodeTaskGuarded(decodeTask, raw, ctx)\n return { kind: 'refine', task, rationale }\n }\n if (kind === 'fanout') {\n const tasks = resolveFanoutTasks(envelope, ctx, decodeTask)\n return { kind: 'fanout', tasks, rationale }\n }\n throw new PlannerError(\n `sandbox planner emitted unknown move kind: ${JSON.stringify(envelope.kind)}`,\n )\n}\n\nfunction resolveFanoutTasks<Task, Output>(\n envelope: TopologyMoveEnvelope,\n ctx: PlannerContext<Task, Output>,\n decodeTask: (raw: unknown, ctx: PlannerContext<Task, Output>) => Task,\n): Task[] {\n if (Array.isArray(envelope.tasks) && envelope.tasks.length > 0) {\n return envelope.tasks.map((raw) => decodeTaskGuarded(decodeTask, raw, ctx))\n }\n // `n` shorthand: N copies of the root task, leaning on `agentRuns` diversity.\n if (typeof envelope.n === 'number' && Number.isFinite(envelope.n) && envelope.n >= 1) {\n return Array.from({ length: Math.floor(envelope.n) }, () => ctx.task)\n }\n throw new PlannerError('sandbox planner fanout envelope needs a non-empty tasks[] or n >= 1')\n}\n\nfunction decodeTaskGuarded<Task, Output>(\n decodeTask: (raw: unknown, ctx: PlannerContext<Task, Output>) => Task,\n raw: unknown,\n ctx: PlannerContext<Task, Output>,\n): Task {\n try {\n return decodeTask(raw, ctx)\n } catch (err) {\n throw new PlannerError(`sandbox planner decodeTask rejected ${JSON.stringify(raw)}`, {\n cause: err,\n })\n }\n}\n\nfunction buildSandboxOptions(\n profile: AgentProfile,\n overrides: AgentRunSpec<unknown>['sandboxOverrides'],\n): CreateSandboxOptions {\n const base = overrides ?? {}\n const overrideBackend = base.backend\n const explicitType = profile.metadata?.backendType\n type BackendType = NonNullable<CreateSandboxOptions['backend']>['type']\n return {\n ...base,\n backend: {\n type: (overrideBackend?.type ?? explicitType ?? 'opencode') as BackendType,\n profile,\n ...(overrideBackend?.model ? { model: overrideBackend.model } : {}),\n ...(overrideBackend?.server ? { server: overrideBackend.server } : {}),\n },\n }\n}\n\nfunction defaultBuildPrompt<Task, Output>(ctx: PlannerContext<Task, Output>): string {\n const summary = summarizeHistory(ctx.history)\n return [\n 'You are the loop planner. You do not do the work — you decide the topology of the next round.',\n '',\n `Root task:\\n${safeJson(ctx.task)}`,\n '',\n `Iterations spent: ${ctx.iterationsSpent}. Remaining before the hard cap: ${ctx.iterationsRemaining}.`,\n '',\n ctx.history.length === 0\n ? 'No attempts yet.'\n : `Attempts so far (index, agent, verdict, output):\\n${safeJson(summary)}`,\n '',\n 'Choose ONE move and emit it as a fenced JSON block:',\n ' - {\"kind\":\"refine\",\"tasks\":[<task>],\"rationale\":\"...\"} — one more attempt; omit tasks to replay the root task.',\n ' - {\"kind\":\"fanout\",\"tasks\":[<task>,<task>],\"rationale\":\"...\"} — N parallel branches (or \"n\": N for N copies of the root task).',\n ' - {\"kind\":\"stop\",\"rationale\":\"...\"} — a valid result exists or further attempts will not help.',\n '',\n 'Stop as soon as an attempt is valid. Prefer refine when an attempt is close; fan out when attempts disagree or the approach is uncertain.',\n 'Emit ONLY the JSON block.',\n ].join('\\n')\n}\n\nconst TERMINAL_EVENT_TYPES = new Set(['result', 'final', 'planner.move', 'done'])\n\n/**\n * Harness-agnostic move extraction. The move arrives one of two ways and we must\n * not assume which harness/SDK-version we're on (opencode emits `data.finalText`,\n * others `data.result`/`data.text`/…) — so we cover all text-bearing fields and\n * prefer (1) a STRUCTURED payload, then (2) a fenced move in a TERMINAL event's\n * final text, then (3) the latest schema-valid fenced/bare envelope anywhere.\n * Reverse iteration is load-bearing: the agent's real move is the LAST envelope\n * emitted; the JSON *examples* echoed from the prompt appear earlier, so latest-\n * first never mistakes an example for the answer.\n *\n * The reliable long-term path is a structured-output tool the planner CALLS (no\n * prose scraping at all); this text extractor is the resilient fallback for\n * harnesses without one.\n */\nfunction defaultParseEnvelope(events: SandboxEvent[]): TopologyMoveEnvelope | undefined {\n const moveText = (data: Record<string, unknown>): string | undefined =>\n pickString(data.finalText) ??\n pickString(data.text) ??\n pickString(data.delta) ??\n pickString(data.content) ??\n pickString(data.message)\n\n // 1) Terminal events, latest first: structured payload, else a fenced move in the final text.\n for (let i = events.length - 1; i >= 0; i -= 1) {\n const event = events[i]\n if (!event) continue\n const data = isRecord(event.data) ? event.data : undefined\n if (!data) continue\n if (!TERMINAL_EVENT_TYPES.has(String(event.type ?? ''))) continue\n const structured = coerceEnvelope(data.result ?? data.output ?? data.move ?? data)\n if (structured) return structured\n const fromText = coerceEnvelope(extractFencedJson(moveText(data) ?? ''))\n if (fromText) return fromText\n }\n // 2) Any text-bearing event, latest first: the last schema-valid fenced/bare envelope.\n for (let i = events.length - 1; i >= 0; i -= 1) {\n const event = events[i]\n if (!event) continue\n const data = isRecord(event.data) ? event.data : undefined\n if (!data) continue\n const text = moveText(data)\n if (!text) continue\n const coerced = coerceEnvelope(extractFencedJson(text))\n if (coerced) return coerced\n }\n return undefined\n}\n\nfunction coerceEnvelope(value: unknown): TopologyMoveEnvelope | undefined {\n if (!isRecord(value)) return undefined\n if (typeof value.kind !== 'string' || value.kind.length === 0) return undefined\n const out: TopologyMoveEnvelope = { kind: value.kind }\n if (Array.isArray(value.tasks)) out.tasks = value.tasks\n if (typeof value.n === 'number') out.n = value.n\n if (typeof value.rationale === 'string') out.rationale = value.rationale\n return out\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return value !== null && typeof value === 'object' && !Array.isArray(value)\n}\n\nfunction pickString(value: unknown): string | undefined {\n return typeof value === 'string' && value.length > 0 ? value : undefined\n}\n\nfunction extractFencedJson(text: string): unknown | undefined {\n const match = text.match(/```(?:json)?\\s*([\\s\\S]*?)```/i)\n const body = (match?.[1] ?? text).trim()\n if (!body) return undefined\n try {\n return JSON.parse(body)\n } catch {\n return undefined\n }\n}\n\nfunction safeJson(value: unknown): string {\n try {\n return JSON.stringify(value, null, 2) ?? String(value)\n } catch {\n return String(value)\n }\n}\n","/**\n * Bridge a finished `runLoop` into an agent-eval campaign / profile-matrix\n * dispatch.\n *\n * `runProfileMatrix` (and `runCampaign`) run the backend-integrity guard over\n * the token usage a dispatch reports through `ctx.cost`. A dispatch that wraps\n * `runLoop` must forward the loop's cost AND token usage, or the guard reads\n * the run as a stub and throws. `reportLoopUsage` is that one line:\n *\n * const dispatch: ProfileDispatchFn<S, A> = async (profile, scenario, ctx) => {\n * const result = await runLoop({ ...optsFor(profile, scenario), ctx: loopCtx })\n * reportLoopUsage(ctx, result)\n * return result.winner?.output as A\n * }\n *\n * Typed structurally against the campaign `DispatchContext.cost` so this module\n * stays free of an agent-eval import — it works with any cost meter exposing\n * `observe` + `observeTokens`.\n */\n\nimport type { LoopResult } from './types'\n\n/** The slice of an agent-eval campaign `DispatchContext.cost` this needs. */\nexport interface UsageSink {\n observe(amountUsd: number, source: string): void\n observeTokens(usage: { input: number; output: number }): void\n}\n\n/**\n * Forward a `LoopResult`'s aggregated cost + token usage into a campaign cost\n * meter so the backend-integrity guard sees real LLM activity. `source`\n * defaults to `'loop'`.\n */\nexport function reportLoopUsage<Task, Output, Decision>(\n cost: UsageSink,\n result: Pick<LoopResult<Task, Output, Decision>, 'costUsd' | 'tokenUsage'>,\n source = 'loop',\n): void {\n cost.observe(result.costUsd, source)\n cost.observeTokens({ input: result.tokenUsage.input, output: result.tokenUsage.output })\n}\n","/**\n * @experimental\n *\n * `acquireSandbox` — cold-start-resilient sandbox acquisition. Eliminates the\n * \"create timed out at the proxy\" failure mode conceptually by DECOUPLING \"the\n * create HTTP call returned\" from \"the sandbox is ready\":\n *\n * - Create is initiated with a known `name`.\n * - Readiness is observed from the sandbox's own `status` (`refresh()` polls\n * true state), NOT from whether the create call returned in time.\n * - If the create call itself times out at a gateway (502/503/504/522/524 or\n * a transport timeout), provisioning is still running server-side — so we\n * find the named sandbox via `list()` and wait for it to reach `running`.\n *\n * Result: a scale-from-zero cold start (node boot + host-agent registration,\n * minutes) can no longer surface as a create failure behind a ~100s proxy\n * limit. The loop becomes indifferent to whether the host pool is warm or cold.\n *\n * Backward-compatible: an instance that reports no `status` (the minimal fakes\n * the loop tests use) is treated as ready — only an explicit `pending`/\n * `provisioning` status triggers waiting, and only a retryable THROW triggers\n * the find-by-name path. Real errors (auth, validation, budget) fail loud.\n */\n\nimport type { CreateSandboxOptions, SandboxInstance } from '@tangle-network/sandbox'\nimport { ValidationError } from '../errors'\nimport type { LoopSandboxClient } from './types'\n\n/** Gateway/transport statuses where the create call returned but provisioning continues. */\nconst RETRYABLE_HTTP = new Set([502, 503, 504, 522, 524, 408, 425, 429])\nconst TERMINAL_STATUS = new Set(['failed', 'expired', 'stopped'])\n\n/** @experimental */\nexport interface AcquireOptions {\n /**\n * Total budget for the sandbox to reach `running`, covering on-demand node\n * cold-start. Default 600_000ms — matches the orchestrator's pending-host\n * registration window so we never give up before the platform itself would.\n */\n readyTimeoutMs?: number\n /** Poll interval while waiting for `running` / for the named sandbox to appear. */\n pollIntervalMs?: number\n /** Cancellation (user abort). Distinct from create-call timeouts. */\n signal?: AbortSignal\n /** Stamp a name so a timed-out create is recoverable by lookup. Auto-generated if absent. */\n name?: string\n /** Clock override for deterministic tests. */\n now?: () => number\n /** Sleep override for deterministic tests. */\n sleep?: (ms: number) => Promise<void>\n}\n\n/** Minimal client surface acquire needs beyond `create` (the real SDK satisfies it). */\ninterface PollableClient extends LoopSandboxClient {\n list?: (options?: unknown) => Promise<SandboxInstance[]>\n get?: (id: string) => Promise<SandboxInstance | null>\n}\n\n/** @experimental */\nexport async function acquireSandbox(\n client: LoopSandboxClient,\n options: CreateSandboxOptions,\n acquire: AcquireOptions = {},\n): Promise<SandboxInstance> {\n if (!client || typeof client.create !== 'function') {\n throw new ValidationError('acquireSandbox: client.create is required')\n }\n const now = acquire.now ?? Date.now\n const sleep = acquire.sleep ?? ((ms: number) => new Promise((r) => setTimeout(r, ms)))\n const pollMs = acquire.pollIntervalMs ?? 3000\n const deadline = now() + (acquire.readyTimeoutMs ?? 600_000)\n const name = options.name ?? acquire.name ?? `loop-sbx-${randomSuffix()}`\n const createOpts: CreateSandboxOptions = { ...options, name }\n const c = client as PollableClient\n\n let lastErr: unknown\n let attempt = 0\n while (now() < deadline) {\n throwIfAborted(acquire.signal)\n try {\n const box = await client.create(createOpts)\n return await waitUntilReady(box, deadline, pollMs, acquire.signal, now, sleep)\n } catch (err) {\n throwIfAborted(acquire.signal)\n // Non-retryable (auth/validation/budget) fails loud immediately.\n if (!isRetryable(err)) throw err\n lastErr = err\n // Two recoveries for a gateway-timed-out create, in order:\n // (a) some orchestrators leave a pending sandbox behind — attach to it;\n // (b) others roll the create back — so retry create with backoff (a\n // retry lands once a warm host exists / the autoscaler caught up).\n if (typeof c.list === 'function') {\n const found = (await c.list().catch(() => []))?.find((b) => b.name === name)\n if (found) return await waitUntilReady(found, deadline, pollMs, acquire.signal, now, sleep)\n }\n attempt += 1\n await sleep(Math.min(pollMs * attempt, 15_000))\n }\n }\n throw new ValidationError(\n `acquireSandbox: could not acquire a running sandbox \"${name}\" within budget`,\n { cause: lastErr instanceof Error ? lastErr : undefined },\n )\n}\n\n/** Wait for `running`. No status (minimal fakes) = ready. Terminal status throws. */\nasync function waitUntilReady(\n box: SandboxInstance,\n deadline: number,\n pollMs: number,\n signal: AbortSignal | undefined,\n now: () => number,\n sleep: (ms: number) => Promise<void>,\n): Promise<SandboxInstance> {\n for (;;) {\n throwIfAborted(signal)\n const status = readStatus(box)\n if (status === undefined || status === 'running') return box\n if (TERMINAL_STATUS.has(status)) {\n throw new ValidationError(\n `acquireSandbox: sandbox ${box.id ?? '(unknown)'} is ${status}${box.error ? `: ${box.error}` : ''}`,\n )\n }\n if (now() >= deadline) {\n throw new ValidationError(\n `acquireSandbox: sandbox ${box.id ?? '(unknown)'} not running within budget (last status: ${status})`,\n )\n }\n await sleep(pollMs)\n if (typeof box.refresh === 'function') await box.refresh()\n }\n}\n\nfunction readStatus(box: SandboxInstance): string | undefined {\n const s = (box as { status?: unknown }).status\n return typeof s === 'string' ? s : undefined\n}\n\nfunction isRetryable(err: unknown): boolean {\n if (!err || typeof err !== 'object') return false\n const e = err as { status?: number; statusCode?: number; name?: string; message?: string }\n const status = e.status ?? e.statusCode\n if (typeof status === 'number' && RETRYABLE_HTTP.has(status)) return true\n const name = e.name ?? ''\n if (name === 'TimeoutError' || name === 'ServerError' || name === 'NetworkError') return true\n return /\\b(timed out|timeout|gateway|temporarily unavailable|ECONNRESET|ETIMEDOUT|EAI_AGAIN)\\b/i.test(\n e.message ?? '',\n )\n}\n\nfunction throwIfAborted(signal: AbortSignal | undefined): void {\n if (signal?.aborted) {\n const err = new Error('aborted')\n err.name = 'AbortError'\n throw err\n }\n}\n\nfunction randomSuffix(len = 10): string {\n return Math.random()\n .toString(36)\n .slice(2, 2 + len)\n}\n","/**\n * Sandbox-event → runtime-event mapping.\n *\n * The sandbox SDK emits a polymorphic `SandboxEvent = { type, data, id? }`\n * whose `type` vocabulary is backend-determined (opencode, etc.) rather than\n * enumerated by the SDK. Two consumers project it:\n * - the loop kernel's cost ledger (`extractLlmCallEvent`) — sums usage off\n * every cost-bearing event, regardless of stream shape;\n * - the `AgentRuntime.act` streaming contract (`mapSandboxEvent`) — projects\n * incremental events to the `RuntimeStreamEvent` chat-UX vocabulary.\n *\n * Both live here so the empirically-observed `type` vocabulary has one home.\n */\n\nimport type { SandboxEvent } from '@tangle-network/sandbox'\nimport type { RuntimeStreamEvent } from '../types'\n\n/**\n * Extract a `RuntimeStreamEvent`-shaped `llm_call` from a sandbox event when\n * the event carries usage/cost data. Returns `undefined` for non-cost events\n * so the kernel can iterate the full stream without branching.\n *\n * Canonical cost-carrying types observed in the wild:\n * - `llm_call` — `data: { model, tokensIn, tokensOut, costUsd, ... }`\n * - `message.completed` / `result` — `data: { usage: { inputTokens,\n * outputTokens, totalCostUsd? } }`\n * - `cost.usage` / `usage` — same shape under a dedicated type\n *\n * Numeric coercion is strict: `Number.isFinite` gates every accumulator write\n * so a sentinel `NaN` from a misbehaving backend cannot poison the ledger.\n */\nexport function extractLlmCallEvent(\n event: SandboxEvent,\n agentRunName: string,\n): (RuntimeStreamEvent & { type: 'llm_call' }) | undefined {\n if (!event || typeof event !== 'object') return undefined\n const type = String(event.type ?? '')\n const data =\n event.data && typeof event.data === 'object'\n ? (event.data as Record<string, unknown>)\n : ({} as Record<string, unknown>)\n\n if (type === 'llm_call' || type === 'cost.usage' || type === 'usage') {\n return buildLlmCall(data, agentRunName)\n }\n if (type === 'message.completed' || type === 'result' || type === 'final') {\n const usage = data.usage as Record<string, unknown> | undefined\n if (!usage || typeof usage !== 'object') return undefined\n return buildLlmCall({ ...usage, model: data.model ?? usage.model }, agentRunName)\n }\n // sandbox 0.4.0 terminal event: `data = { tokenUsage: { inputTokens, outputTokens,\n // reasoningTokens, cacheReadInputTokens }, totalCostUsd }`. Usage lives under\n // `tokenUsage` (not `usage`) and the cost is top-level — neither matched the\n // branches above, so an in-process loopDispatch run reported {0,0} and the\n // backend-integrity guard misread a real run as a stub. Reasoning tokens are\n // billed output (reasoning models), so they fold into the output count.\n if (type === 'done') {\n const usage = data.tokenUsage as Record<string, unknown> | undefined\n if (!usage || typeof usage !== 'object') return undefined\n const out = pickFiniteNumber(usage, ['outputTokens', 'completion_tokens', 'tokensOut'])\n const reasoning = pickFiniteNumber(usage, ['reasoningTokens'])\n const mergedOut =\n out !== undefined || reasoning !== undefined ? (out ?? 0) + (reasoning ?? 0) : undefined\n return buildLlmCall(\n {\n inputTokens: usage.inputTokens,\n outputTokens: mergedOut,\n totalCostUsd: data.totalCostUsd,\n model: data.model ?? usage.model,\n },\n agentRunName,\n )\n }\n return undefined\n}\n\nfunction buildLlmCall(\n data: Record<string, unknown>,\n agentRunName: string,\n): (RuntimeStreamEvent & { type: 'llm_call' }) | undefined {\n const tokensIn = pickFiniteNumber(data, ['tokensIn', 'inputTokens', 'prompt_tokens'])\n const tokensOut = pickFiniteNumber(data, ['tokensOut', 'outputTokens', 'completion_tokens'])\n const costUsd = pickFiniteNumber(data, ['costUsd', 'totalCostUsd', 'cost_usd', 'cost'])\n if (tokensIn === undefined && tokensOut === undefined && costUsd === undefined) {\n return undefined\n }\n const model = typeof data.model === 'string' && data.model.length > 0 ? data.model : agentRunName\n const event: RuntimeStreamEvent & { type: 'llm_call' } = {\n type: 'llm_call',\n model,\n }\n if (tokensIn !== undefined) event.tokensIn = tokensIn\n if (tokensOut !== undefined) event.tokensOut = tokensOut\n if (costUsd !== undefined) event.costUsd = costUsd\n return event\n}\n\nfunction pickFiniteNumber(data: Record<string, unknown>, keys: string[]): number | undefined {\n for (const key of keys) {\n const value = data[key]\n if (typeof value === 'number' && Number.isFinite(value)) return value\n }\n return undefined\n}\n\n/**\n * Project one `SandboxEvent` onto the `RuntimeStreamEvent` chat-UX vocabulary,\n * for runtimes that bridge a sandbox `streamPrompt` into the\n * `AgentRuntime.act` streaming contract. Returns `undefined` for events that\n * have no faithful projection — the raw stream is preserved separately for the\n * `OutputAdapter`, so an unmapped event never loses data.\n *\n * Mapped (the task-optional incremental variants — no synthesized task\n * lifecycle, no guessed tool-part shapes):\n * - `message.part.updated` text part → `text_delta`\n * - `message.part.updated` reasoning/thinking part → `reasoning_delta`\n * - cost-bearing events → `llm_call` (shared with the ledger extractor)\n *\n * The opencode backend emits incremental text as\n * `{ type: 'message.part.updated', data: { part: { type, text }, delta } }`;\n * `delta` is the increment, `part.text` the running accumulation.\n */\nexport function mapSandboxEvent(\n event: SandboxEvent,\n opts: { agentRunName?: string } = {},\n): RuntimeStreamEvent | undefined {\n if (!event || typeof event !== 'object') return undefined\n const type = String(event.type ?? '')\n const data =\n event.data && typeof event.data === 'object'\n ? (event.data as Record<string, unknown>)\n : ({} as Record<string, unknown>)\n\n if (type === 'message.part.updated') {\n const part =\n data.part && typeof data.part === 'object' ? (data.part as Record<string, unknown>) : {}\n const partType = String(part.type ?? '')\n const delta = typeof data.delta === 'string' ? data.delta : undefined\n const text = delta ?? (typeof part.text === 'string' ? part.text : undefined)\n if (text === undefined) return undefined\n if (partType === 'text') return { type: 'text_delta', text }\n if (partType === 'reasoning' || partType === 'thinking')\n return { type: 'reasoning_delta', text }\n return undefined\n }\n\n return extractLlmCallEvent(event, opts.agentRunName ?? 'agent')\n}\n","/**\n * @experimental\n *\n * `runLoop` — the topology-agnostic kernel built atop the sandbox SDK.\n *\n * Each iteration:\n * 1. `driver.plan(task, history)` → N tasks (1 = refine, N = fanout, 0 = stop)\n * 2. For each task (parallel, bounded by `maxConcurrency`):\n * a. round-robin an `AgentRunSpec` from `agentRuns`\n * b. `sandboxClient.create({ backend: { profile }, ...overrides })`\n * c. emit `loop.iteration.dispatch` with the placement\n * (`{ sibling, sandboxId }` or `{ fleet, fleetId, machineId, sandboxId }`)\n * d. iterate `box.streamPrompt(taskToPrompt(task))` and collect events\n * 3. `output.parse(events)` → typed `Output`\n * 4. `validator?.validate(output)` → `DefaultVerdict`\n * 5. Append `Iteration` to history; emit `loop.iteration.ended`\n * 6. `driver.decide(history)` → if terminal, return result + winner\n *\n * The kernel owns: iteration accounting, per-iteration timing, error\n * capture, abort propagation, concurrency cap, cost aggregation, and trace\n * emission. The kernel does NOT own: what the agent runs (sandbox SDK +\n * profile), how outputs are decoded (output adapter), how outputs are\n * scored (validator), or topology (driver).\n */\n\nimport type {\n AgentProfile,\n CreateSandboxOptions,\n SandboxEvent,\n SandboxInstance,\n} from '@tangle-network/sandbox'\nimport { ValidationError } from '../errors'\nimport { acquireSandbox } from './sandbox-acquire'\nimport { extractLlmCallEvent } from './sandbox-events'\nimport type {\n AgentRunSpec,\n Driver,\n ExecCtx,\n Iteration,\n LoopResult,\n LoopSandboxClient,\n LoopSandboxPlacement,\n LoopTraceEmitter,\n LoopTraceEvent,\n LoopWinner,\n OutputAdapter,\n Validator,\n} from './types'\n\nconst DEFAULT_MAX_ITERATIONS = 10\nconst DEFAULT_MAX_CONCURRENCY = 4\n\n/** @experimental */\nexport interface RunLoopOptions<Task, Output, Decision> {\n driver: Driver<Task, Output, Decision>\n /**\n * Single agent spec — every iteration uses this profile. Mutually\n * exclusive with `agentRuns`.\n */\n agentRun?: AgentRunSpec<Task>\n /**\n * Multiple specs for heterogeneous fanout. The kernel round-robins\n * through them when the driver plans N tasks. Mutually exclusive with\n * `agentRun`.\n */\n agentRuns?: AgentRunSpec<Task>[]\n output: OutputAdapter<Output>\n validator?: Validator<Output>\n task: Task\n ctx: ExecCtx\n /** Default 10. Hard cap on total iterations across all `plan()` rounds. */\n maxIterations?: number\n /** Default 4. In-flight worker cap within a single `plan()` batch. */\n maxConcurrency?: number\n /**\n * Pre-allocated id for trace correlation. Default = `loop-${random}`.\n * Surfaces as `runId` on every emitted `LoopTraceEvent`.\n */\n runId?: string\n /**\n * Clock override; default `Date.now`. Deterministic tests pass a\n * monotonic counter to stabilize iteration timing fields.\n */\n now?: () => number\n /**\n * Override the default winner selector (highest-valid-score, ties broken\n * by earliest iteration).\n */\n selectWinner?: (iterations: Iteration<Task, Output>[]) => LoopWinner<Task, Output> | undefined\n /**\n * Same-sandbox driver mode. Pass a setter and the kernel keeps each worker box\n * alive across the `plan()` boundary and hands the latest one here, so a\n * same-sandbox planner (`createSandboxPlanner` with `reuseBox`) can stream its\n * move INTO the worker's live box — steering from the worker's real filesystem\n * and state, not just a history summary. The kernel owns teardown: every box\n * kept alive this way is destroyed at loop end (and the setter is called with\n * `undefined` then). Without it, worker boxes are torn down per-iteration\n * (default) and a same-sandbox planner has nothing to reuse. Intended for\n * single-worker (refine) loops; under fanout the most-recent box is shared.\n */\n shareWorkerBox?: (box: SandboxInstance | undefined) => void\n}\n\n/** @experimental */\nexport async function runLoop<Task, Output, Decision>(\n options: RunLoopOptions<Task, Output, Decision>,\n): Promise<LoopResult<Task, Output, Decision>> {\n const specs = resolveAgentRuns(options)\n const maxIterations = options.maxIterations ?? DEFAULT_MAX_ITERATIONS\n if (!Number.isFinite(maxIterations) || maxIterations <= 0) {\n throw new ValidationError('runLoop: maxIterations must be > 0')\n }\n const maxConcurrency = options.maxConcurrency ?? DEFAULT_MAX_CONCURRENCY\n if (!Number.isFinite(maxConcurrency) || maxConcurrency <= 0) {\n throw new ValidationError('runLoop: maxConcurrency must be > 0')\n }\n if (!options.ctx?.sandboxClient || typeof options.ctx.sandboxClient.create !== 'function') {\n throw new ValidationError('runLoop: ctx.sandboxClient.create is required')\n }\n const now = options.now ?? Date.now\n const runId = options.runId ?? `loop-${randomSuffix()}`\n const loopStart = now()\n const driverName = options.driver.name ?? 'driver'\n const iterations: Iteration<Task, Output>[] = []\n let round = 0\n // Same-sandbox mode: worker boxes are kept alive (not torn down per-iteration)\n // so the planner can stream into the latest; the kernel destroys them at loop end.\n const ownedBoxes: SandboxInstance[] = []\n const collectBox = options.shareWorkerBox\n ? (box: SandboxInstance) => {\n ownedBoxes.push(box)\n options.shareWorkerBox?.(box)\n }\n : undefined\n\n await emitTrace(options.ctx.traceEmitter, {\n kind: 'loop.started',\n runId,\n timestamp: now(),\n payload: {\n driver: driverName,\n agentRunNames: specs.map((spec) => spec.name ?? spec.profile.name ?? 'agent'),\n maxIterations,\n maxConcurrency,\n },\n })\n\n const controller = new AbortController()\n const onOuterAbort = () => controller.abort()\n if (options.ctx.signal) {\n if (options.ctx.signal.aborted) controller.abort()\n else options.ctx.signal.addEventListener('abort', onOuterAbort, { once: true })\n }\n\n try {\n while (iterations.length < maxIterations) {\n if (controller.signal.aborted) throwAbort()\n const planned = await options.driver.plan(options.task, iterations)\n const planDesc = options.driver.describePlan?.()\n const roundIndex = round\n const baseIndex = iterations.length\n const remaining = maxIterations - iterations.length\n const slice = planned.slice(0, remaining)\n // Edge lineage: a driver may DECLARE the branch source (planner-authored\n // topology); otherwise the kernel infers it — round 0 branches from root\n // (undefined), later rounds from the best-valid (else latest) iteration so\n // far. Either way it's emitted, not guessed by the viewer.\n const parentIndex =\n planDesc?.parentIndex ?? (roundIndex === 0 ? undefined : branchPoint(iterations))\n const childIndices = slice.map((_, i) => baseIndex + i)\n await emitTrace(options.ctx.traceEmitter, {\n kind: 'loop.plan',\n runId,\n timestamp: now(),\n payload: {\n roundIndex,\n plannedCount: planned.length,\n moveKind:\n planDesc?.kind ??\n (planned.length === 0 ? 'stop' : planned.length === 1 ? 'refine' : 'fanout'),\n rationale: planDesc?.rationale,\n parentIndex,\n childIndices,\n },\n })\n round += 1\n if (planned.length === 0) break\n\n // Reserve slots up front so concurrent workers may mutate by index.\n for (let i = 0; i < slice.length; i += 1) {\n const spec = specs[(baseIndex + i) % specs.length]!\n iterations.push({\n index: baseIndex + i,\n task: slice[i] as Task,\n agentRunName: spec.name ?? spec.profile.name ?? 'agent',\n events: [],\n startedAt: now(),\n endedAt: 0,\n costUsd: 0,\n tokenUsage: { input: 0, output: 0 },\n })\n }\n\n await runBatch({\n slice,\n baseIndex,\n iterations,\n specs,\n output: options.output,\n validator: options.validator,\n maxConcurrency,\n signal: controller.signal,\n ctx: options.ctx,\n runId,\n now,\n roundIndex,\n parentIndex,\n collectBox,\n })\n\n if (controller.signal.aborted) throwAbort()\n\n const decision = await options.driver.decide(iterations)\n await emitTrace(options.ctx.traceEmitter, {\n kind: 'loop.decision',\n runId,\n timestamp: now(),\n payload: { decision: serializeDecision(decision), historyLength: iterations.length },\n })\n if (isTerminalDecision(decision)) {\n return finalize({\n options,\n decision,\n iterations,\n startMs: loopStart,\n now,\n runId,\n })\n }\n }\n\n if (iterations.length >= maxIterations) {\n // Cap reached without a terminal decision — ask the driver one more time\n // for its final state, then close out.\n const decision = await options.driver.decide(iterations)\n await emitTrace(options.ctx.traceEmitter, {\n kind: 'loop.decision',\n runId,\n timestamp: now(),\n payload: { decision: serializeDecision(decision), historyLength: iterations.length },\n })\n return finalize({ options, decision, iterations, startMs: loopStart, now, runId })\n }\n // `plan()` returned `[]` before `decide()` reached a terminal state.\n const decision = await options.driver.decide(iterations)\n await emitTrace(options.ctx.traceEmitter, {\n kind: 'loop.decision',\n runId,\n timestamp: now(),\n payload: { decision: serializeDecision(decision), historyLength: iterations.length },\n })\n return finalize({ options, decision, iterations, startMs: loopStart, now, runId })\n } finally {\n if (options.ctx.signal) options.ctx.signal.removeEventListener('abort', onOuterAbort)\n // Same-sandbox mode kept worker boxes alive across plan() so the planner could\n // stream into them — the kernel owns their teardown, so destroy them all now.\n for (const b of ownedBoxes) await destroySandboxSafe(b)\n if (options.shareWorkerBox) options.shareWorkerBox(undefined)\n }\n}\n\ninterface RunBatchArgs<Task, Output> {\n slice: Task[]\n baseIndex: number\n iterations: Iteration<Task, Output>[]\n specs: AgentRunSpec<Task>[]\n output: OutputAdapter<Output>\n validator: Validator<Output> | undefined\n maxConcurrency: number\n signal: AbortSignal\n ctx: ExecCtx\n runId: string\n now: () => number\n /** Plan round these iterations belong to — stamped as `groupId`. */\n roundIndex: number\n /** Iteration this round branched from — stamped as `parentIndex`. */\n parentIndex?: number\n /**\n * Same-sandbox mode: when set, a finished iteration's box is handed here\n * (kept alive for the planner) instead of being torn down. `undefined` =\n * default per-iteration teardown.\n */\n collectBox?: (box: SandboxInstance) => void\n}\n\nasync function runBatch<Task, Output>(args: RunBatchArgs<Task, Output>) {\n const queue = args.slice.map((task, offset) => ({ task, index: args.baseIndex + offset }))\n const inflight = new Set<Promise<void>>()\n while (queue.length > 0 || inflight.size > 0) {\n while (inflight.size < args.maxConcurrency && queue.length > 0) {\n const item = queue.shift()!\n const p = executeIteration({ ...args, item }).finally(() => inflight.delete(p))\n inflight.add(p)\n }\n if (inflight.size === 0) break\n await Promise.race(inflight)\n }\n}\n\ninterface ExecuteIterationArgs<Task, Output> extends RunBatchArgs<Task, Output> {\n item: { task: Task; index: number }\n}\n\nasync function executeIteration<Task, Output>(args: ExecuteIterationArgs<Task, Output>) {\n const slot = args.iterations[args.item.index]\n if (!slot)\n throw new ValidationError(`runLoop: missing iteration slot at index ${args.item.index}`)\n const spec = args.specs[args.item.index % args.specs.length]\n if (!spec) throw new ValidationError('runLoop: no AgentRunSpec available for iteration')\n slot.startedAt = args.now()\n slot.agentRunName = spec.name ?? spec.profile.name ?? 'agent'\n\n await emitTrace(args.ctx.traceEmitter, {\n kind: 'loop.iteration.started',\n runId: args.runId,\n timestamp: args.now(),\n payload: {\n iterationIndex: args.item.index,\n agentRunName: slot.agentRunName,\n taskHash: hashJson(args.item.task),\n groupId: args.roundIndex,\n parentIndex: args.parentIndex,\n },\n })\n\n let box: SandboxInstance | undefined\n try {\n box = await createSandboxForSpec(args.ctx.sandboxClient, spec, args.signal)\n const placement = describeSandboxPlacement(args.ctx.sandboxClient, box)\n await emitTrace(args.ctx.traceEmitter, {\n kind: 'loop.iteration.dispatch',\n runId: args.runId,\n timestamp: args.now(),\n payload: {\n iterationIndex: args.item.index,\n agentRunName: slot.agentRunName,\n placement: placement.kind,\n sandboxId: placement.sandboxId,\n fleetId: placement.fleetId,\n machineId: placement.machineId,\n groupId: args.roundIndex,\n parentIndex: args.parentIndex,\n },\n })\n const message = spec.taskToPrompt(args.item.task)\n const events: SandboxEvent[] = []\n for await (const event of box.streamPrompt(message, { signal: args.signal })) {\n events.push(event)\n const llmCall = extractLlmCallEvent(event, slot.agentRunName)\n if (llmCall) {\n slot.costUsd += llmCall.costUsd ?? 0\n slot.tokenUsage.input += llmCall.tokensIn ?? 0\n slot.tokenUsage.output += llmCall.tokensOut ?? 0\n args.ctx.runHandle?.observe(llmCall)\n }\n }\n slot.events = events\n slot.output = args.output.parse(events)\n if (args.validator) {\n slot.verdict = await args.validator.validate(slot.output, {\n iteration: args.item.index,\n signal: args.signal,\n traceEmitter: args.ctx.traceEmitter,\n })\n }\n } catch (err) {\n slot.error = err instanceof Error ? err : new Error(String(err))\n } finally {\n slot.endedAt = args.now()\n await emitTrace(args.ctx.traceEmitter, {\n kind: 'loop.iteration.ended',\n runId: args.runId,\n timestamp: args.now(),\n payload: {\n iterationIndex: args.item.index,\n agentRunName: slot.agentRunName,\n outputHash: slot.output !== undefined ? hashJson(slot.output) : undefined,\n verdict: slot.verdict,\n error: slot.error?.message,\n costUsd: slot.costUsd,\n durationMs: slot.endedAt - slot.startedAt,\n tokenUsage:\n slot.tokenUsage.input || slot.tokenUsage.output ? { ...slot.tokenUsage } : undefined,\n groupId: args.roundIndex,\n parentIndex: args.parentIndex,\n outputPreview: slot.output !== undefined ? previewOutput(slot.output) : undefined,\n },\n })\n // The loop owns the per-shot box lifecycle. Default: tear it down now so\n // sandboxes don't leak. Same-sandbox mode: hand it to the kernel to keep\n // alive for the planner (it tears these down at loop end instead).\n if (args.collectBox && box) args.collectBox(box)\n else await destroySandboxSafe(box)\n }\n}\n\n/**\n * Best-effort sandbox teardown. A failed delete must never surface as a loop\n * error, and instances without a `delete` (the loop's test fakes) are skipped.\n */\nexport async function destroySandboxSafe(box: SandboxInstance | undefined): Promise<void> {\n if (!box || typeof (box as { delete?: unknown }).delete !== 'function') return\n try {\n await box.delete()\n } catch {\n // ignore — platform reaps on expiry\n }\n}\n\n/**\n * Branch point for a new round — the iteration a later round descends from.\n * Highest-valid-score iteration so far; ties + no-valid fall back to the latest\n * index. Inferred (not driver-declared), so refine renders as a chain and\n * fanout→refine chains off the fanout winner.\n */\nfunction branchPoint<Task, Output>(\n iterations: ReadonlyArray<Iteration<Task, Output>>,\n): number | undefined {\n if (iterations.length === 0) return undefined\n let best = iterations.length - 1\n let bestScore = -Infinity\n for (const iter of iterations) {\n if (iter.verdict?.valid !== true) continue\n const score = iter.verdict.score ?? 0\n if (score > bestScore) {\n bestScore = score\n best = iter.index\n }\n }\n return best\n}\n\n/** Bounded string preview of a parsed output for a viewer's drawer — never the\n * full payload. JSON when serializable, else `String()`, truncated to 280. */\nfunction previewOutput(output: unknown): string {\n let s: string\n try {\n s = typeof output === 'string' ? output : (JSON.stringify(output) ?? String(output))\n } catch {\n s = String(output)\n }\n return s.length > 280 ? `${s.slice(0, 280)}…` : s\n}\n\nexport function describeSandboxPlacement(\n client: LoopSandboxClient,\n box: SandboxInstance,\n): LoopSandboxPlacement {\n if (typeof client.describePlacement === 'function') {\n try {\n const result = client.describePlacement(box)\n if (\n result &&\n typeof result === 'object' &&\n (result.kind === 'sibling' || result.kind === 'fleet')\n ) {\n return {\n kind: result.kind,\n sandboxId: result.sandboxId ?? readSandboxId(box),\n fleetId: result.fleetId,\n machineId: result.machineId,\n }\n }\n } catch {\n // Adapter bug must not corrupt the iteration; fall through to default.\n }\n }\n return { kind: 'sibling', sandboxId: readSandboxId(box) }\n}\n\nfunction readSandboxId(box: SandboxInstance): string | undefined {\n const raw = (box as unknown as { id?: unknown }).id\n return typeof raw === 'string' && raw.length > 0 ? raw : undefined\n}\n\n/**\n * Instantiate a sandbox for an `AgentRunSpec`: sets `backend.profile` to the\n * spec's profile (inferring the backend type when the spec doesn't override\n * it) and merges `sandboxOverrides`. Shared by the loop kernel and the\n * `AgentRuntime.act` sandbox bridge so both boot the sandbox identically.\n */\nexport async function createSandboxForSpec<Task>(\n client: LoopSandboxClient,\n spec: AgentRunSpec<Task>,\n signal: AbortSignal,\n): Promise<SandboxInstance> {\n const overrides = spec.sandboxOverrides ?? {}\n const overrideBackend = overrides.backend\n const opts: CreateSandboxOptions = {\n ...overrides,\n backend: {\n type: overrideBackend?.type ?? inferBackendType(spec.profile),\n profile: spec.profile satisfies AgentProfile,\n ...(overrideBackend?.model ? { model: overrideBackend.model } : {}),\n ...(overrideBackend?.server ? { server: overrideBackend.server } : {}),\n },\n }\n // Cold-start-resilient acquire: a slow scale-from-zero create (node boot +\n // host-agent registration) can't surface as a failure — readiness is observed\n // from sandbox status, and a gateway-timed-out create is recovered by lookup.\n if (signal.aborted) throwAbort()\n return acquireSandbox(client, opts, { signal })\n}\n\nfunction inferBackendType(\n profile: AgentProfile,\n): CreateSandboxOptions['backend'] extends infer B\n ? B extends { type: infer T }\n ? T\n : never\n : never {\n // The sandbox SDK accepts profile-driven backend selection by name. When the\n // profile has no explicit hint we fall through to the SDK's default\n // ('opencode' on the platform side). Returning a literal here would lie\n // about provenance — let the SDK pick.\n type BackendType = NonNullable<CreateSandboxOptions['backend']>['type']\n const explicit = profile.metadata?.backendType\n if (typeof explicit === 'string') return explicit as BackendType\n return 'opencode' as BackendType\n}\n\ninterface FinalizeArgs<Task, Output, Decision> {\n options: RunLoopOptions<Task, Output, Decision>\n decision: Decision\n iterations: Iteration<Task, Output>[]\n startMs: number\n now: () => number\n runId: string\n}\n\nfunction finalize<Task, Output, Decision>(\n args: FinalizeArgs<Task, Output, Decision>,\n): LoopResult<Task, Output, Decision> {\n const winner = (args.options.selectWinner ?? defaultSelectWinner)(args.iterations)\n const costUsd = args.iterations.reduce((sum, iter) => sum + (iter.costUsd || 0), 0)\n const tokenUsage = args.iterations.reduce(\n (acc, iter) => {\n acc.input += iter.tokenUsage?.input ?? 0\n acc.output += iter.tokenUsage?.output ?? 0\n return acc\n },\n { input: 0, output: 0 },\n )\n const result: LoopResult<Task, Output, Decision> = {\n decision: args.decision,\n iterations: args.iterations,\n winner,\n durationMs: args.now() - args.startMs,\n costUsd,\n tokenUsage,\n }\n void emitTrace(args.options.ctx.traceEmitter, {\n kind: 'loop.ended',\n runId: args.runId,\n timestamp: args.now(),\n payload: {\n winnerIterationIndex: winner?.iterationIndex,\n totalCostUsd: costUsd,\n durationMs: result.durationMs,\n iterations: args.iterations.length,\n },\n })\n return result\n}\n\nfunction defaultSelectWinner<Task, Output>(\n iterations: Iteration<Task, Output>[],\n): LoopWinner<Task, Output> | undefined {\n const candidates = iterations.filter((iter) => iter.output !== undefined && !iter.error)\n if (candidates.length === 0) return undefined\n const valid = candidates.filter((iter) => iter.verdict?.valid === true)\n const pool = valid.length > 0 ? valid : candidates\n const sorted = [...pool].sort(\n (a, b) => (b.verdict?.score ?? 0) - (a.verdict?.score ?? 0) || a.index - b.index,\n )\n const top = sorted[0]\n if (!top || top.output === undefined) return undefined\n return {\n task: top.task,\n output: top.output,\n verdict: top.verdict,\n iterationIndex: top.index,\n agentRunName: top.agentRunName,\n }\n}\n\nfunction resolveAgentRuns<Task, Output, Decision>(\n options: RunLoopOptions<Task, Output, Decision>,\n): AgentRunSpec<Task>[] {\n if (options.agentRun && options.agentRuns) {\n throw new ValidationError('runLoop: pass exactly one of `agentRun` or `agentRuns`')\n }\n if (options.agentRun) return [options.agentRun]\n if (options.agentRuns && options.agentRuns.length > 0) return options.agentRuns\n throw new ValidationError('runLoop: `agentRun` or non-empty `agentRuns` is required')\n}\n\nfunction isTerminalDecision(decision: unknown): boolean {\n return (\n decision === 'stop' || decision === 'pick-winner' || decision === 'fail' || decision === 'done'\n )\n}\n\nfunction serializeDecision(decision: unknown): string {\n if (typeof decision === 'string') return decision\n if (decision === null || decision === undefined) return 'null'\n try {\n return JSON.stringify(decision)\n } catch {\n return String(decision)\n }\n}\n\nasync function emitTrace(\n emitter: LoopTraceEmitter | undefined,\n event: LoopTraceEvent,\n): Promise<void> {\n if (!emitter) return\n await emitter.emit(event)\n}\n\nfunction randomSuffix(len = 8): string {\n return Math.random()\n .toString(36)\n .slice(2, 2 + len)\n}\n\nfunction throwAbort(): never {\n const err = new Error('aborted')\n err.name = 'AbortError'\n throw err\n}\n\n/**\n * Stable hash for the trace payload. Not cryptographic — only used so\n * downstream eval pipelines can group iterations whose task / output is the\n * same. Bare structural hash; non-JSON values stringify via their `toString`.\n */\nfunction hashJson(value: unknown): string {\n let str: string\n try {\n str = JSON.stringify(value) ?? String(value)\n } catch {\n str = String(value)\n }\n // FNV-1a 32-bit — branch-free, dependency-free, good enough for grouping.\n let h = 0x811c9dc5\n for (let i = 0; i < str.length; i += 1) {\n h ^= str.charCodeAt(i)\n h = Math.imul(h, 0x01000193)\n }\n return (h >>> 0).toString(16).padStart(8, '0')\n}\n","/**\n * `loopDispatch` — turn `runLoop` into an agent-eval campaign dispatch.\n *\n * Without this adapter a consumer wiring `runLoop` into `runProfileMatrix` /\n * `runCampaign` has to, by hand, every time: (a) build an `ExecCtx` with a\n * sandbox client, (b) adapt the campaign `DispatchContext.trace` into a\n * `LoopTraceEmitter` (or lose all loop trace correlation), and (c) remember to\n * forward the loop's cost + tokens via `ctx.cost` (forgetting it yields a\n * `{0,0}` cell the backend-integrity guard reads as a stub). Three foot-guns,\n * the third silent. The fleet's products skipped (c) and fell back to a\n * `workerRecords[]` side-channel — the exact anti-pattern the substrate exists\n * to kill.\n *\n * `loopDispatch` collapses all three into one typed call:\n *\n * const dispatch = loopDispatch({\n * sandboxClient,\n * toLoopOptions: (scenario, profile) => ({ driver, agentRun, output, validator, task }),\n * })\n * await runProfileMatrix({ profiles, scenarios, dispatch, judges, commitSha })\n *\n * Usage is reported automatically; trace events are forwarded automatically;\n * the ctx is built automatically. The seam becomes impossible to mis-wire.\n *\n * Typed structurally against the campaign `DispatchContext` (imported type-only\n * from `@tangle-network/agent-eval/campaign`) — a downward dependency, never an\n * inversion.\n */\n\n// agent-eval's AgentProfile (the eval-harness unit of variation, `model: string`)\n// — NOT sandbox's AgentProfile. ProfileDispatchFn is keyed on the former.\nimport type { AgentProfile } from '@tangle-network/agent-eval'\nimport type {\n CampaignTraceWriter,\n DispatchContext,\n DispatchFn,\n ProfileDispatchFn,\n Scenario,\n} from '@tangle-network/agent-eval/campaign'\nimport { reportLoopUsage } from './report-usage'\nimport { type RunLoopOptions, runLoop } from './run-loop'\nimport type { LoopResult, LoopSandboxClient, LoopTraceEmitter } from './types'\n\n/** runLoop options minus the `ctx` (loopDispatch builds the ctx). */\nexport type LoopOptionsForDispatch<Task, Output, Decision> = Omit<\n RunLoopOptions<Task, Output, Decision>,\n 'ctx'\n>\n\nexport interface LoopDispatchOptions<\n Task,\n Output,\n Decision,\n TScenario extends Scenario,\n TArtifact,\n> {\n /** Sandbox client used for every cell's `runLoop`. Supplied once. */\n sandboxClient: LoopSandboxClient\n /** Build the per-cell runLoop options from the scenario (+ profile, when\n * used with `runProfileMatrix`). */\n toLoopOptions: (\n scenario: TScenario,\n profile: AgentProfile,\n ) => LoopOptionsForDispatch<Task, Output, Decision>\n /** Map the finished loop to the artifact the judges score. Default:\n * `result.winner?.output`. A loop with no winner yields `undefined` (judges\n * skip the cell) — but the loop's token usage is STILL reported, so the\n * integrity guard sees real activity. */\n toArtifact?: (result: LoopResult<Task, Output, Decision>) => TArtifact\n /** Forward `loop.*` trace events into the campaign's scoped trace so loop\n * spans correlate with the cell. Default true. */\n forwardTrace?: boolean\n /** Cost-meter source label for the loop's spend. Default `'loop'`. */\n costSource?: string\n}\n\n/** Bridge a campaign `DispatchContext.trace` to a `LoopTraceEmitter` so every\n * `loop.*` event lands as a span under the cell's scoped trace. */\nfunction campaignTraceToLoopEmitter(trace: CampaignTraceWriter): LoopTraceEmitter {\n return {\n emit(event) {\n trace\n .span(event.kind, { runId: event.runId, timestamp: event.timestamp, ...event.payload })\n .end()\n },\n }\n}\n\nasync function runLoopForCell<Task, Output, Decision, TScenario extends Scenario, TArtifact>(\n opts: LoopDispatchOptions<Task, Output, Decision, TScenario, TArtifact>,\n scenario: TScenario,\n profile: AgentProfile,\n ctx: DispatchContext,\n): Promise<TArtifact> {\n const loopOptions = opts.toLoopOptions(scenario, profile)\n const result = await runLoop<Task, Output, Decision>({\n ...loopOptions,\n ctx: {\n sandboxClient: opts.sandboxClient,\n signal: ctx.signal,\n traceEmitter: opts.forwardTrace === false ? undefined : campaignTraceToLoopEmitter(ctx.trace),\n },\n })\n reportLoopUsage(ctx.cost, result, opts.costSource ?? 'loop')\n const toArtifact =\n opts.toArtifact ?? ((r: LoopResult<Task, Output, Decision>) => r.winner?.output as TArtifact)\n return toArtifact(result)\n}\n\n/**\n * Adapter for `runProfileMatrix` (profile is an axis). Returns a\n * `ProfileDispatchFn` that runs `runLoop` per (profile, scenario) cell and\n * reports usage automatically.\n */\nexport function loopDispatch<Task, Output, Decision, TScenario extends Scenario, TArtifact>(\n opts: LoopDispatchOptions<Task, Output, Decision, TScenario, TArtifact>,\n): ProfileDispatchFn<TScenario, TArtifact> {\n return (profile, scenario, ctx) => runLoopForCell(opts, scenario, profile, ctx)\n}\n\n/**\n * Adapter for `runCampaign` (no profile axis). `toLoopOptions` receives only\n * the scenario; the `profile` passed to the shared core is a stable sentinel\n * so a single `runLoop` config is reused across cells.\n */\nexport function loopCampaignDispatch<Task, Output, Decision, TScenario extends Scenario, TArtifact>(\n opts: Omit<LoopDispatchOptions<Task, Output, Decision, TScenario, TArtifact>, 'toLoopOptions'> & {\n toLoopOptions: (scenario: TScenario) => LoopOptionsForDispatch<Task, Output, Decision>\n },\n): DispatchFn<TScenario, TArtifact> {\n const profileSentinel = { id: 'loop-campaign', model: 'n/a@loop-campaign' } as AgentProfile\n const profiled: LoopDispatchOptions<Task, Output, Decision, TScenario, TArtifact> = {\n ...opts,\n toLoopOptions: (scenario) => opts.toLoopOptions(scenario),\n }\n return (scenario, ctx) => runLoopForCell(profiled, scenario, profileSentinel, ctx)\n}\n"],"mappings":"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","names":["randomSuffix","decision"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/mcp/executor.ts","../src/mcp/delegates.ts"],"sourcesContent":["/**\n * @experimental\n *\n * Delegation executors — the layer between MCP delegates and the sandbox\n * substrate. Each executor exposes a {@link LoopSandboxClient} the kernel\n * consumes plus a placement tag so the trace pipeline can correlate workers\n * with their physical placement.\n *\n * Two implementations ship in-box:\n *\n * - {@link createSiblingSandboxExecutor} — every delegation spawns a fresh\n * sandbox sibling to the caller. Default when the MCP server runs as a\n * standalone CLI mounted outside a fleet.\n *\n * - {@link createFleetWorkspaceExecutor} — delegations dispatch onto machines\n * in the caller's existing fleet so worker diffs land directly on the\n * caller's filesystem (the fleet's shared workspace). Selected when the\n * parent sandbox passes `TANGLE_FLEET_ID` into the MCP server's env.\n */\n\nimport type { CreateSandboxOptions, SandboxInstance } from '@tangle-network/sandbox'\nimport type { LoopSandboxClient, LoopSandboxPlacement } from '../loops'\n\n/** @experimental */\nexport interface DelegationExecutor {\n /** Sandbox client the kernel calls. Returned with `describePlacement` set. */\n readonly client: LoopSandboxClient\n /** Best-effort one-liner used in stderr boot logs and diagnostics. */\n describe(): string\n}\n\n/** @experimental */\nexport interface SiblingSandboxExecutorOptions {\n client: LoopSandboxClient\n}\n\n/**\n * Wrap a raw sandbox SDK client so the kernel emits\n * `loop.iteration.dispatch` events with `{ placement: 'sibling', sandboxId }`.\n *\n * The returned client `.create()` delegates to the underlying client; the\n * only added behavior is a `describePlacement` tag the kernel reads.\n *\n * @experimental\n */\nexport function createSiblingSandboxExecutor(\n options: SiblingSandboxExecutorOptions,\n): DelegationExecutor {\n const underlying = options.client\n const client: LoopSandboxClient = {\n create(opts?: CreateSandboxOptions): Promise<SandboxInstance> {\n return underlying.create(opts)\n },\n describePlacement(box: SandboxInstance): LoopSandboxPlacement {\n return { kind: 'sibling', sandboxId: readId(box) }\n },\n }\n return {\n client,\n describe(): string {\n return 'sibling-sandbox (each delegation = fresh sandbox via client.create)'\n },\n }\n}\n\n/**\n * Minimal `SandboxFleet` surface the fleet executor calls. Declared\n * structurally so tests can pass an in-memory stub without instantiating the\n * sandbox SDK.\n *\n * @experimental\n */\nexport interface FleetHandle {\n readonly fleetId: string\n /** Machine ids in dispatch-eligible order. The executor round-robins. */\n readonly ids: ReadonlyArray<string>\n /** Resolve a machine id to its `SandboxInstance` — that machine is mounted\n * on the fleet's shared workspace, so any diff the worker writes lands on\n * every other fleet machine's filesystem too. */\n sandbox(machineId: string): Promise<SandboxInstance>\n}\n\n/** @experimental */\nexport interface FleetWorkspaceExecutorOptions {\n fleet: FleetHandle\n /**\n * Override the machine-selection policy. Default = round-robin across\n * `fleet.ids`, skipping the optional `excludeMachineIds` set (typically the\n * coordinator machine the MCP server is running on).\n */\n selectMachine?: (call: { callIndex: number; ids: ReadonlyArray<string> }) => string\n /**\n * Machine ids to skip during default round-robin. Set to the caller's own\n * machineId so workers don't compete with the orchestrator on the same VM.\n */\n excludeMachineIds?: ReadonlyArray<string>\n}\n\n/**\n * Build an executor that resolves each delegated iteration to an existing\n * machine in `fleet`. The fleet's shared-workspace policy means the worker\n * machine sees the caller's filesystem — diffs land in-place with no\n * cross-sandbox copy step.\n *\n * @experimental\n */\nexport function createFleetWorkspaceExecutor(\n options: FleetWorkspaceExecutorOptions,\n): DelegationExecutor {\n const fleet = options.fleet\n const exclude = new Set(options.excludeMachineIds ?? [])\n let callIndex = 0\n // machineId-by-sandboxId, populated as we resolve machines so\n // `describePlacement` can recover the assignment from the SandboxInstance\n // the kernel hands back.\n const placementBySandboxId = new Map<string, { machineId: string }>()\n\n const client: LoopSandboxClient = {\n async create(): Promise<SandboxInstance> {\n const ids = fleet.ids.filter((id) => !exclude.has(id))\n if (ids.length === 0) {\n throw new Error(\n `agent-runtime: fleet ${fleet.fleetId} has no eligible worker machines (ids=[${fleet.ids.join(',')}], excluded=[${[...exclude].join(',')}])`,\n )\n }\n const selector = options.selectMachine\n const machineId = selector ? selector({ callIndex, ids }) : ids[callIndex % ids.length]\n callIndex += 1\n if (typeof machineId !== 'string' || machineId.length === 0) {\n throw new Error('agent-runtime: fleet executor selectMachine returned an empty machine id')\n }\n const box = await fleet.sandbox(machineId)\n const sandboxId = readId(box)\n if (sandboxId) placementBySandboxId.set(sandboxId, { machineId })\n return box\n },\n describePlacement(box: SandboxInstance): LoopSandboxPlacement {\n const sandboxId = readId(box)\n const recorded = sandboxId ? placementBySandboxId.get(sandboxId) : undefined\n return {\n kind: 'fleet',\n sandboxId,\n fleetId: fleet.fleetId,\n machineId: recorded?.machineId,\n }\n },\n }\n\n return {\n client,\n describe(): string {\n const excluded = exclude.size > 0 ? ` (excluded=[${[...exclude].join(',')}])` : ''\n return `fleet-workspace (fleetId=${fleet.fleetId}, machines=[${fleet.ids.join(',')}]${excluded})`\n },\n }\n}\n\nfunction readId(box: SandboxInstance): string | undefined {\n const raw = (box as unknown as { id?: unknown }).id\n return typeof raw === 'string' && raw.length > 0 ? raw : undefined\n}\n","/**\n * @experimental\n *\n * Delegate factories — the layer between MCP tool handlers and the\n * underlying `runLoop` runners.\n *\n * The MCP server is profile-agnostic: it owns the task queue + feedback\n * store + transport. Each `*Delegate` is the closure that the queue\n * invokes when a task runs. Consumers can override either delegate to\n * inject custom drivers, mocks, fleet-aware dispatchers, etc.\n *\n * The default coder delegate is wired here because we own\n * `coderProfile` / `multiHarnessCoderFanout`. The default researcher\n * delegate is **not** wired in this file — `agent-knowledge` cannot be\n * imported from `agent-runtime` without inducing a cycle. Consumers\n * pass `researcherDelegate` explicitly when constructing the server.\n */\n\nimport type { Iteration, LoopSandboxClient, LoopTraceEmitter } from '../loops'\nimport { runLoop } from '../loops'\nimport { type CoderOutput, coderProfile, multiHarnessCoderFanout } from '../profiles/coder'\nimport { createSiblingSandboxExecutor, type DelegationExecutor } from './executor'\nimport type {\n CoderTask,\n DelegateCodeArgs,\n DelegateResearchArgs,\n DelegationProgress,\n ResearchOutputShape,\n} from './types'\n\n/** @experimental */\nexport interface DelegateRunCtx {\n signal: AbortSignal\n report(progress: DelegationProgress): void\n}\n\n/** @experimental */\nexport type CoderDelegate = (\n args: DelegateCodeArgs,\n ctx: DelegateRunCtx,\n) => Promise<import('../profiles/coder').CoderOutput>\n\n/** @experimental */\nexport type ResearcherDelegate = (\n args: DelegateResearchArgs,\n ctx: DelegateRunCtx,\n) => Promise<ResearchOutputShape>\n\n/** @experimental Structured review verdict over a coder candidate. */\nexport interface CoderReview {\n /** Gate: only approved candidates are eligible to win. */\n approved: boolean\n /** Reviewer's recommendation — surfaced in traces. */\n recommendation: 'ship' | 'approve-with-nits' | 'changes-requested' | 'reject'\n /** Readiness 0..1, used by the `highest-readiness` winner-selection strategy. */\n readiness: number\n notes?: string\n}\n\n/**\n * @experimental\n *\n * Optional adversarial reviewer over a coder candidate that already passed\n * mechanical validation (tests/typecheck/forbidden/diff/no-op/secrets). Folded\n * from the ai-trading-blueprint delegation MCP: a candidate is only eligible to\n * win if the reviewer approves it. The reviewer is the consumer's seam — an LLM\n * judge, a `pnpm review` command, anything returning a `CoderReview`.\n */\nexport type CoderReviewer = (\n output: import('../profiles/coder').CoderOutput,\n task: CoderTask,\n ctx: { signal: AbortSignal },\n) => Promise<CoderReview> | CoderReview\n\n/**\n * @experimental Winner-selection strategy among validated (+ reviewed)\n * candidates. `highest-readiness` requires a `reviewer`. Default `highest-score`\n * (the kernel's behavior — preserves backward compatibility).\n */\nexport type CoderWinnerSelection =\n | 'highest-score'\n | 'smallest-diff'\n | 'highest-readiness'\n | 'first-approved'\n\n/** @experimental */\nexport interface CreateDefaultCoderDelegateOptions {\n /**\n * Execution placement. Pass a {@link DelegationExecutor} (sibling or fleet)\n * to control where worker iterations land. `sandboxClient` is a\n * convenience shorthand that wraps the client in a sibling executor — pass\n * one or the other, not both.\n */\n executor?: DelegationExecutor\n /**\n * Convenience shorthand for sibling placement. Equivalent to\n * `executor: createSiblingSandboxExecutor({ client: sandboxClient })`.\n */\n sandboxClient?: LoopSandboxClient\n /** Default `['claude-code', 'codex', 'opencode/zai-coding-plan/glm-5.1']` when variants > 1. */\n fanoutHarnesses?: string[]\n /** Hard cap on the kernel's per-batch concurrency. Default 4. */\n maxConcurrency?: number\n /**\n * Optional adversarial reviewer. When set, a candidate must pass mechanical\n * validation AND `reviewer.approved` to be eligible to win — empty/secret/\n * test-failing patches are already gone; this catches the \"compiles + passes\n * but wrong/unsafe\" class the deterministic validator can't see.\n */\n reviewer?: CoderReviewer\n /** Winner-selection strategy among eligible candidates. Default `highest-score`. */\n winnerSelection?: CoderWinnerSelection\n /**\n * Loop trace emitter forwarded into every delegated `runLoop`. Wire\n * `createPropagatingTraceEmitter(readTraceContextFromEnv())` here (the bin\n * does) so delegated build-loops export their topology spans to the OTLP /\n * Tangle Intelligence sink when `OTEL_EXPORTER_OTLP_ENDPOINT` is set — and\n * are a cheap no-op when it isn't. Configurable by construction.\n */\n traceEmitter?: LoopTraceEmitter\n}\n\n/**\n * Build a coder delegate that drives `runLoop` against the project's\n * sandbox client + coder profile. When `args.variants > 1` it switches\n * to the multi-harness fanout topology.\n *\n * @experimental\n */\nexport function createDefaultCoderDelegate(\n options: CreateDefaultCoderDelegateOptions,\n): CoderDelegate {\n const executor = resolveExecutor(options)\n const sandboxClient = executor.client\n const fanoutHarnesses = options.fanoutHarnesses\n const maxConcurrency = options.maxConcurrency ?? 4\n const traceEmitter = options.traceEmitter\n return async (args, ctx) => {\n const task: CoderTask = {\n goal: buildCoderGoal(args),\n repoRoot: args.repoRoot,\n testCmd: args.config?.testCmd,\n typecheckCmd: args.config?.typecheckCmd,\n forbiddenPaths: args.config?.forbiddenPaths,\n maxDiffLines: args.config?.maxDiffLines,\n }\n const variants = Math.max(1, Math.trunc(args.variants ?? 1))\n ctx.report({ iteration: 0, phase: 'starting' })\n if (variants <= 1) {\n const { agentRunSpec, output, validator } = coderProfile({ task })\n const result = await runLoop({\n driver: singleShotDriver,\n agentRun: agentRunSpec,\n output,\n validator,\n task,\n ctx: { sandboxClient, signal: ctx.signal, ...(traceEmitter ? { traceEmitter } : {}) },\n maxIterations: 1,\n maxConcurrency,\n })\n const chosen = await pickCoderWinner({\n iterations: result.iterations,\n reviewer: options.reviewer,\n selection: options.winnerSelection ?? 'highest-score',\n task,\n signal: ctx.signal,\n })\n if (!chosen) throw new Error(noWinnerMessage(options.reviewer))\n ctx.report({ iteration: 1, phase: 'completed' })\n return chosen\n }\n const fanout = multiHarnessCoderFanout(\n fanoutHarnesses && fanoutHarnesses.length > 0\n ? { harnesses: fanoutHarnesses.slice(0, variants) }\n : { harnesses: undefined },\n )\n const agentRuns = fanout.agentRuns.slice(0, variants)\n const result = await runLoop({\n driver: fanout.driver,\n agentRuns,\n output: fanout.output,\n validator: fanout.validator,\n task,\n ctx: { sandboxClient, signal: ctx.signal, ...(traceEmitter ? { traceEmitter } : {}) },\n maxIterations: variants,\n maxConcurrency: Math.min(maxConcurrency, variants),\n })\n const chosen = await pickCoderWinner({\n iterations: result.iterations,\n reviewer: options.reviewer,\n selection: options.winnerSelection ?? 'highest-score',\n task,\n signal: ctx.signal,\n })\n if (!chosen) throw new Error(noWinnerMessage(options.reviewer))\n ctx.report({ iteration: agentRuns.length, phase: 'completed' })\n return chosen\n }\n}\n\ninterface PickCoderWinnerArgs {\n iterations: ReadonlyArray<Iteration<CoderTask, CoderOutput>>\n reviewer: CoderReviewer | undefined\n selection: CoderWinnerSelection\n task: CoderTask\n signal: AbortSignal\n}\n\ninterface CoderCandidate {\n index: number\n output: CoderOutput\n score: number\n readiness: number\n}\n\n/**\n * Pick the winning coder candidate from a finished loop's iterations:\n * 1. keep only mechanically-VALID candidates (the validator already gated\n * tests/typecheck/forbidden/diff/no-op/secrets),\n * 2. if a `reviewer` is wired, keep only those it APPROVES,\n * 3. select among survivors by the chosen strategy.\n * Returns `undefined` when nothing survives — the delegate fails loud.\n */\nasync function pickCoderWinner(args: PickCoderWinnerArgs): Promise<CoderOutput | undefined> {\n const valid: CoderCandidate[] = []\n for (const iter of args.iterations) {\n if (iter.output === undefined || iter.error || iter.verdict?.valid !== true) continue\n valid.push({\n index: iter.index,\n output: iter.output,\n score: iter.verdict.score ?? 0,\n readiness: iter.verdict.score ?? 0,\n })\n }\n if (valid.length === 0) return undefined\n\n let eligible = valid\n if (args.reviewer) {\n eligible = []\n for (const c of valid) {\n const review = await args.reviewer(c.output, args.task, { signal: args.signal })\n if (review.approved) eligible.push({ ...c, readiness: review.readiness })\n }\n if (eligible.length === 0) return undefined\n }\n\n return selectCoderCandidate(eligible, args.selection).output\n}\n\n/** Apply the winner-selection strategy; ties broken by earliest iteration. */\nfunction selectCoderCandidate(\n candidates: CoderCandidate[],\n selection: CoderWinnerSelection,\n): CoderCandidate {\n const diffLines = (c: CoderCandidate) =>\n c.output.diffStats.insertions + c.output.diffStats.deletions\n const sorted = [...candidates].sort((a, b) => {\n switch (selection) {\n case 'smallest-diff':\n return diffLines(a) - diffLines(b) || a.index - b.index\n case 'highest-readiness':\n return b.readiness - a.readiness || a.index - b.index\n case 'first-approved':\n return a.index - b.index\n default:\n return b.score - a.score || a.index - b.index\n }\n })\n return sorted[0]!\n}\n\nfunction noWinnerMessage(reviewer: CoderReviewer | undefined): string {\n return reviewer\n ? 'coder delegate: no candidate passed validation + review'\n : 'coder delegate: no candidate passed validation'\n}\n\nfunction buildCoderGoal(args: DelegateCodeArgs): string {\n if (!args.contextHint) return args.goal\n return [args.goal, '', '## Context', args.contextHint].join('\\n')\n}\n\nfunction resolveExecutor(options: CreateDefaultCoderDelegateOptions): DelegationExecutor {\n if (options.executor && options.sandboxClient) {\n throw new Error('createDefaultCoderDelegate: pass exactly one of `executor` or `sandboxClient`')\n }\n if (options.executor) return options.executor\n if (options.sandboxClient) {\n return createSiblingSandboxExecutor({ client: options.sandboxClient })\n }\n throw new Error('createDefaultCoderDelegate: `executor` or `sandboxClient` is required')\n}\n\n/**\n * Single-shot driver — plan one task on iteration 0, stop after one\n * iteration. Used by the coder delegate when `variants <= 1`. Keeps the\n * runLoop kernel-level accounting (timing, cost, trace emission) while\n * skipping fanout/refine topology overhead.\n */\nconst singleShotDriver = {\n name: 'mcp-single-shot',\n async plan<Task>(task: Task, history: ReadonlyArray<unknown>): Promise<Task[]> {\n return history.length === 0 ? [task] : []\n },\n decide(history: ReadonlyArray<unknown>): 'pick-winner' | 'fail' {\n return history.length > 0 ? 'pick-winner' : 'fail'\n },\n}\n"],"mappings":"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","names":["result","chosen"]}
@@ -1,65 +0,0 @@
1
- // src/errors.ts
2
- import { AgentEvalError } from "@tangle-network/agent-eval";
3
- import {
4
- AgentEvalError as AgentEvalError2,
5
- CaptureIntegrityError,
6
- ConfigError,
7
- JudgeError,
8
- NotFoundError,
9
- ReplayError,
10
- ValidationError,
11
- VerificationError
12
- } from "@tangle-network/agent-eval";
13
- var SessionMismatchError = class extends AgentEvalError {
14
- sessionBackend;
15
- requestedBackend;
16
- constructor(sessionBackend, requestedBackend, options) {
17
- super(
18
- "validation",
19
- `Cannot resume ${sessionBackend} session with ${requestedBackend} backend`,
20
- options
21
- );
22
- this.sessionBackend = sessionBackend;
23
- this.requestedBackend = requestedBackend;
24
- }
25
- };
26
- var BackendTransportError = class extends AgentEvalError {
27
- backend;
28
- status;
29
- /**
30
- * Truncated upstream response body (≤2 KiB) when available. Diagnostic
31
- * only — surfaces in `backend_error.error.body` and `final.error.body`
32
- * so operators can see "free_tier_limit", "invalid_api_key", etc. without
33
- * cracking the log line open.
34
- */
35
- body;
36
- constructor(backend, message, options) {
37
- super("config", message, options);
38
- this.backend = backend;
39
- this.status = options?.status;
40
- this.body = options?.body;
41
- }
42
- };
43
- var RuntimeRunStateError = class extends AgentEvalError {
44
- constructor(message, options) {
45
- super("validation", message, options);
46
- }
47
- };
48
- var PlannerError = class extends AgentEvalError {
49
- constructor(message, options) {
50
- super("validation", message, options);
51
- }
52
- };
53
-
54
- export {
55
- SessionMismatchError,
56
- BackendTransportError,
57
- RuntimeRunStateError,
58
- PlannerError,
59
- AgentEvalError2 as AgentEvalError,
60
- ConfigError,
61
- JudgeError,
62
- NotFoundError,
63
- ValidationError
64
- };
65
- //# sourceMappingURL=chunk-SQSCRJ7U.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/errors.ts"],"sourcesContent":["/**\n * @stable\n *\n * Error taxonomy for `@tangle-network/agent-runtime`.\n *\n * Public contract: every error this package throws as part of its consumer-\n * facing API either extends `AgentEvalError` (re-exported here for ergonomic\n * `instanceof` checks at the runtime boundary) or extends one of the\n * runtime-specific subclasses below.\n *\n * Internal invariant guards (`throw new Error('this should never happen')`)\n * remain plain `Error` — they are programmer-mistake assertions, not\n * consumer-catchable contract failures.\n *\n * Subclassing strategy: where a runtime-specific failure maps cleanly to an\n * agent-eval code (validation, config, not_found), we re-use the agent-eval\n * subclass. Runtime-only failure modes (session resume against the wrong\n * backend, backend transport errors) get fresh subclasses that still carry an\n * `AgentEvalErrorCode` so cross-package handlers can pattern-match without\n * importing the runtime.\n */\n\nimport { AgentEvalError } from '@tangle-network/agent-eval'\n\nexport {\n AgentEvalError,\n type AgentEvalErrorCode,\n CaptureIntegrityError,\n ConfigError,\n JudgeError,\n NotFoundError,\n ReplayError,\n ValidationError,\n VerificationError,\n} from '@tangle-network/agent-eval'\n\n/**\n * @stable\n *\n * Caller asked to resume a session against a backend whose `kind` does not\n * match the session's recorded backend. This is a routing bug — the same\n * session id was reused across two different backend implementations — and\n * is not retryable without picking the right backend.\n */\nexport class SessionMismatchError extends AgentEvalError {\n readonly sessionBackend: string\n readonly requestedBackend: string\n\n constructor(sessionBackend: string, requestedBackend: string, options?: { cause?: unknown }) {\n super(\n 'validation',\n `Cannot resume ${sessionBackend} session with ${requestedBackend} backend`,\n options,\n )\n this.sessionBackend = sessionBackend\n this.requestedBackend = requestedBackend\n }\n}\n\n/**\n * @stable\n *\n * A backend transport call (HTTP, gRPC, sidecar IPC) failed with a non-success\n * status. Distinct from `JudgeError` (which is structural / unrecoverable)\n * because backend failures are sometimes retryable and consumers may want to\n * branch on the upstream status code.\n */\nexport class BackendTransportError extends AgentEvalError {\n readonly backend: string\n readonly status?: number\n /**\n * Truncated upstream response body (≤2 KiB) when available. Diagnostic\n * only — surfaces in `backend_error.error.body` and `final.error.body`\n * so operators can see \"free_tier_limit\", \"invalid_api_key\", etc. without\n * cracking the log line open.\n */\n readonly body?: string\n\n constructor(\n backend: string,\n message: string,\n options?: { cause?: unknown; status?: number; body?: string },\n ) {\n super('config', message, options)\n this.backend = backend\n this.status = options?.status\n this.body = options?.body\n }\n}\n\n/**\n * @stable\n *\n * A runtime-run lifecycle method was called in an order the state machine does\n * not allow: `persist()` before `complete()`, `complete()` twice, etc.\n */\nexport class RuntimeRunStateError extends AgentEvalError {\n constructor(message: string, options?: { cause?: unknown }) {\n super('validation', message, options)\n }\n}\n\n/**\n * @stable\n *\n * The dynamic-loop planner returned an unusable topology move — the LLM emitted\n * no parseable envelope, an unknown `kind`, or a structurally-invalid move\n * (e.g. a fanout with zero tasks). This is a structural failure of the\n * agent-authored topology, not a config mistake: the planner ran but its output\n * cannot drive the kernel. Carries `validation` so cross-package handlers can\n * pattern-match without importing the runtime. Fail loud — never substitute a\n * default move, or the loop silently runs a topology nobody chose.\n */\nexport class PlannerError extends AgentEvalError {\n constructor(message: string, options?: { cause?: unknown }) {\n super('validation', message, options)\n }\n}\n"],"mappings":";AAsBA,SAAS,sBAAsB;AAE/B;AAAA,EACE,kBAAAA;AAAA,EAEA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAUA,IAAM,uBAAN,cAAmC,eAAe;AAAA,EAC9C;AAAA,EACA;AAAA,EAET,YAAY,gBAAwB,kBAA0B,SAA+B;AAC3F;AAAA,MACE;AAAA,MACA,iBAAiB,cAAc,iBAAiB,gBAAgB;AAAA,MAChE;AAAA,IACF;AACA,SAAK,iBAAiB;AACtB,SAAK,mBAAmB;AAAA,EAC1B;AACF;AAUO,IAAM,wBAAN,cAAoC,eAAe;AAAA,EAC/C;AAAA,EACA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA;AAAA,EAET,YACE,SACA,SACA,SACA;AACA,UAAM,UAAU,SAAS,OAAO;AAChC,SAAK,UAAU;AACf,SAAK,SAAS,SAAS;AACvB,SAAK,OAAO,SAAS;AAAA,EACvB;AACF;AAQO,IAAM,uBAAN,cAAmC,eAAe;AAAA,EACvD,YAAY,SAAiB,SAA+B;AAC1D,UAAM,cAAc,SAAS,OAAO;AAAA,EACtC;AACF;AAaO,IAAM,eAAN,cAA2B,eAAe;AAAA,EAC/C,YAAY,SAAiB,SAA+B;AAC1D,UAAM,cAAc,SAAS,OAAO;AAAA,EACtC;AACF;","names":["AgentEvalError"]}
@@ -1,90 +0,0 @@
1
- import {
2
- ConfigError
3
- } from "./chunk-SQSCRJ7U.js";
4
-
5
- // src/improvement/optimize-prompt.ts
6
- import { gepaDriver, heldOutGate, runImprovementLoop } from "@tangle-network/agent-eval/campaign";
7
- async function optimizePrompt(opts) {
8
- if (!opts.driver && !opts.reflection) {
9
- throw new ConfigError(
10
- "optimizePrompt: pass `reflection` (builds the default gepaDriver) or a custom `driver`"
11
- );
12
- }
13
- if (opts.scenarios.length === 0) {
14
- throw new ConfigError("optimizePrompt: `scenarios` must be non-empty");
15
- }
16
- if (opts.holdoutScenarios.length === 0) {
17
- throw new ConfigError(
18
- "optimizePrompt: `holdoutScenarios` must be non-empty (the gate needs it)"
19
- );
20
- }
21
- const driver = opts.driver ?? gepaDriver({
22
- llm: opts.reflection.llm,
23
- model: opts.reflection.model,
24
- target: opts.reflection.target ?? "system prompt",
25
- mutationPrimitives: opts.reflection.mutationPrimitives,
26
- constraints: opts.reflection.preserveSections || opts.reflection.maxSentenceEdits !== void 0 ? {
27
- preserveSections: opts.reflection.preserveSections,
28
- maxSentenceEdits: opts.reflection.maxSentenceEdits
29
- } : void 0
30
- });
31
- const gate = opts.gate ?? heldOutGate({
32
- scenarios: opts.holdoutScenarios,
33
- ...opts.deltaThreshold !== void 0 ? { deltaThreshold: opts.deltaThreshold } : {}
34
- });
35
- const result = await runImprovementLoop({
36
- baselineSurface: opts.baselinePrompt,
37
- dispatchWithSurface: (surface, scenario, ctx) => {
38
- if (typeof surface !== "string") {
39
- throw new ConfigError(
40
- "optimizePrompt: received a CodeSurface \u2014 this entry point optimizes string prompts only"
41
- );
42
- }
43
- return opts.runWithPrompt(surface, scenario, ctx);
44
- },
45
- driver,
46
- populationSize: opts.populationSize ?? 4,
47
- maxGenerations: opts.maxGenerations ?? 3,
48
- ...opts.promoteTopK !== void 0 ? { promoteTopK: opts.promoteTopK } : {},
49
- scenarios: opts.scenarios,
50
- holdoutScenarios: opts.holdoutScenarios,
51
- judges: opts.judges,
52
- gate,
53
- autoOnPromote: opts.autoOnPromote ?? "none",
54
- ...opts.ghOwner !== void 0 ? { ghOwner: opts.ghOwner } : {},
55
- ...opts.ghRepo !== void 0 ? { ghRepo: opts.ghRepo } : {},
56
- runDir: opts.runDir,
57
- ...opts.storage !== void 0 ? { storage: opts.storage } : {},
58
- ...opts.seed !== void 0 ? { seed: opts.seed } : {},
59
- ...opts.reps !== void 0 ? { reps: opts.reps } : {},
60
- ...opts.maxConcurrency !== void 0 ? { maxConcurrency: opts.maxConcurrency } : {},
61
- ...opts.now !== void 0 ? { now: opts.now } : {}
62
- });
63
- const improved = result.gateResult.decision === "ship";
64
- const winnerSurface = typeof result.winnerSurface === "string" ? result.winnerSurface : opts.baselinePrompt;
65
- const baselineComposite = meanComposite(result.baselineOnHoldout);
66
- const winnerComposite = meanComposite(result.winnerOnHoldout);
67
- return {
68
- prompt: improved ? winnerSurface : opts.baselinePrompt,
69
- improved,
70
- decision: result.gateResult.decision,
71
- reasons: result.gateResult.reasons,
72
- baselineComposite,
73
- winnerComposite,
74
- delta: result.gateResult.delta ?? winnerComposite - baselineComposite,
75
- ...improved && result.winnerRationale ? { rationale: result.winnerRationale } : {},
76
- diff: result.promotedDiff,
77
- raw: result
78
- };
79
- }
80
- function meanComposite(campaign) {
81
- const scenarios = Object.values(campaign.aggregates.byScenario);
82
- if (scenarios.length === 0) return 0;
83
- const sum = scenarios.reduce((acc, s) => acc + s.meanComposite, 0);
84
- return sum / scenarios.length;
85
- }
86
-
87
- export {
88
- optimizePrompt
89
- };
90
- //# sourceMappingURL=chunk-VOX6Z3II.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/improvement/optimize-prompt.ts"],"sourcesContent":["/**\n * @experimental\n *\n * `optimizePrompt` — identity-gated optimization for any TEXT prompt surface\n * (system prompt, planner prompt, judge rubric, skill doc).\n *\n * The text-surface sibling to this module's `improvementDriver` (the\n * CODE-surface / worktree path). Both feed agent-eval's `runImprovementLoop`;\n * this one defaults the driver to agent-eval's `gepaDriver` (reflective text\n * mutator) and the gate to `heldOutGate`.\n *\n * IDENTITY-GATED BY CONSTRUCTION — the whole point. The loop runs evals,\n * collects per-scenario signal, proposes candidates, and the gate compares\n * candidate-vs-baseline ON THE HELDOUT. `result.prompt` is the baseline\n * (identity) UNLESS the gate decided `'ship'`. So wiring a surface up is safe:\n * a surface with no beneficial mutation simply keeps its baseline. You never\n * regress by registering a prompt — you only ever improve when the held-out\n * data earns it.\n *\n * Generic over the runtime: `runWithPrompt` is the only domain seam — given a\n * candidate prompt + scenario, run it however the surface runs (sandbox\n * `streamPrompt`, a `runLoop`, a direct model call) and return the artifact the\n * judges score. The optimizer never assumes how a prompt is executed.\n */\n\nimport type { LlmClientOptions } from '@tangle-network/agent-eval'\nimport type {\n CampaignResult,\n CampaignStorage,\n DispatchContext,\n Gate,\n GateResult,\n ImprovementDriver,\n JudgeConfig,\n RunImprovementLoopResult,\n Scenario,\n} from '@tangle-network/agent-eval/campaign'\nimport { gepaDriver, heldOutGate, runImprovementLoop } from '@tangle-network/agent-eval/campaign'\nimport { ConfigError } from '../errors'\n\n/** Reflection config for the default `gepaDriver`. Omit when passing a custom\n * `driver`. */\nexport interface OptimizePromptReflection {\n /** Router transport for the reflection model. */\n llm: LlmClientOptions\n /** Model that performs the reflective rewrite. */\n model: string\n /** What is being optimized — orients the reflection prompt. Default\n * `'system prompt'`. */\n target?: string\n /** Surface-specific mutation levers offered to the reflector. */\n mutationPrimitives?: string[]\n /** H2 (`## Foo`) headings that MUST survive every candidate. gepaDriver's\n * only structural guard — load-bearing sections of the prompt should be\n * `##` headings so a rewrite cannot drop them. */\n preserveSections?: string[]\n /** Max sentence-level edits per candidate vs the parent (a textual learning\n * rate). Caps a rewrite from wiping prior rules in one generation. */\n maxSentenceEdits?: number\n}\n\n/** @experimental */\nexport interface OptimizePromptOptions<TScenario extends Scenario, TArtifact> {\n /** The prompt being optimized — the identity baseline the gate protects. */\n baselinePrompt: string\n /** Domain seam: run a candidate prompt against a scenario → artifact the\n * judges score. The optimizer is agnostic to HOW the prompt runs. */\n runWithPrompt: (prompt: string, scenario: TScenario, ctx: DispatchContext) => Promise<TArtifact>\n /** Training pool — scored each generation to rank candidates. */\n scenarios: TScenario[]\n /** Held out of training — scored ONLY for the gate's baseline-vs-winner\n * delta. Disjoint from `scenarios`; this is what makes promotion measure\n * generalization, not memorization. */\n holdoutScenarios: TScenario[]\n /** Scorers — deterministic checks or LLM judges. */\n judges: JudgeConfig<TArtifact, TScenario>[]\n /** Where artifacts + traces land (opaque key under in-memory storage). */\n runDir: string\n /** Default driver = `gepaDriver` built from this. Required UNLESS `driver`\n * is supplied. */\n reflection?: OptimizePromptReflection\n /** Override the improvement strategy (custom driver / deterministic tests). */\n driver?: ImprovementDriver\n /** Override the promotion gate. Default `heldOutGate` over `holdoutScenarios`\n * — zero extra LLM. Wrap `defaultProductionGate` for red-team/reward-hacking\n * hardening on production wiring. */\n gate?: Gate<TArtifact, TScenario>\n /** Minimum held-out composite lift to ship, forwarded to the default\n * `heldOutGate`. When omitted the gate uses its own default. */\n deltaThreshold?: number\n /** Candidates proposed per generation. Default 4. */\n populationSize?: number\n /** Generations to run. Default 3. */\n maxGenerations?: number\n /** Candidates carried to the next generation. Default 2. */\n promoteTopK?: number\n /** Storage backend. Pass `inMemoryCampaignStorage()` for filesystem-less /\n * test runs. Default: Node filesystem. */\n storage?: CampaignStorage\n /** Reproducibility seed. Default 42. */\n seed?: number\n /** Per-scenario replicates for CI bands. Default 1. */\n reps?: number\n /** Max concurrent cells. Default 2. */\n maxConcurrency?: number\n /** Test seam — override the wall clock. */\n now?: () => Date\n /** On a shipped gate: `'pr'` opens a PR, `'none'` just reports. Default\n * `'none'`. */\n autoOnPromote?: 'pr' | 'none'\n ghOwner?: string\n ghRepo?: string\n}\n\n/** @experimental */\nexport interface OptimizePromptResult<TArtifact, TScenario extends Scenario> {\n /** The prompt to USE. Identity (the baseline) unless the gate shipped a\n * winner — so a caller can always assign `result.prompt` unconditionally. */\n prompt: string\n /** True only when the gate promoted a candidate over baseline on holdout. */\n improved: boolean\n /** The gate's verdict (`'ship' | 'hold' | 'need_more_work' | ...`). */\n decision: GateResult['decision']\n /** Human-readable reasons the gate gave. */\n reasons: string[]\n /** Mean held-out composite of the baseline. */\n baselineComposite: number\n /** Mean held-out composite of the winner candidate. */\n winnerComposite: number\n /** Held-out lift (winner − baseline); the gate's `delta` when it reported one. */\n delta: number\n /** Why the winner was proposed — present when a shipped winner carried a\n * driver rationale. */\n rationale?: string\n /** Unified baseline→winner diff (empty when the winner is the baseline). */\n diff: string\n /** The full loop result for callers that need generations / campaigns. */\n raw: RunImprovementLoopResult<TArtifact, TScenario>\n}\n\n/** @experimental */\nexport async function optimizePrompt<TScenario extends Scenario, TArtifact>(\n opts: OptimizePromptOptions<TScenario, TArtifact>,\n): Promise<OptimizePromptResult<TArtifact, TScenario>> {\n if (!opts.driver && !opts.reflection) {\n throw new ConfigError(\n 'optimizePrompt: pass `reflection` (builds the default gepaDriver) or a custom `driver`',\n )\n }\n if (opts.scenarios.length === 0) {\n throw new ConfigError('optimizePrompt: `scenarios` must be non-empty')\n }\n if (opts.holdoutScenarios.length === 0) {\n throw new ConfigError(\n 'optimizePrompt: `holdoutScenarios` must be non-empty (the gate needs it)',\n )\n }\n\n const driver =\n opts.driver ??\n gepaDriver({\n llm: opts.reflection!.llm,\n model: opts.reflection!.model,\n target: opts.reflection!.target ?? 'system prompt',\n mutationPrimitives: opts.reflection!.mutationPrimitives,\n constraints:\n opts.reflection!.preserveSections || opts.reflection!.maxSentenceEdits !== undefined\n ? {\n preserveSections: opts.reflection!.preserveSections,\n maxSentenceEdits: opts.reflection!.maxSentenceEdits,\n }\n : undefined,\n })\n\n const gate =\n opts.gate ??\n heldOutGate<TArtifact, TScenario>({\n scenarios: opts.holdoutScenarios,\n ...(opts.deltaThreshold !== undefined ? { deltaThreshold: opts.deltaThreshold } : {}),\n })\n\n const result = await runImprovementLoop<TScenario, TArtifact>({\n baselineSurface: opts.baselinePrompt,\n dispatchWithSurface: (surface, scenario, ctx) => {\n if (typeof surface !== 'string') {\n // optimizePrompt is the TEXT-surface entry point; a CodeSurface means\n // the caller wired the wrong driver. Fail loud — don't silently run the\n // baseline and report a phantom score.\n throw new ConfigError(\n 'optimizePrompt: received a CodeSurface — this entry point optimizes string prompts only',\n )\n }\n return opts.runWithPrompt(surface, scenario, ctx)\n },\n driver,\n populationSize: opts.populationSize ?? 4,\n maxGenerations: opts.maxGenerations ?? 3,\n ...(opts.promoteTopK !== undefined ? { promoteTopK: opts.promoteTopK } : {}),\n scenarios: opts.scenarios,\n holdoutScenarios: opts.holdoutScenarios,\n judges: opts.judges,\n gate,\n autoOnPromote: opts.autoOnPromote ?? 'none',\n ...(opts.ghOwner !== undefined ? { ghOwner: opts.ghOwner } : {}),\n ...(opts.ghRepo !== undefined ? { ghRepo: opts.ghRepo } : {}),\n runDir: opts.runDir,\n ...(opts.storage !== undefined ? { storage: opts.storage } : {}),\n ...(opts.seed !== undefined ? { seed: opts.seed } : {}),\n ...(opts.reps !== undefined ? { reps: opts.reps } : {}),\n ...(opts.maxConcurrency !== undefined ? { maxConcurrency: opts.maxConcurrency } : {}),\n ...(opts.now !== undefined ? { now: opts.now } : {}),\n })\n\n const improved = result.gateResult.decision === 'ship'\n const winnerSurface =\n typeof result.winnerSurface === 'string' ? result.winnerSurface : opts.baselinePrompt\n const baselineComposite = meanComposite(result.baselineOnHoldout)\n const winnerComposite = meanComposite(result.winnerOnHoldout)\n\n return {\n prompt: improved ? winnerSurface : opts.baselinePrompt,\n improved,\n decision: result.gateResult.decision,\n reasons: result.gateResult.reasons,\n baselineComposite,\n winnerComposite,\n delta: result.gateResult.delta ?? winnerComposite - baselineComposite,\n ...(improved && result.winnerRationale ? { rationale: result.winnerRationale } : {}),\n diff: result.promotedDiff,\n raw: result,\n }\n}\n\n/** Mean composite over a campaign's per-scenario aggregates. The held-out\n * campaigns score one surface across `holdoutScenarios`; averaging the\n * per-scenario means gives the single number the gate's delta is built from. */\nfunction meanComposite(campaign: CampaignResult<unknown, Scenario>): number {\n const scenarios = Object.values(campaign.aggregates.byScenario)\n if (scenarios.length === 0) return 0\n const sum = scenarios.reduce((acc, s) => acc + s.meanComposite, 0)\n return sum / scenarios.length\n}\n"],"mappings":"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","names":[]}