@tangle-network/agent-runtime 0.47.0 → 0.49.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 (41) hide show
  1. package/README.md +79 -15
  2. package/dist/agent.js +1 -1
  3. package/dist/chunk-GHX7XOJ2.js +433 -0
  4. package/dist/chunk-GHX7XOJ2.js.map +1 -0
  5. package/dist/{chunk-T4OQQEE3.js → chunk-IQS4HI3F.js} +14 -5
  6. package/dist/chunk-IQS4HI3F.js.map +1 -0
  7. package/dist/{chunk-72JQCHOZ.js → chunk-PXUTIMGJ.js} +2318 -237
  8. package/dist/chunk-PXUTIMGJ.js.map +1 -0
  9. package/dist/{chunk-MGFEUYOH.js → chunk-U2VEWKKK.js} +3 -3
  10. package/dist/{chunk-JNPK46YH.js → chunk-VIEDXELL.js} +408 -6
  11. package/dist/chunk-VIEDXELL.js.map +1 -0
  12. package/dist/{chunk-VR4JIC5H.js → chunk-XTEZ3YJ4.js} +2 -2
  13. package/dist/index.d.ts +29 -4
  14. package/dist/index.js +109 -21
  15. package/dist/index.js.map +1 -1
  16. package/dist/kb-gate-CsXpNRk7.d.ts +1145 -0
  17. package/dist/{loop-runner-bin-DEm4roYF.d.ts → loop-runner-bin-Cgn0A-NW.d.ts} +1 -1
  18. package/dist/loop-runner-bin.d.ts +2 -2
  19. package/dist/loop-runner-bin.js +3 -3
  20. package/dist/loops.d.ts +3 -3
  21. package/dist/loops.js +57 -1
  22. package/dist/mcp/bin.js +187 -24
  23. package/dist/mcp/bin.js.map +1 -1
  24. package/dist/mcp/index.d.ts +28 -125
  25. package/dist/mcp/index.js +28 -6
  26. package/dist/mcp/index.js.map +1 -1
  27. package/dist/platform.js +2 -2
  28. package/dist/platform.js.map +1 -1
  29. package/dist/runtime.d.ts +1100 -62
  30. package/dist/runtime.js +57 -1
  31. package/dist/{types-Cbx3dNK5.d.ts → types-BpDfCPUp.d.ts} +1 -1
  32. package/dist/workflow.js +1 -1
  33. package/package.json +7 -6
  34. package/dist/chunk-5YDS7BLC.js +0 -218
  35. package/dist/chunk-5YDS7BLC.js.map +0 -1
  36. package/dist/chunk-72JQCHOZ.js.map +0 -1
  37. package/dist/chunk-JNPK46YH.js.map +0 -1
  38. package/dist/chunk-T4OQQEE3.js.map +0 -1
  39. package/dist/kb-gate-51BlLlVM.d.ts +0 -529
  40. /package/dist/{chunk-MGFEUYOH.js.map → chunk-U2VEWKKK.js.map} +0 -0
  41. /package/dist/{chunk-VR4JIC5H.js.map → chunk-XTEZ3YJ4.js.map} +0 -0
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/mcp/detached-turn.ts","../src/mcp/executor.ts","../src/mcp/delegates.ts"],"sourcesContent":["/**\n * @experimental\n *\n * Detached delegation turns over the sandbox SDK's `driveTurn` primitive.\n *\n * Two halves of one story:\n *\n * - {@link runDetachedTurn} — the dispatch side. A single-session delegate\n * (single-variant coder / researcher) acquires a box, binds the sandbox id\n * into the record's `detachedSessionRef`, then advances the turn with\n * repeated `driveTurn` ticks instead of holding a live SSE stream. The\n * session id is deterministic and supplied at submit time, so a process\n * crash between ticks loses nothing — the turn keeps running in the box.\n *\n * - {@link createDriveTurnResumeDriver} — the resume side. A\n * `DelegationResumeDriver` that re-attaches restored in-flight records to\n * their detached runs: parse the record's ref, resolve the box, advance the\n * turn one `driveTurn` pass per `tick()`, and map the SDK's three states\n * (`completed | running | failed`) onto `DelegationResumeTick`.\n *\n * Both sides type the box structurally ({@link DriveTurnCapableBox}) so tests\n * inject fakes and the module never requires the sandbox SDK at runtime — the\n * SDK stays an optional peer, exactly like the executors' `SandboxClient` seam.\n *\n * Tradeoffs of detached mode (why it is opt-in, not the default): a detached\n * turn yields one terminal payload instead of a live event stream, so per-token\n * loop trace events and kernel token/cost aggregation are not produced for that\n * turn. Multi-variant fanout stays on the streaming `runLoop` path — N\n * concurrent sessions cannot be expressed as one resume key, and winner\n * selection needs every candidate.\n */\n\nimport type { SandboxEvent } from '@tangle-network/sandbox'\nimport { ValidationError } from '../errors'\nimport type { AgentRunSpec, SandboxClient } from '../runtime'\nimport { createSandboxForSpec } from '../runtime'\nimport { deleteBoxSafe, sleep, throwAbort, throwIfAborted } from '../runtime/util'\nimport type { DelegationRecord, DelegationResumeDriver, DelegationResumeTick } from './task-queue'\nimport type { DelegationProgress, DelegationResultPayload } from './types'\n\nconst DEFAULT_TICK_INTERVAL_MS = 5000\n\n/**\n * Structural mirror of the sandbox SDK's `TurnDriveResult` (>= 0.6).\n * Discriminated on `state`; `failed` is terminal and deterministic per the\n * SDK contract — re-invoking with the same ids returns the same outcome.\n *\n * @experimental\n */\nexport type DriveTurnTick =\n | { state: 'completed'; text: string; result: Record<string, unknown> }\n | { state: 'running'; startedAt?: Date; elapsedMs?: number }\n | { state: 'failed'; error: string }\n\n/**\n * The box surface detached turns need. `SandboxInstance`\n * (`@tangle-network/sandbox` >= 0.6) satisfies it structurally; tests pass\n * in-memory fakes. `_sessionCancel` is the SDK's remote-cancellation surface —\n * optional here because older SDKs / fakes may not expose it; when present it\n * is invoked on abort so the remote run actually stops.\n *\n * @experimental\n */\nexport interface DriveTurnCapableBox {\n driveTurn(\n message: string,\n opts: { sessionId: string; turnId?: string; wallCapMs?: number },\n ): Promise<DriveTurnTick>\n _sessionCancel?(id: string): Promise<void>\n}\n\n/**\n * Decoded `DelegationRecord.detachedSessionRef`. `sandboxId` is absent between\n * submit and box acquisition — a record restored in that window is not\n * resumable (there is no box to resume on) and the resume driver fails it\n * loud rather than dispatching onto a guessed box.\n *\n * @experimental\n */\nexport interface DetachedSessionRefParts {\n sessionId: string\n sandboxId?: string\n}\n\n/**\n * Encode ref parts into the JSON-safe string stored on the record:\n * `session=<id>` before the box exists, `sandbox=<id>;session=<id>` once\n * bound. Ids must not contain the `;`/`=` delimiters.\n *\n * @experimental\n */\nexport function formatDetachedSessionRef(parts: DetachedSessionRefParts): string {\n assertRefComponent('sessionId', parts.sessionId)\n if (parts.sandboxId === undefined) return `session=${parts.sessionId}`\n assertRefComponent('sandboxId', parts.sandboxId)\n return `sandbox=${parts.sandboxId};session=${parts.sessionId}`\n}\n\n/** @experimental Inverse of {@link formatDetachedSessionRef}; throws `ValidationError` on malformed input. */\nexport function parseDetachedSessionRef(raw: string): DetachedSessionRefParts {\n const fields = new Map<string, string>()\n for (const pair of raw.split(';')) {\n const eq = pair.indexOf('=')\n const key = eq === -1 ? '' : pair.slice(0, eq)\n const value = eq === -1 ? '' : pair.slice(eq + 1)\n if ((key !== 'session' && key !== 'sandbox') || value.length === 0 || fields.has(key)) {\n throw new ValidationError(\n `parseDetachedSessionRef: malformed detachedSessionRef ${JSON.stringify(raw)} — expected \"session=<id>\" or \"sandbox=<id>;session=<id>\"`,\n )\n }\n fields.set(key, value)\n }\n const sessionId = fields.get('session')\n if (!sessionId) {\n throw new ValidationError(\n `parseDetachedSessionRef: detachedSessionRef ${JSON.stringify(raw)} carries no session id`,\n )\n }\n const sandboxId = fields.get('sandbox')\n return { sessionId, ...(sandboxId !== undefined ? { sandboxId } : {}) }\n}\n\nfunction assertRefComponent(name: string, value: string): void {\n if (value.length === 0 || value.includes(';') || value.includes('=')) {\n throw new ValidationError(\n `formatDetachedSessionRef: ${name} ${JSON.stringify(value)} must be non-empty and free of \";\" / \"=\"`,\n )\n }\n}\n\n/** @experimental The terminal payload of a finished detached turn. */\nexport interface DetachedTurn {\n /** Final assistant text. */\n text: string\n /** The SDK's cached AgentExecutionResult-shape record for the turn. */\n result: Record<string, unknown>\n}\n\n/**\n * Synthesize the terminal event array a detached turn settles through. Shaped\n * so the existing event-stream output adapters (coder, researcher) parse it:\n * `data.result` for adapters that read a structured terminal record, `data.text`\n * for adapters that scan assistant text for the fenced result block.\n *\n * @experimental\n */\nexport function detachedTurnEvents(sessionId: string, turn: DetachedTurn): SandboxEvent[] {\n return [\n {\n type: 'result',\n id: sessionId,\n data: {\n text: turn.text,\n finalText: turn.text,\n success: true,\n result: turn.result,\n },\n } as SandboxEvent,\n ]\n}\n\n/** @experimental */\nexport interface RunDetachedTurnOptions {\n /** Sandbox client used to acquire the box (the delegate's executor client). */\n client: SandboxClient\n /** Profile + overrides for box acquisition — same spec the streaming path uses. */\n spec: AgentRunSpec<unknown>\n /** The full turn prompt; consumed by `driveTurn`'s dispatch leg. */\n prompt: string\n /** Deterministic resume key, minted at submit time (`parseDetachedSessionRef(ref).sessionId`). */\n sessionId: string\n /**\n * Called once the box exists, with its sandbox id. Callers persist\n * `formatDetachedSessionRef({ sandboxId, sessionId })` onto the record here so\n * a restart can resolve the box again.\n */\n bindSandbox(sandboxId: string): void\n signal: AbortSignal\n report(progress: DelegationProgress): void\n /** Delay between `running` ticks (ms). Default 5000. */\n tickIntervalMs?: number\n /** Wall-clock cap forwarded to `driveTurn` — the SDK cancels and fails a session past it. */\n wallCapMs?: number\n}\n\n/**\n * Dispatch one detached turn and advance it to a terminal state with\n * `driveTurn` ticks. The first tick dispatches (idempotent on `sessionId`);\n * subsequent ticks poll. On abort the remote session is cancelled via\n * `_sessionCancel` when the box exposes it. The box is torn down on every\n * in-process exit path (success, failure, abort) — only a process death skips\n * teardown, which is exactly the case the resume driver re-attaches to.\n *\n * @experimental\n */\nexport async function runDetachedTurn(options: RunDetachedTurnOptions): Promise<DetachedTurn> {\n const intervalMs = options.tickIntervalMs ?? DEFAULT_TICK_INTERVAL_MS\n const box = await createSandboxForSpec(options.client, options.spec, options.signal)\n const drive = box as Partial<DriveTurnCapableBox>\n const onAbort = () => {\n void drive._sessionCancel?.(options.sessionId).catch(() => {})\n }\n try {\n if (typeof drive.driveTurn !== 'function') {\n throw new ValidationError(\n 'runDetachedTurn: the acquired sandbox exposes no driveTurn(message, { sessionId }) — ' +\n 'detached dispatch requires @tangle-network/sandbox >= 0.6 and a session-backed ' +\n 'placement (sibling/fleet); disable detached dispatch for this executor.',\n )\n }\n const sandboxId = (box as { id?: unknown }).id\n if (typeof sandboxId !== 'string' || sandboxId.length === 0) {\n throw new ValidationError(\n 'runDetachedTurn: the acquired sandbox carries no id — without it the detached run ' +\n 'cannot be resumed after a restart, so refusing to dispatch detached.',\n )\n }\n options.bindSandbox(sandboxId)\n options.signal.addEventListener('abort', onAbort, { once: true })\n for (;;) {\n throwIfAborted(options.signal)\n const tick = await drive.driveTurn(options.prompt, {\n sessionId: options.sessionId,\n turnId: options.sessionId,\n ...(options.wallCapMs !== undefined ? { wallCapMs: options.wallCapMs } : {}),\n })\n throwIfAborted(options.signal)\n if (tick.state === 'completed') return { text: tick.text, result: tick.result }\n if (tick.state === 'failed') {\n throw new Error(`detached turn ${options.sessionId} failed: ${tick.error}`)\n }\n options.report({ iteration: 0, phase: detachedRunningPhase(tick.elapsedMs) })\n await sleep(intervalMs, options.signal)\n }\n } finally {\n options.signal.removeEventListener('abort', onAbort)\n if (options.signal.aborted) onAbort()\n await deleteBoxSafe(box)\n }\n}\n\nfunction detachedRunningPhase(elapsedMs: number | undefined): string {\n return elapsedMs === undefined\n ? 'detached-running'\n : `detached-running ${Math.round(elapsedMs / 1000)}s`\n}\n\n/** @experimental */\nexport interface DriveTurnResumeDriverOptions {\n /**\n * Resolve the live box owning a detached session. The bin wires this to the\n * sandbox client's `get(sandboxId)`; throw when the box no longer exists —\n * a thrown tick settles the record as failed, which is the truth.\n */\n resolveSandbox(sandboxId: string): Promise<DriveTurnCapableBox>\n /**\n * Rebuild the turn prompt from the persisted record. Only consumed by\n * `driveTurn`'s dispatch leg — i.e. when the previous process died after\n * binding the box but before the session was dispatched. Must reproduce the\n * prompt the delegate would have sent.\n */\n buildMessage(record: DelegationRecord): string\n /**\n * Map a completed turn onto the delegation's typed output payload (parse +\n * validate per profile). Throw when the resumed result does not pass the\n * profile's gate — the queue settles the record as failed with that error.\n */\n settleOutput(\n turn: DetachedTurn,\n record: DelegationRecord,\n ctx: { signal: AbortSignal },\n ): Promise<DelegationResultPayload['output']> | DelegationResultPayload['output']\n /** Delay between `running` ticks (ms). Default 5000. */\n intervalMs?: number\n /** Wall-clock cap forwarded to `driveTurn` on every tick. */\n wallCapMs?: number\n}\n\n/**\n * Build the `driveTurn`-backed {@link DelegationResumeDriver}. Each `tick()`\n * is one settle/poll/dispatch pass:\n *\n * - ref without a sandbox binding → `failed` (`DetachedSessionUnboundError`):\n * the previous process died before a box existed; there is nothing to resume.\n * - `driveTurn` `completed` → `settleOutput` → `completed` tick.\n * - `running` → progress via `ctx.report`, `running` tick (queue re-ticks\n * after `intervalMs`).\n * - `failed` → `failed` tick (`DetachedTurnFailedError`) — terminal per the\n * SDK's deterministic-failure contract.\n *\n * Abort: the queue stops ticking once `cancel()` flips the record, so remote\n * cancellation is hooked onto `ctx.signal` (once per task) and fires\n * `_sessionCancel` when the SDK surface exposes it. The driver never deletes\n * boxes — it cannot know whether `sandboxId` is a disposable sibling or a\n * fleet machine, and destroying a fleet machine would be unrecoverable.\n *\n * @experimental\n */\nexport function createDriveTurnResumeDriver(\n options: DriveTurnResumeDriverOptions,\n): DelegationResumeDriver {\n const cancelHooked = new Set<string>()\n return {\n intervalMs: options.intervalMs ?? DEFAULT_TICK_INTERVAL_MS,\n async tick({ record, detachedSessionRef }, ctx): Promise<DelegationResumeTick> {\n const ref = parseDetachedSessionRef(detachedSessionRef)\n if (ref.sandboxId === undefined) {\n return {\n state: 'failed',\n error: {\n message:\n `detached session \"${ref.sessionId}\" was never bound to a sandbox — the previous ` +\n 'process died before the box was acquired, so the turn was never dispatched and ' +\n 'cannot be resumed',\n kind: 'DetachedSessionUnboundError',\n },\n }\n }\n const box = await options.resolveSandbox(ref.sandboxId)\n if (!cancelHooked.has(record.taskId)) {\n cancelHooked.add(record.taskId)\n ctx.signal.addEventListener(\n 'abort',\n () => {\n void box._sessionCancel?.(ref.sessionId).catch(() => {})\n },\n { once: true },\n )\n }\n if (ctx.signal.aborted) throwAbort()\n const tick = await box.driveTurn(options.buildMessage(record), {\n sessionId: ref.sessionId,\n turnId: ref.sessionId,\n ...(options.wallCapMs !== undefined ? { wallCapMs: options.wallCapMs } : {}),\n })\n if (tick.state === 'completed') {\n const output = await options.settleOutput(\n { text: tick.text, result: tick.result },\n record,\n {\n signal: ctx.signal,\n },\n )\n return { state: 'completed', output }\n }\n if (tick.state === 'failed') {\n return {\n state: 'failed',\n error: {\n message: `detached turn ${ref.sessionId} failed: ${tick.error}`,\n kind: 'DetachedTurnFailedError',\n },\n }\n }\n ctx.report({ iteration: 0, phase: detachedRunningPhase(tick.elapsedMs) })\n return { state: 'running' }\n },\n }\n}\n","/**\n * @experimental\n *\n * Delegation executors — the layer between MCP delegates and the sandbox\n * substrate. Each executor exposes a {@link SandboxClient} 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 { LoopSandboxPlacement, SandboxClient } from '../runtime'\n\n/** @experimental */\nexport interface DelegationExecutor {\n /** Sandbox client the kernel calls. Returned with `describePlacement` set. */\n readonly client: SandboxClient\n /** Best-effort one-liner used in stderr boot logs and diagnostics. */\n describe(): string\n /**\n * Where delegated work physically runs. `sibling` and `fleet` placements are\n * session-backed (boxes expose `driveTurn`, so detached dispatch + resume\n * apply); `in-process` spawns local harness CLIs with no sandbox session to\n * detach. Optional so consumer-implemented executors stay source-compatible;\n * absent means \"unknown\" and detached dispatch is not enabled for it.\n */\n readonly placement?: 'sibling' | 'fleet' | 'in-process'\n}\n\n/** @experimental */\nexport interface SiblingSandboxExecutorOptions {\n client: SandboxClient\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: SandboxClient = {\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 placement: 'sibling',\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: SandboxClient = {\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 placement: 'fleet',\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 CoderOutput, coderProfile, multiHarnessCoderFanout } from '../profiles/coder'\nimport type { AgentRunSpec, Iteration, LoopTraceEmitter, SandboxClient } from '../runtime'\nimport { runLoop } from '../runtime'\nimport {\n type DetachedTurn,\n detachedTurnEvents,\n formatDetachedSessionRef,\n parseDetachedSessionRef,\n runDetachedTurn,\n} from './detached-turn'\nimport { createSiblingSandboxExecutor, type DelegationExecutor } from './executor'\nimport type {\n CoderTask,\n DelegateCodeArgs,\n DelegateResearchArgs,\n DelegateUiAuditArgs,\n DelegationProgress,\n ResearchOutputShape,\n UiAuditorDelegationOutput,\n} from './types'\n\n/** @experimental */\nexport interface DelegateRunCtx {\n signal: AbortSignal\n report(progress: DelegationProgress): void\n /**\n * Detached-run resume key recorded on the queue record at submit time\n * (`formatDetachedSessionRef`). Present only when the submit path requested\n * detached dispatch — its presence is what routes a session-backed delegate\n * onto the `driveTurn` tick path instead of holding a stream.\n */\n detachedSessionRef?: string\n /** Rebind the record's resume key (e.g. once the sandbox id is known). */\n updateDetachedSessionRef?(ref: string): 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/**\n * UI-auditor delegate — fully consumer-injected. agent-runtime ships no\n * default factory because the inputs are workspace path + judge function\n * + (optionally) a `SandboxClient`, and the judge is the consumer's\n * model seam. See `createInProcessUiAuditClient` + `uiAuditorProfile` in\n * `@tangle-network/agent-runtime/profiles` for the canonical wiring.\n *\n * @experimental\n */\nexport type UiAuditorDelegate = (\n args: DelegateUiAuditArgs,\n ctx: DelegateRunCtx,\n) => Promise<UiAuditorDelegationOutput>\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?: SandboxClient\n /** Backend harness for the single-coder path. Default comes from `coderProfile`. */\n harness?: string\n /** Model override for the single-coder path. */\n model?: string\n /** Default `['claude-code', 'codex', 'opencode/zai-coding-plan/glm-5.1']` when variants > 1. */\n fanoutHarnesses?: string[]\n /** Optional per-harness model override for `variants > 1`. */\n fanoutModels?: (string | undefined)[]\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 * Detached single-variant turns (taken when `ctx.detachedSessionRef` is set)\n * bypass `runLoop` and therefore emit no loop trace events for that turn.\n */\n traceEmitter?: LoopTraceEmitter\n /** Tick cadence (ms) for the detached single-variant path. Default 5000. */\n detachedTickIntervalMs?: number\n /** Wall-clock cap (ms) forwarded to `driveTurn` for detached turns. */\n detachedWallCapMs?: number\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 = coderTaskFromArgs(args)\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({\n task,\n ...(options.harness ? { harness: options.harness } : {}),\n ...(options.model ? { model: options.model } : {}),\n })\n // Detached dispatch: one session on one box, driven by `driveTurn` ticks\n // instead of a held stream, so the run survives an MCP-process restart\n // (the resume driver re-attaches via the persisted ref). Only the\n // single-variant path detaches — fanout needs N sessions + winner\n // selection over every candidate, which one resume key cannot express.\n if (ctx.detachedSessionRef !== undefined && ctx.updateDetachedSessionRef) {\n const { sessionId } = parseDetachedSessionRef(ctx.detachedSessionRef)\n const rebind = ctx.updateDetachedSessionRef\n const turn = await runDetachedTurn({\n client: sandboxClient,\n spec: agentRunSpec as AgentRunSpec<unknown>,\n prompt: agentRunSpec.taskToPrompt(task),\n sessionId,\n bindSandbox: (sandboxId) => rebind(formatDetachedSessionRef({ sandboxId, sessionId })),\n signal: ctx.signal,\n report: ctx.report,\n ...(options.detachedTickIntervalMs !== undefined\n ? { tickIntervalMs: options.detachedTickIntervalMs }\n : {}),\n ...(options.detachedWallCapMs !== undefined\n ? { wallCapMs: options.detachedWallCapMs }\n : {}),\n })\n const chosen = await settleDetachedCoderTurn(turn, {\n task,\n sessionId,\n signal: ctx.signal,\n ...(options.harness ? { harness: options.harness } : {}),\n ...(options.model ? { model: options.model } : {}),\n ...(options.reviewer ? { reviewer: options.reviewer } : {}),\n })\n ctx.report({ iteration: 1, phase: 'completed' })\n return chosen\n }\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 : {}),\n ...(options.fanoutModels ? { models: options.fanoutModels.slice(0, variants) } : {}),\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\n/**\n * Canonical `DelegateCodeArgs` → `CoderTask` mapping — the single source for\n * the delegate's live dispatch AND the resume driver's settle/message\n * rebuilding, so a resumed record reproduces exactly the task the original\n * process dispatched.\n *\n * @experimental\n */\nexport function coderTaskFromArgs(args: DelegateCodeArgs): CoderTask {\n return {\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}\n\n/** @experimental */\nexport interface SettleDetachedCoderTurnOptions {\n task: CoderTask\n /** Session id of the detached turn — used as the synthesized event id. */\n sessionId: string\n signal: AbortSignal\n harness?: string\n model?: string\n /** Same gate as the streaming path: an unapproved candidate cannot win. */\n reviewer?: CoderReviewer\n}\n\n/**\n * Settle a completed detached coder turn through the same gate the streaming\n * path applies: parse the terminal payload with the coder output adapter,\n * run the mechanical validator (tests/typecheck/forbidden/diff/no-op/secrets),\n * then the optional reviewer. Throws when nothing survives — a resumed or\n * detached run must not return an unvalidated patch.\n *\n * @experimental\n */\nexport async function settleDetachedCoderTurn(\n turn: DetachedTurn,\n options: SettleDetachedCoderTurnOptions,\n): Promise<CoderOutput> {\n const { output, validator } = coderProfile({\n task: options.task,\n ...(options.harness ? { harness: options.harness } : {}),\n ...(options.model ? { model: options.model } : {}),\n })\n const parsed = output.parse(detachedTurnEvents(options.sessionId, turn))\n const verdict = await validator.validate(parsed, { iteration: 0, signal: options.signal })\n if (verdict.valid !== true) throw new Error(noWinnerMessage(options.reviewer))\n if (options.reviewer) {\n const review = await options.reviewer(parsed, options.task, { signal: options.signal })\n if (!review.approved) throw new Error(noWinnerMessage(options.reviewer))\n }\n return parsed\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":["chosen","result"]}
@@ -27,11 +27,11 @@ import {
27
27
  createDelegationStatusHandler,
28
28
  createOtelExporter,
29
29
  hashIdempotencyInput
30
- } from "./chunk-JNPK46YH.js";
30
+ } from "./chunk-VIEDXELL.js";
31
31
  import {
32
32
  createFleetWorkspaceExecutor,
33
33
  createSiblingSandboxExecutor
34
- } from "./chunk-5YDS7BLC.js";
34
+ } from "./chunk-GHX7XOJ2.js";
35
35
  import {
36
36
  runLocalHarness
37
37
  } from "./chunk-GLR25NG7.js";
@@ -246,6 +246,7 @@ function createInProcessExecutor(options) {
246
246
  };
247
247
  return {
248
248
  client,
249
+ placement: "in-process",
249
250
  describe() {
250
251
  return `in-process (repoRoot=${options.repoRoot}, harnesses=[${harnesses.join(",")}]${options.testCmd ? `, testCmd="${options.testCmd}"` : ""}${options.typecheckCmd ? `, typecheckCmd="${options.typecheckCmd}"` : ""})`;
251
252
  }
@@ -647,7 +648,11 @@ function createMcpServer(options = {}) {
647
648
  name: DELEGATE_CODE_TOOL_NAME,
648
649
  description: DELEGATE_CODE_DESCRIPTION,
649
650
  inputSchema: DELEGATE_CODE_INPUT_SCHEMA,
650
- handler: createDelegateCodeHandler({ queue, delegate: options.coderDelegate })
651
+ handler: createDelegateCodeHandler({
652
+ queue,
653
+ delegate: options.coderDelegate,
654
+ ...options.detachedDispatch !== void 0 ? { detachedDispatch: options.detachedDispatch } : {}
655
+ })
651
656
  });
652
657
  }
653
658
  if (options.researcherDelegate) {
@@ -655,7 +660,11 @@ function createMcpServer(options = {}) {
655
660
  name: DELEGATE_RESEARCH_TOOL_NAME,
656
661
  description: DELEGATE_RESEARCH_DESCRIPTION,
657
662
  inputSchema: DELEGATE_RESEARCH_INPUT_SCHEMA,
658
- handler: createDelegateResearchHandler({ queue, delegate: options.researcherDelegate })
663
+ handler: createDelegateResearchHandler({
664
+ queue,
665
+ delegate: options.researcherDelegate,
666
+ ...options.detachedDispatch !== void 0 ? { detachedDispatch: options.detachedDispatch } : {}
667
+ })
659
668
  });
660
669
  }
661
670
  if (options.uiAuditorDelegate) {
@@ -892,4 +901,4 @@ export {
892
901
  createPropagatingTraceEmitter,
893
902
  traceContextToEnv
894
903
  };
895
- //# sourceMappingURL=chunk-T4OQQEE3.js.map
904
+ //# sourceMappingURL=chunk-IQS4HI3F.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/mcp/worktree.ts","../src/mcp/in-process-executor.ts","../src/mcp/bin-helpers.ts","../src/mcp/tools/delegate-ui-audit.ts","../src/mcp/server.ts","../src/mcp/trace-propagation.ts"],"sourcesContent":["/**\n * @experimental\n *\n * Git worktree helpers for the in-process delegation executor. Each\n * delegation runs in its own worktree so multiple parallel harness\n * subprocesses (claude / codex / opencode in a 3-way fanout) don't clobber\n * each other's edits on the shared workspace.\n *\n * Worktrees live under `<repoRoot>/.coder-variants/<runId>/`. After the\n * harness exits + the diff is captured, the worktree is removed.\n *\n * All operations spawn `git` via `child_process.spawn` synchronously\n * (via a `runGit` helper). Stays narrow on purpose: no working-tree\n * staging, no commits, no rebases.\n */\n\nimport { spawn } from 'node:child_process'\n\n/** @experimental */\nexport interface WorktreeHandle {\n /** Absolute path to the worktree directory. */\n path: string\n /** SHA the worktree was created at. */\n baseSha: string\n /** Branch name created for this worktree (typically `delegate/<runId>`). */\n branch: string\n}\n\n/** @experimental */\nexport interface CreateWorktreeOptions {\n /** Absolute path to the main git checkout. */\n repoRoot: string\n /** Unique id for the worktree path + branch. Use the delegation run id. */\n runId: string\n /** Parent directory the worktree lives under. Defaults to `.coder-variants`. */\n variantsDir?: string\n /** Override the base ref (default `HEAD`). */\n baseRef?: string\n /** Test seam — inject a custom git runner. */\n runGit?: GitRunner\n}\n\n/** @experimental */\nexport interface DiffOptions {\n /** Worktree to diff. */\n worktree: WorktreeHandle\n /** What to compare against. Default `worktree.baseSha`. */\n baseRef?: string\n /** Test seam. */\n runGit?: GitRunner\n}\n\n/** @experimental */\nexport interface DiffResult {\n patch: string\n stats: {\n filesChanged: number\n insertions: number\n deletions: number\n }\n}\n\n/** @experimental */\nexport interface RemoveWorktreeOptions {\n worktree: WorktreeHandle\n repoRoot: string\n /** Force removal even if dirty (default true; the loser of a fanout has uncommitted changes). */\n force?: boolean\n /** Test seam. */\n runGit?: GitRunner\n}\n\n/** Pluggable git runner (sync) — replaceable in tests. */\nexport type GitRunner = (\n args: ReadonlyArray<string>,\n opts: { cwd: string },\n) => { stdout: string; stderr: string; exitCode: number }\n\nasync function runGitAsync(\n args: ReadonlyArray<string>,\n cwd: string,\n runner?: GitRunner,\n): Promise<{ stdout: string; stderr: string; exitCode: number }> {\n if (runner) return runner(args, { cwd })\n return new Promise((resolve, reject) => {\n const proc = spawn('git', args, { cwd, stdio: 'pipe' })\n let stdout = ''\n let stderr = ''\n proc.stdout?.on('data', (c) => {\n stdout += String(c)\n })\n proc.stderr?.on('data', (c) => {\n stderr += String(c)\n })\n proc.on('error', reject)\n proc.on('close', (code) => resolve({ stdout, stderr, exitCode: code ?? -1 }))\n })\n}\n\nfunction ensureGitOk(\n step: string,\n result: { stdout: string; stderr: string; exitCode: number },\n): void {\n if (result.exitCode !== 0) {\n throw new Error(\n `worktree: git ${step} failed (exit ${result.exitCode}): ${result.stderr.slice(0, 400)}`,\n )\n }\n}\n\n/** @experimental */\nexport async function createWorktree(options: CreateWorktreeOptions): Promise<WorktreeHandle> {\n const variants = options.variantsDir ?? '.coder-variants'\n const baseRef = options.baseRef ?? 'HEAD'\n const branch = `delegate/${options.runId}`\n const path = `${options.repoRoot.replace(/\\/+$/, '')}/${variants}/${options.runId}`\n\n const headSha = await runGitAsync(['rev-parse', baseRef], options.repoRoot, options.runGit)\n ensureGitOk(`rev-parse ${baseRef}`, headSha)\n\n const add = await runGitAsync(\n ['worktree', 'add', '-b', branch, path, baseRef],\n options.repoRoot,\n options.runGit,\n )\n ensureGitOk(`worktree add ${path}`, add)\n\n return { path, baseSha: headSha.stdout.trim(), branch }\n}\n\n/** @experimental */\nexport async function captureWorktreeDiff(options: DiffOptions): Promise<DiffResult> {\n const baseRef = options.baseRef ?? options.worktree.baseSha\n const patch = await runGitAsync(['diff', baseRef], options.worktree.path, options.runGit)\n // No `ensureGitOk` here — diff returns 0 even when there are no changes.\n\n // Stats: `git diff --shortstat` produces e.g. \" 3 files changed, 42 insertions(+), 10 deletions(-)\".\n const shortstat = await runGitAsync(\n ['diff', '--shortstat', baseRef],\n options.worktree.path,\n options.runGit,\n )\n const stats = parseShortstat(shortstat.stdout)\n return { patch: patch.stdout, stats }\n}\n\nfunction parseShortstat(text: string): DiffResult['stats'] {\n // `text` is the raw stdout of `git diff --shortstat`. Empty when no\n // changes. Parse defensively — the format is stable but we don't trust\n // it for type-safety.\n const out = { filesChanged: 0, insertions: 0, deletions: 0 }\n const filesMatch = text.match(/(\\d+)\\s+files?\\s+changed/)\n if (filesMatch?.[1]) out.filesChanged = Number(filesMatch[1])\n const insertMatch = text.match(/(\\d+)\\s+insertions?/)\n if (insertMatch?.[1]) out.insertions = Number(insertMatch[1])\n const deleteMatch = text.match(/(\\d+)\\s+deletions?/)\n if (deleteMatch?.[1]) out.deletions = Number(deleteMatch[1])\n return out\n}\n\n/** @experimental */\nexport async function removeWorktree(options: RemoveWorktreeOptions): Promise<void> {\n const force = options.force ?? true\n const args = ['worktree', 'remove']\n if (force) args.push('--force')\n args.push(options.worktree.path)\n const result = await runGitAsync(args, options.repoRoot, options.runGit)\n // Don't ensureGitOk — partial-removal scenarios are tolerable; the\n // worktree dir may already be gone (caller deleted it manually).\n if (result.exitCode !== 0 && !/not a working tree/.test(result.stderr)) {\n // Best-effort branch cleanup so the next run can reuse the runId.\n await runGitAsync(\n ['branch', '-D', options.worktree.branch],\n options.repoRoot,\n options.runGit,\n ).catch(() => undefined)\n }\n // Always attempt branch removal — the worktree-remove sometimes leaves\n // the branch behind even when the directory is gone.\n await runGitAsync(\n ['branch', '-D', options.worktree.branch],\n options.repoRoot,\n options.runGit,\n ).catch(() => undefined)\n}\n","/**\n * @experimental\n *\n * In-process delegation executor — when `agent-runtime-mcp` is running\n * inside a sandbox whose image carries the local coding-harness CLIs\n * (claude / codex / opencode), delegations spawn the harness AS A\n * SUBPROCESS against a git worktree on the SAME filesystem instead of\n * provisioning a sibling sandbox.\n *\n * Why: zero provisioning latency, worker diffs land in-place, multi-harness\n * fanout = N parallel subprocesses in N parallel worktrees.\n *\n * Selection:\n * - env `AGENT_RUNTIME_IN_SANDBOX=1` (set by the parent harness at MCP\n * server launch) → in-process executor\n * - env `TANGLE_FLEET_ID=...` → fleet executor (Phase 2.5)\n * - neither → sibling sandbox executor (default)\n *\n * Multi-harness rotation: pass `harnesses: ['claude', 'codex', 'opencode']`\n * to round-robin across calls. A `runLoop` + `FanoutVote(n: 3)` against this\n * executor produces three parallel iterations, each running a different\n * harness on its own worktree.\n *\n * Architecture:\n *\n * client.create() → returns a fake SandboxInstance whose streamPrompt:\n * 1. createWorktree() — git worktree add /workspace/.coder-variants/<id>\n * 2. runLocalHarness() — spawn claude/codex/opencode subprocess\n * 3. captureWorktreeDiff() — git diff HEAD → patch + stats\n * 4. run testCmd + typecheckCmd if specified (the executor doesn't\n * own these — caller wires via task-extractor callback)\n * 5. emit ONE SandboxEvent { type: 'result', data: { result: CoderOutput } }\n * 6. removeWorktree() in finally\n */\n\nimport { randomUUID } from 'node:crypto'\nimport type { CreateSandboxOptions, SandboxEvent, SandboxInstance } from '@tangle-network/sandbox'\nimport type { LoopSandboxPlacement, SandboxClient } from '../runtime'\nimport type { DelegationExecutor } from './executor'\nimport { type LocalHarness, runLocalHarness } from './local-harness'\nimport {\n captureWorktreeDiff,\n createWorktree,\n type GitRunner,\n removeWorktree,\n type WorktreeHandle,\n} from './worktree'\n\n/** @experimental */\nexport interface InProcessExecutorOptions {\n /**\n * Absolute path to the git repo (the workspace inside the sandbox). The\n * executor creates worktrees under `<repoRoot>/.coder-variants/`.\n */\n repoRoot: string\n /**\n * Harnesses to round-robin across calls. With one entry every delegation\n * uses that harness; with three you get fanout diversity for free.\n * Default `['claude']`.\n */\n harnesses?: ReadonlyArray<LocalHarness>\n /**\n * Optional per-delegation test command. Run with `cwd = worktree.path`\n * after the harness exits. The exit code populates\n * `CoderOutput.testResult.passed`.\n */\n testCmd?: string\n /**\n * Optional per-delegation typecheck command. Same shape as `testCmd`.\n */\n typecheckCmd?: string\n /** Wall-clock cap per harness subprocess (ms). Default 5min. */\n harnessTimeoutMs?: number\n /** Wall-clock cap per test/typecheck subprocess (ms). Default 2min. */\n postCheckTimeoutMs?: number\n /** Test seam — override the git runner used by the worktree helpers. */\n runGit?: GitRunner\n /**\n * Test seam — override the harness runner. Defaults to spawning the real\n * CLI via `runLocalHarness`. Tests inject a stub that returns a scripted\n * `LocalHarnessResult`.\n */\n runHarness?: typeof runLocalHarness\n /**\n * Test seam — override the post-check runner. Defaults to spawning the\n * configured `testCmd` / `typecheckCmd` via `child_process.spawn`.\n */\n runPostCheck?: (\n cmd: string,\n cwd: string,\n signal?: AbortSignal,\n ) => Promise<{ exitCode: number; stdout: string; stderr: string }>\n}\n\n/** @experimental */\nexport interface InProcessExecutorDescribePlacement extends LoopSandboxPlacement {\n /**\n * Worktree path in the parent sandbox's filesystem. Set so trace\n * consumers can correlate dispatch events with on-disk artifacts after\n * the worker exits.\n */\n worktreePath?: string\n /** Which harness handled this delegation. */\n harness?: LocalHarness\n}\n\ninterface VirtualSandbox extends SandboxInstance {\n __inProcess: {\n runId: string\n harness: LocalHarness\n worktree?: WorktreeHandle\n }\n}\n\nconst DEFAULT_HARNESS_TIMEOUT_MS = 5 * 60 * 1000\nconst DEFAULT_POSTCHECK_TIMEOUT_MS = 2 * 60 * 1000\n\n/**\n * Build an in-process executor.\n *\n * Returns a {@link DelegationExecutor} whose `client.create()` returns a\n * minimal \"virtual\" SandboxInstance — the kernel calls `streamPrompt(msg)`\n * on it, which runs the local harness on a worktree and emits one\n * `result` event whose `data.result` is a `CoderOutput`-shaped record.\n *\n * Pairs with `coderProfile`'s event parser (it walks the event list\n * back-to-front for the first `type === 'result'`).\n *\n * @experimental\n */\nexport function createInProcessExecutor(options: InProcessExecutorOptions): DelegationExecutor {\n const harnesses =\n options.harnesses && options.harnesses.length > 0\n ? [...options.harnesses]\n : (['claude'] as const)\n const runHarness = options.runHarness ?? runLocalHarness\n const runPostCheck = options.runPostCheck ?? defaultRunPostCheck\n\n let callIndex = 0\n\n const client: SandboxClient = {\n async create(_opts?: CreateSandboxOptions): Promise<SandboxInstance> {\n const runId = randomUUID()\n const harness = harnesses[callIndex % harnesses.length] as LocalHarness\n callIndex += 1\n\n const virtual: VirtualSandbox = {\n // Synthesize the minimum SandboxInstance surface the kernel touches.\n // We CAST through unknown because SandboxInstance is a `declare class`\n // with private fields; we're producing a structural subtype that\n // satisfies the kernel's narrow usage (`box.id`, `box.streamPrompt`).\n id: `in-process-${runId}`,\n __inProcess: { runId, harness },\n // eslint-disable-next-line require-yield\n async *streamPrompt(\n this: VirtualSandbox,\n message: string | unknown[],\n promptOpts?: { signal?: AbortSignal },\n ): AsyncGenerator<SandboxEvent> {\n const taskPrompt =\n typeof message === 'string'\n ? message\n : message\n .map((p) =>\n typeof p === 'object' && p && 'text' in p\n ? String((p as { text: unknown }).text)\n : '',\n )\n .join('\\n')\n\n let worktree: WorktreeHandle | undefined\n try {\n worktree = await createWorktree({\n repoRoot: options.repoRoot,\n runId,\n runGit: options.runGit,\n })\n this.__inProcess.worktree = worktree\n\n // Yield a dispatch-equivalent event so traces see the placement.\n yield {\n type: 'in_process.harness.started',\n data: {\n runId,\n harness,\n worktreePath: worktree.path,\n command: harness,\n },\n }\n\n const harnessResult = await runHarness({\n harness,\n cwd: worktree.path,\n taskPrompt,\n timeoutMs: options.harnessTimeoutMs ?? DEFAULT_HARNESS_TIMEOUT_MS,\n signal: promptOpts?.signal,\n })\n\n yield {\n type: 'in_process.harness.ended',\n data: {\n runId,\n exitCode: harnessResult.exitCode,\n durationMs: harnessResult.durationMs,\n killedBySignal: harnessResult.killedBySignal,\n timedOut: harnessResult.timedOut,\n stdoutBytes: harnessResult.stdout.length,\n stderrBytes: harnessResult.stderr.length,\n },\n }\n\n // Capture diff regardless of exit code — a failed run can still\n // leave a partial diff worth inspecting.\n const diff = await captureWorktreeDiff({ worktree, runGit: options.runGit })\n\n // Optional post-checks. Each runs in the WORKTREE so it sees the\n // harness's edits.\n const testCheck = options.testCmd\n ? await runPostCheck(options.testCmd, worktree.path, promptOpts?.signal).catch(\n (err) => ({\n exitCode: -1,\n stdout: '',\n stderr: err instanceof Error ? err.message : String(err),\n }),\n )\n : { exitCode: 0, stdout: '', stderr: '' }\n const typecheckCheck = options.typecheckCmd\n ? await runPostCheck(options.typecheckCmd, worktree.path, promptOpts?.signal).catch(\n (err) => ({\n exitCode: -1,\n stdout: '',\n stderr: err instanceof Error ? err.message : String(err),\n }),\n )\n : { exitCode: 0, stdout: '', stderr: '' }\n\n const coderOutput = {\n branch: worktree.branch,\n patch: diff.patch,\n testResult: {\n passed: !options.testCmd || testCheck.exitCode === 0,\n output: tail(testCheck.stderr || testCheck.stdout, 4000),\n },\n typecheckResult: {\n passed: !options.typecheckCmd || typecheckCheck.exitCode === 0,\n output: tail(typecheckCheck.stderr || typecheckCheck.stdout, 4000),\n },\n diffStats: diff.stats,\n reviewerNotes:\n harnessResult.exitCode === 0\n ? undefined\n : `harness ${harness} exited ${harnessResult.exitCode}${harnessResult.timedOut ? ' (timed out)' : ''}`,\n }\n\n // The terminal event the coderProfile parser looks for.\n yield {\n type: 'result',\n data: {\n result: coderOutput,\n source: 'in-process-executor',\n harness,\n runId,\n },\n }\n } finally {\n if (worktree) {\n await removeWorktree({\n worktree,\n repoRoot: options.repoRoot,\n runGit: options.runGit,\n }).catch(() => undefined)\n }\n }\n },\n } as unknown as VirtualSandbox\n\n return virtual\n },\n describePlacement(box: SandboxInstance): InProcessExecutorDescribePlacement {\n const sandboxId = (box as unknown as { id?: string }).id\n const meta = (box as VirtualSandbox).__inProcess\n return {\n kind: 'sibling',\n sandboxId,\n worktreePath: meta?.worktree?.path,\n harness: meta?.harness,\n }\n },\n }\n\n return {\n client,\n placement: 'in-process',\n describe(): string {\n return `in-process (repoRoot=${options.repoRoot}, harnesses=[${harnesses.join(',')}]${\n options.testCmd ? `, testCmd=\"${options.testCmd}\"` : ''\n }${options.typecheckCmd ? `, typecheckCmd=\"${options.typecheckCmd}\"` : ''})`\n },\n }\n}\n\nasync function defaultRunPostCheck(\n cmd: string,\n cwd: string,\n signal?: AbortSignal,\n): Promise<{ exitCode: number; stdout: string; stderr: string }> {\n const { spawn } = await import('node:child_process')\n return new Promise((resolve, reject) => {\n // Run via sh -c so multi-word commands (\"pnpm test\") and shell features work.\n const child = spawn('sh', ['-c', cmd], { cwd, stdio: 'pipe' })\n let stdout = ''\n let stderr = ''\n child.stdout?.on('data', (c) => {\n stdout += String(c)\n })\n child.stderr?.on('data', (c) => {\n stderr += String(c)\n })\n if (signal) {\n const onAbort = () => {\n if (!child.killed) child.kill('SIGTERM')\n }\n if (signal.aborted) onAbort()\n else signal.addEventListener('abort', onAbort, { once: true })\n }\n const killTimer = setTimeout(() => {\n if (!child.killed) child.kill('SIGTERM')\n }, DEFAULT_POSTCHECK_TIMEOUT_MS)\n if (typeof (killTimer as { unref?: () => void }).unref === 'function') {\n ;(killTimer as { unref: () => void }).unref()\n }\n child.on('error', (err) => {\n clearTimeout(killTimer)\n reject(err)\n })\n child.on('close', (code) => {\n clearTimeout(killTimer)\n resolve({ exitCode: code ?? -1, stdout, stderr })\n })\n })\n}\n\nfunction tail(text: string, max: number): string {\n if (text.length <= max) return text\n return text.slice(text.length - max)\n}\n","/**\n * @experimental\n *\n * Helpers extracted from `bin.ts` so the env-detection + executor-selection\n * logic is unit-testable without spawning a subprocess. The bin imports from\n * here; tests import from here directly.\n */\n\nimport type { SandboxClient } from '../runtime'\nimport {\n createFleetWorkspaceExecutor,\n createSiblingSandboxExecutor,\n type DelegationExecutor,\n type FleetHandle,\n} from './executor'\nimport { createInProcessExecutor } from './in-process-executor'\nimport type { LocalHarness } from './local-harness'\n\n/** @experimental */\nexport interface DetectExecutorArgs {\n sandboxClient: SandboxClient\n /** Raw env (defaults to `process.env`). Pass an explicit map for tests. */\n env?: Record<string, string | undefined>\n /**\n * Override how a fleet handle is resolved from the client + fleet id. The\n * default reads `client.fleets.get(fleetId)` and validates the returned\n * shape against the structural `FleetHandle` contract.\n */\n resolveFleet?: (client: SandboxClient, fleetId: string) => Promise<FleetHandle>\n}\n\n/**\n * Pick the right executor for an MCP server invocation based on env vars.\n *\n * - `TANGLE_FLEET_ID` set → fleet-workspace placement; resolves the handle\n * via `sandboxClient.fleets.get(...)`.\n * - Otherwise → sibling-sandbox placement; each delegation creates a fresh\n * sandbox via `sandboxClient.create(...)`.\n *\n * Fails loud (throws) when fleet mode is requested but the SDK shape is\n * incompatible — the operator chose fleet semantics, silently degrading to\n * sibling mode would lie about workspace topology.\n *\n * @experimental\n */\nexport async function detectExecutor(args: DetectExecutorArgs): Promise<DelegationExecutor> {\n const env = args.env ?? process.env\n\n // In-process (Phase 2.8): parent harness sets AGENT_RUNTIME_IN_SANDBOX=1\n // and points us at the workspace root. Highest-priority — when this is\n // set, delegations spawn local harness CLIs on git worktrees in the\n // SAME filesystem instead of provisioning sibling sandboxes.\n if (env.AGENT_RUNTIME_IN_SANDBOX === '1') {\n const repoRoot = env.AGENT_RUNTIME_REPO_ROOT?.trim()\n if (!repoRoot) {\n throw new Error(\n 'agent-runtime-mcp: AGENT_RUNTIME_IN_SANDBOX=1 requires AGENT_RUNTIME_REPO_ROOT to point at the workspace root',\n )\n }\n return createInProcessExecutor({\n repoRoot,\n harnesses: parseHarnesses(env.AGENT_RUNTIME_LOCAL_HARNESSES),\n testCmd: env.AGENT_RUNTIME_TEST_CMD?.trim() || undefined,\n typecheckCmd: env.AGENT_RUNTIME_TYPECHECK_CMD?.trim() || undefined,\n })\n }\n\n const fleetId = parseFleetId(env.TANGLE_FLEET_ID)\n if (!fleetId) {\n return createSiblingSandboxExecutor({ client: args.sandboxClient })\n }\n const resolveFleet = args.resolveFleet ?? defaultResolveFleet\n const fleet = await resolveFleet(args.sandboxClient, fleetId)\n const excludeMachineIds = parseList(env.TANGLE_FLEET_EXCLUDE_MACHINES)\n return createFleetWorkspaceExecutor({\n fleet,\n excludeMachineIds,\n })\n}\n\nconst KNOWN_HARNESSES: ReadonlyArray<LocalHarness> = ['claude', 'codex', 'opencode']\n\nfunction parseHarnesses(raw: string | undefined): ReadonlyArray<LocalHarness> | undefined {\n if (!raw) return undefined\n const parts = raw\n .split(',')\n .map((s) => s.trim())\n .filter(Boolean)\n if (parts.length === 0) return undefined\n for (const part of parts) {\n if (!KNOWN_HARNESSES.includes(part as LocalHarness)) {\n throw new Error(\n `agent-runtime-mcp: AGENT_RUNTIME_LOCAL_HARNESSES contains unknown harness \"${part}\". Expected: ${KNOWN_HARNESSES.join(', ')}.`,\n )\n }\n }\n return parts as LocalHarness[]\n}\n\ninterface FleetsApi {\n get(fleetId: string): Promise<unknown>\n}\n\nasync function defaultResolveFleet(\n sandboxClient: SandboxClient,\n fleetId: string,\n): Promise<FleetHandle> {\n const fleets = (sandboxClient as unknown as { fleets?: FleetsApi }).fleets\n if (!fleets || typeof fleets.get !== 'function') {\n throw new Error(\n 'agent-runtime-mcp: the configured sandbox client does not expose `.fleets.get`; upgrade @tangle-network/sandbox to >= 0.2.1 or unset TANGLE_FLEET_ID.',\n )\n }\n const raw = await fleets.get(fleetId)\n if (!raw || typeof raw !== 'object') {\n throw new Error(`agent-runtime-mcp: fleets.get(${fleetId}) returned no handle`)\n }\n const handle = raw as Partial<FleetHandle>\n if (typeof handle.fleetId !== 'string' || !Array.isArray(handle.ids)) {\n throw new Error(\n `agent-runtime-mcp: fleet handle for ${fleetId} is missing fleetId/ids — incompatible sandbox SDK shape`,\n )\n }\n if (typeof handle.sandbox !== 'function') {\n throw new Error(\n `agent-runtime-mcp: fleet handle for ${fleetId} is missing sandbox(machineId) — incompatible sandbox SDK shape`,\n )\n }\n return handle as FleetHandle\n}\n\nfunction parseFleetId(raw: string | undefined): string | undefined {\n if (typeof raw !== 'string') return undefined\n const trimmed = raw.trim()\n return trimmed.length > 0 ? trimmed : undefined\n}\n\nfunction parseList(raw: string | undefined): string[] | undefined {\n if (!raw) return undefined\n const list = raw\n .split(',')\n .map((entry) => entry.trim())\n .filter(Boolean)\n return list.length > 0 ? list : undefined\n}\n","/**\n * @experimental\n *\n * `delegate_ui_audit` MCP tool — async kickoff for UI audit runs. Same\n * async semantics as `delegate_code` / `delegate_research`: validates the\n * input, computes an idempotency key over the canonical fields, hands\n * the task to the queue, and returns a taskId. Identical inputs return\n * the same taskId.\n *\n * The handler does not import the auditor profile directly — consumers\n * inject a `UiAuditorDelegate` via `createMcpServer({ uiAuditorDelegate })`.\n * The delegate is the seam where the consumer chooses the judge (vision\n * model) and the `SandboxClient` (in-process Playwright vs fleet vs\n * remote browser). agent-runtime ships the in-process client under\n * `./profiles` so consumers who want the canonical setup can wire it\n * with a few lines.\n */\n\nimport path from 'node:path'\nimport { UI_LENSES, type UiLens } from '../../profiles/ui-auditor/substrate'\nimport type { UiAuditorDelegate } from '../delegates'\nimport {\n type DelegateUiAuditArgs,\n type DelegateUiAuditResult,\n type DelegationTaskQueue,\n hashIdempotencyInput,\n} from '../task-queue'\n\n/** @experimental */\nexport const DELEGATE_UI_AUDIT_TOOL_NAME = 'delegate_ui_audit'\n\n/** @experimental */\nexport const DELEGATE_UI_AUDIT_DESCRIPTION = [\n 'Delegate a UI/UX audit to a vision-driven auditor that produces self-contained',\n 'GitHub-issue-ready Markdown findings — one file per finding, with embedded',\n 'screenshot evidence and a suggested fix.',\n '',\n 'Use when: you want a thorough pass over a running web app for consistency,',\n 'hierarchy, layout, ux-flow, duplication, accessibility, responsive, states,',\n 'content, interaction, or perceived-performance issues. The auditor iterates',\n 'lens-by-lens so each pass finds new classes of issues; the workspace registry',\n 'deduplicates across iterations.',\n '',\n 'Returns immediately with a taskId. Poll delegation_status to retrieve the',\n 'workspace path + indexed findings (typically minutes per audited route).',\n 'Identical inputs return the same taskId — safe to retry.',\n '',\n 'Output layout under workspaceDir:',\n ' registry.json — finding index + capture sidecar',\n ' index.md — human-readable rollup',\n ' issues/NNN--<lens>--<slug>.md — one self-contained GitHub-issue',\n ' screenshots/<route>--<viewport>.png — capture archive',\n '',\n 'Multi-tenant isolation: every finding is scoped to `namespace` when set.',\n \"Never pass another tenant's namespace.\",\n].join('\\n')\n\nconst VIEWPORT_SCHEMA = {\n type: 'object',\n properties: {\n width: { type: 'integer', minimum: 1 },\n height: { type: 'integer', minimum: 1 },\n },\n required: ['width', 'height'],\n additionalProperties: false,\n} as const\n\nconst ROUTE_SCHEMA = {\n type: 'object',\n properties: {\n name: { type: 'string', description: 'Stable route name (used in screenshot filenames).' },\n url: { type: 'string', description: 'Fully-qualified URL.' },\n viewports: {\n type: 'array',\n items: VIEWPORT_SCHEMA,\n description: 'Viewports to capture at. Default [{1280, 800}].',\n },\n fullPage: { type: 'boolean' },\n waitFor: { type: 'string', description: 'CSS selector to wait for before capturing.' },\n },\n required: ['name', 'url'],\n additionalProperties: false,\n} as const\n\n/** @experimental */\nexport const DELEGATE_UI_AUDIT_INPUT_SCHEMA = {\n type: 'object',\n properties: {\n workspaceDir: { type: 'string', description: 'Absolute path for the audit workspace.' },\n routes: { type: 'array', items: ROUTE_SCHEMA, minItems: 1 },\n namespace: { type: 'string', description: 'Multi-tenant scope.' },\n config: {\n type: 'object',\n properties: {\n lenses: {\n type: 'array',\n items: { type: 'string', enum: [...UI_LENSES] },\n description: 'Lenses to iterate. Default: every lens except \"other\".',\n },\n maxIterations: { type: 'integer', minimum: 1 },\n maxConcurrency: { type: 'integer', minimum: 1 },\n productContext: { type: 'string' },\n },\n additionalProperties: false,\n },\n },\n required: ['workspaceDir', 'routes'],\n additionalProperties: false,\n} as const\n\nconst PER_LENS_PER_ROUTE_ESTIMATE_MS = 45_000\n\n/** @experimental */\nexport function validateDelegateUiAuditArgs(raw: unknown): DelegateUiAuditArgs {\n if (raw === null || typeof raw !== 'object') {\n throw new TypeError('delegate_ui_audit: arguments must be an object')\n }\n const value = raw as Record<string, unknown>\n const workspaceDir = value.workspaceDir\n if (typeof workspaceDir !== 'string' || workspaceDir.trim().length === 0) {\n throw new TypeError('delegate_ui_audit: `workspaceDir` must be a non-empty string')\n }\n // Reject relative paths at the boundary. Without this, the writer joins a\n // relative workspaceDir against the process CWD, so the same MCP call\n // produces different file layouts depending on where the server was\n // launched — a silent fail-loud violation for a public API.\n const trimmedWs = workspaceDir.trim()\n if (!path.isAbsolute(trimmedWs)) {\n throw new TypeError(\n `delegate_ui_audit: \\`workspaceDir\\` must be an absolute path (got ${JSON.stringify(workspaceDir)})`,\n )\n }\n // Reject `..` segments. An absolute path like `/tmp/../../etc` passes\n // `isAbsolute`, then `path.join(workspaceDir, 'issues', …)` normalises it\n // to `/etc/issues/…`, escaping the intended workspace. Check the RAW\n // input split on the platform separator; `path.resolve` already collapses\n // `..` and would silently launder a traversal attempt past us.\n if (trimmedWs.split(path.sep).includes('..')) {\n throw new TypeError(\n `delegate_ui_audit: \\`workspaceDir\\` must not contain '..' segments (got ${JSON.stringify(workspaceDir)})`,\n )\n }\n const routesRaw = value.routes\n if (!Array.isArray(routesRaw) || routesRaw.length === 0) {\n throw new TypeError('delegate_ui_audit: `routes` must be a non-empty array')\n }\n const routes = routesRaw.map((r, i) => validateRoute(r, i))\n const args: DelegateUiAuditArgs = { workspaceDir: workspaceDir.trim(), routes }\n if (value.namespace !== undefined) {\n if (typeof value.namespace !== 'string' || value.namespace.trim().length === 0) {\n throw new TypeError('delegate_ui_audit: `namespace` must be a non-empty string when set')\n }\n args.namespace = value.namespace.trim()\n }\n if (value.config !== undefined) {\n args.config = validateConfig(value.config)\n }\n return args\n}\n\nfunction validateRoute(raw: unknown, index: number): DelegateUiAuditArgs['routes'][number] {\n if (raw === null || typeof raw !== 'object') {\n throw new TypeError(`delegate_ui_audit: routes[${index}] must be an object`)\n }\n const v = raw as Record<string, unknown>\n if (typeof v.name !== 'string' || v.name.trim().length === 0) {\n throw new TypeError(`delegate_ui_audit: routes[${index}].name must be a non-empty string`)\n }\n // Defense-in-depth: the slug helpers downstream strip non-alphanumerics, so\n // a route name with `..`, `/`, `\\`, or NUL cannot actually escape the\n // workspace. Rejecting them at the wire boundary keeps the invariant\n // explicit and matches the lens-allowlist style of validation.\n const trimmedName = v.name.trim()\n if (/[./\\\\]/.test(trimmedName) || trimmedName.includes('\\u0000')) {\n throw new TypeError(\n `delegate_ui_audit: routes[${index}].name must not contain path separators, dots, or NUL (got ${JSON.stringify(v.name)})`,\n )\n }\n if (typeof v.url !== 'string' || v.url.trim().length === 0) {\n throw new TypeError(`delegate_ui_audit: routes[${index}].url must be a non-empty string`)\n }\n // Parse + restrict to http/https. The in-process auditor client navigates\n // to whatever URL the MCP caller supplies; permitting `file://`, `data:`,\n // `javascript:` would let an attacker controlling the tool input pull\n // local files or run inline scripts.\n let parsedUrl: URL\n try {\n parsedUrl = new URL(v.url)\n } catch {\n throw new TypeError(\n `delegate_ui_audit: routes[${index}].url is not a parseable URL (got ${JSON.stringify(v.url)})`,\n )\n }\n if (parsedUrl.protocol !== 'http:' && parsedUrl.protocol !== 'https:') {\n throw new TypeError(\n `delegate_ui_audit: routes[${index}].url must use http or https (got ${parsedUrl.protocol})`,\n )\n }\n const out: DelegateUiAuditArgs['routes'][number] = { name: v.name.trim(), url: v.url.trim() }\n if (v.viewports !== undefined) {\n if (!Array.isArray(v.viewports) || v.viewports.length === 0) {\n throw new TypeError(\n `delegate_ui_audit: routes[${index}].viewports must be a non-empty array when set`,\n )\n }\n out.viewports = v.viewports.map((vp, j) => validateViewport(vp, index, j))\n }\n if (v.fullPage !== undefined) {\n if (typeof v.fullPage !== 'boolean') {\n throw new TypeError(`delegate_ui_audit: routes[${index}].fullPage must be a boolean`)\n }\n out.fullPage = v.fullPage\n }\n if (v.waitFor !== undefined) {\n if (typeof v.waitFor !== 'string' || v.waitFor.trim().length === 0) {\n throw new TypeError(\n `delegate_ui_audit: routes[${index}].waitFor must be a non-empty string when set`,\n )\n }\n out.waitFor = v.waitFor.trim()\n }\n return out\n}\n\nfunction validateViewport(\n raw: unknown,\n routeIndex: number,\n viewportIndex: number,\n): { width: number; height: number } {\n if (raw === null || typeof raw !== 'object') {\n throw new TypeError(\n `delegate_ui_audit: routes[${routeIndex}].viewports[${viewportIndex}] must be an object`,\n )\n }\n const v = raw as Record<string, unknown>\n const w = Number(v.width)\n const h = Number(v.height)\n if (!Number.isInteger(w) || w <= 0 || !Number.isInteger(h) || h <= 0) {\n throw new RangeError(\n `delegate_ui_audit: routes[${routeIndex}].viewports[${viewportIndex}] must have positive integer width/height`,\n )\n }\n return { width: w, height: h }\n}\n\nfunction validateConfig(raw: unknown): DelegateUiAuditArgs['config'] {\n if (raw === null || typeof raw !== 'object') {\n throw new TypeError('delegate_ui_audit: `config` must be an object')\n }\n const v = raw as Record<string, unknown>\n const out: NonNullable<DelegateUiAuditArgs['config']> = {}\n if (v.lenses !== undefined) {\n if (!Array.isArray(v.lenses) || v.lenses.length === 0) {\n throw new TypeError('delegate_ui_audit: `config.lenses` must be a non-empty array when set')\n }\n const knownSet = new Set<string>(UI_LENSES)\n const lenses: UiLens[] = []\n for (let i = 0; i < v.lenses.length; i += 1) {\n const lens = v.lenses[i]\n if (typeof lens !== 'string' || !knownSet.has(lens)) {\n throw new TypeError(\n `delegate_ui_audit: config.lenses[${i}] must be one of ${UI_LENSES.join('|')}`,\n )\n }\n lenses.push(lens as UiLens)\n }\n out.lenses = lenses\n }\n if (v.maxIterations !== undefined) {\n const n = Number(v.maxIterations)\n if (!Number.isInteger(n) || n < 1) {\n throw new RangeError('delegate_ui_audit: `config.maxIterations` must be a positive integer')\n }\n out.maxIterations = n\n }\n if (v.maxConcurrency !== undefined) {\n const n = Number(v.maxConcurrency)\n if (!Number.isInteger(n) || n < 1) {\n throw new RangeError('delegate_ui_audit: `config.maxConcurrency` must be a positive integer')\n }\n out.maxConcurrency = n\n }\n if (v.productContext !== undefined) {\n if (typeof v.productContext !== 'string') {\n throw new TypeError('delegate_ui_audit: `config.productContext` must be a string')\n }\n out.productContext = v.productContext\n }\n return out\n}\n\n/** @experimental */\nexport interface DelegateUiAuditHandlerOptions {\n queue: DelegationTaskQueue\n delegate: UiAuditorDelegate\n estimateDurationMs?: (args: DelegateUiAuditArgs) => number\n}\n\n/** @experimental */\nexport function createDelegateUiAuditHandler(\n options: DelegateUiAuditHandlerOptions,\n): (raw: unknown) => Promise<DelegateUiAuditResult> {\n const estimateDurationMs = options.estimateDurationMs ?? defaultEstimate\n return async (raw) => {\n const args = validateDelegateUiAuditArgs(raw)\n const idempotencyKey = hashIdempotencyInput({\n profile: 'ui-auditor',\n workspaceDir: args.workspaceDir,\n routes: args.routes,\n namespace: args.namespace,\n config: args.config,\n })\n const submitted = options.queue.submit<DelegateUiAuditArgs>({\n profile: 'ui-auditor',\n args,\n namespace: args.namespace,\n idempotencyKey,\n run: async (ctx) => options.delegate(args, ctx),\n })\n return {\n taskId: submitted.taskId,\n estimatedDurationMs: estimateDurationMs(args),\n }\n }\n}\n\nfunction defaultEstimate(args: DelegateUiAuditArgs): number {\n const lenses = args.config?.lenses?.length ?? UI_LENSES.length - 1\n const routes = args.routes.length\n return PER_LENS_PER_ROUTE_ESTIMATE_MS * lenses * routes\n}\n","/**\n * @experimental\n *\n * Stdio JSON-RPC MCP server exposing the 5 delegation tools to sandbox\n * coding-harness agents (claude-code, codex, opencode, ...).\n *\n * The server is transport-bound but topology-free: tool execution is\n * delegated to handler functions composed from a queue, a feedback\n * store, and per-profile run delegates. Consumers wire those at\n * construction time. The `agent-runtime-mcp` bin spins up a default\n * configuration for the common case (real sandbox client + coder).\n *\n * Wire protocol: line-delimited JSON-RPC 2.0 over stdio. Each line is\n * one request; each response is one line. `tools/list` and `tools/call`\n * mirror the MCP 2024-11-05 spec; we do not pull in\n * `@modelcontextprotocol/sdk` to keep the dependency footprint zero.\n */\n\nimport { createInterface, type Interface as ReadlineInterface } from 'node:readline'\nimport { Readable, Writable } from 'node:stream'\nimport { ValidationError } from '../errors'\nimport type { CoderDelegate, ResearcherDelegate, UiAuditorDelegate } from './delegates'\nimport { type FeedbackStore, InMemoryFeedbackStore } from './feedback-store'\nimport { DelegationTaskQueue } from './task-queue'\nimport {\n createDelegateCodeHandler,\n DELEGATE_CODE_DESCRIPTION,\n DELEGATE_CODE_INPUT_SCHEMA,\n DELEGATE_CODE_TOOL_NAME,\n} from './tools/delegate-code'\nimport {\n createDelegateFeedbackHandler,\n DELEGATE_FEEDBACK_DESCRIPTION,\n DELEGATE_FEEDBACK_INPUT_SCHEMA,\n DELEGATE_FEEDBACK_TOOL_NAME,\n} from './tools/delegate-feedback'\nimport {\n createDelegateResearchHandler,\n DELEGATE_RESEARCH_DESCRIPTION,\n DELEGATE_RESEARCH_INPUT_SCHEMA,\n DELEGATE_RESEARCH_TOOL_NAME,\n} from './tools/delegate-research'\nimport {\n createDelegateUiAuditHandler,\n DELEGATE_UI_AUDIT_DESCRIPTION,\n DELEGATE_UI_AUDIT_INPUT_SCHEMA,\n DELEGATE_UI_AUDIT_TOOL_NAME,\n} from './tools/delegate-ui-audit'\nimport {\n createDelegationHistoryHandler,\n DELEGATION_HISTORY_DESCRIPTION,\n DELEGATION_HISTORY_INPUT_SCHEMA,\n DELEGATION_HISTORY_TOOL_NAME,\n} from './tools/delegation-history'\nimport {\n createDelegationStatusHandler,\n DELEGATION_STATUS_DESCRIPTION,\n DELEGATION_STATUS_INPUT_SCHEMA,\n DELEGATION_STATUS_TOOL_NAME,\n} from './tools/delegation-status'\n\n/** @experimental */\nexport interface McpServerOptions {\n /** Required to enable delegate_code. */\n coderDelegate?: CoderDelegate\n /**\n * Required to enable delegate_research. The substrate cannot ship a\n * default — wire one that closes over your `runLoop` + a\n * researcher profile (typically `@tangle-network/agent-knowledge`'s\n * `researcherProfile` / `multiHarnessResearcherFanout`).\n */\n researcherDelegate?: ResearcherDelegate\n /**\n * Required to enable delegate_ui_audit. Wire one that closes over your\n * `runLoop` + `uiAuditorProfile` + a `SandboxClient` (the\n * canonical in-process choice is `createInProcessUiAuditClient` from\n * `@tangle-network/agent-runtime/profiles`) + your vision judge.\n */\n uiAuditorDelegate?: UiAuditorDelegate\n /** Override the default in-memory feedback store. */\n feedbackStore?: FeedbackStore\n /** Override the default in-memory task queue. */\n queue?: DelegationTaskQueue\n /**\n * Record deterministic detached-session resume keys on single-variant\n * coder/researcher submissions so a durable queue can resume them after a\n * restart. Enable only when the wired delegates dispatch via sandbox\n * sessions (`driveTurn`) AND `queue` persists records — the keys are inert\n * otherwise. The bin turns this on alongside the durable store for\n * session-backed (sibling/fleet) placements.\n */\n detachedDispatch?: boolean\n /**\n * Extra tools to serve alongside the delegation tools, for example\n * `createCoordinationTools(...).tools`. Registered after the built-ins; a\n * duplicate name throws so delegation tools cannot be shadowed silently.\n */\n extraTools?: McpToolDescriptor[]\n /** Server display name surfaced via `initialize`. Default `'agent-runtime-mcp'`. */\n serverName?: string\n /** Server version surfaced via `initialize`. Default = the package version baked at build time. */\n serverVersion?: string\n}\n\n/** @experimental */\nexport interface McpToolDescriptor {\n name: string\n description: string\n inputSchema: Record<string, unknown>\n handler: (raw: unknown) => Promise<unknown>\n}\n\n/** @experimental */\nexport interface McpServer {\n /** Tools currently registered (depend on which delegates were wired). */\n readonly tools: ReadonlyMap<string, McpToolDescriptor>\n /** The underlying queue — exposed so tests can introspect it. */\n readonly queue: DelegationTaskQueue\n /** The feedback store — exposed for the same reason. */\n readonly feedbackStore: FeedbackStore\n /** Handle a single parsed JSON-RPC message. Returns the response object (or `null` for notifications). */\n handle(message: JsonRpcMessage): Promise<JsonRpcResponse | null>\n /** Drive the server on a stdio-shaped transport until `stop()` is called. */\n serve(transport?: McpTransport): Promise<void>\n /** Stop a `serve` call. Subsequent requests are rejected. */\n stop(): void\n}\n\n/** @experimental */\nexport interface McpTransport {\n input: NodeJS.ReadableStream\n output: NodeJS.WritableStream\n}\n\n/** @experimental */\nexport interface JsonRpcMessage {\n jsonrpc: '2.0'\n id?: number | string | null\n method: string\n params?: unknown\n}\n\n/** @experimental */\nexport interface JsonRpcResponse {\n jsonrpc: '2.0'\n id: number | string | null\n result?: unknown\n error?: { code: number; message: string; data?: unknown }\n}\n\nconst PROTOCOL_VERSION = '2024-11-05'\nconst DEFAULT_SERVER_NAME = 'agent-runtime-mcp'\nconst DEFAULT_SERVER_VERSION = '0.22.0'\n\n/** @experimental */\nexport function createMcpServer(options: McpServerOptions = {}): McpServer {\n const queue = options.queue ?? new DelegationTaskQueue()\n const feedbackStore = options.feedbackStore ?? new InMemoryFeedbackStore()\n const serverName = options.serverName ?? DEFAULT_SERVER_NAME\n const serverVersion = options.serverVersion ?? DEFAULT_SERVER_VERSION\n\n const tools = new Map<string, McpToolDescriptor>()\n\n if (options.coderDelegate) {\n tools.set(DELEGATE_CODE_TOOL_NAME, {\n name: DELEGATE_CODE_TOOL_NAME,\n description: DELEGATE_CODE_DESCRIPTION,\n inputSchema: DELEGATE_CODE_INPUT_SCHEMA as unknown as Record<string, unknown>,\n handler: createDelegateCodeHandler({\n queue,\n delegate: options.coderDelegate,\n ...(options.detachedDispatch !== undefined\n ? { detachedDispatch: options.detachedDispatch }\n : {}),\n }),\n })\n }\n if (options.researcherDelegate) {\n tools.set(DELEGATE_RESEARCH_TOOL_NAME, {\n name: DELEGATE_RESEARCH_TOOL_NAME,\n description: DELEGATE_RESEARCH_DESCRIPTION,\n inputSchema: DELEGATE_RESEARCH_INPUT_SCHEMA as unknown as Record<string, unknown>,\n handler: createDelegateResearchHandler({\n queue,\n delegate: options.researcherDelegate,\n ...(options.detachedDispatch !== undefined\n ? { detachedDispatch: options.detachedDispatch }\n : {}),\n }),\n })\n }\n if (options.uiAuditorDelegate) {\n tools.set(DELEGATE_UI_AUDIT_TOOL_NAME, {\n name: DELEGATE_UI_AUDIT_TOOL_NAME,\n description: DELEGATE_UI_AUDIT_DESCRIPTION,\n inputSchema: DELEGATE_UI_AUDIT_INPUT_SCHEMA as unknown as Record<string, unknown>,\n handler: createDelegateUiAuditHandler({ queue, delegate: options.uiAuditorDelegate }),\n })\n }\n tools.set(DELEGATE_FEEDBACK_TOOL_NAME, {\n name: DELEGATE_FEEDBACK_TOOL_NAME,\n description: DELEGATE_FEEDBACK_DESCRIPTION,\n inputSchema: DELEGATE_FEEDBACK_INPUT_SCHEMA as unknown as Record<string, unknown>,\n handler: createDelegateFeedbackHandler({ queue, store: feedbackStore }),\n })\n tools.set(DELEGATION_STATUS_TOOL_NAME, {\n name: DELEGATION_STATUS_TOOL_NAME,\n description: DELEGATION_STATUS_DESCRIPTION,\n inputSchema: DELEGATION_STATUS_INPUT_SCHEMA as unknown as Record<string, unknown>,\n handler: createDelegationStatusHandler({ queue }),\n })\n tools.set(DELEGATION_HISTORY_TOOL_NAME, {\n name: DELEGATION_HISTORY_TOOL_NAME,\n description: DELEGATION_HISTORY_DESCRIPTION,\n inputSchema: DELEGATION_HISTORY_INPUT_SCHEMA as unknown as Record<string, unknown>,\n handler: createDelegationHistoryHandler({ queue }),\n })\n for (const tool of options.extraTools ?? []) {\n if (tools.has(tool.name)) {\n throw new ValidationError(\n `createMcpServer: extra tool \"${tool.name}\" shadows a built-in tool`,\n )\n }\n tools.set(tool.name, tool)\n }\n\n let stopped = false\n let activeReadline: ReadlineInterface | undefined\n\n async function handle(message: JsonRpcMessage): Promise<JsonRpcResponse | null> {\n if (stopped) {\n return rpcError(message.id ?? null, -32099, 'server stopped')\n }\n if (message.method === 'initialize') {\n return rpcResult(message.id ?? null, {\n protocolVersion: PROTOCOL_VERSION,\n capabilities: { tools: {} },\n serverInfo: { name: serverName, version: serverVersion },\n })\n }\n if (message.method === 'notifications/initialized') {\n // MCP clients send this after the handshake; it has no id and expects\n // no response.\n return null\n }\n if (message.method === 'tools/list') {\n return rpcResult(message.id ?? null, {\n tools: [...tools.values()].map((tool) => ({\n name: tool.name,\n description: tool.description,\n inputSchema: tool.inputSchema,\n })),\n })\n }\n if (message.method === 'tools/call') {\n const params = (message.params ?? {}) as { name?: unknown; arguments?: unknown }\n const name = typeof params.name === 'string' ? params.name : ''\n const tool = tools.get(name)\n if (!tool) {\n return rpcError(message.id ?? null, -32601, `unknown tool: ${name}`)\n }\n try {\n const output = await tool.handler(params.arguments ?? {})\n return rpcResult(message.id ?? null, {\n content: [{ type: 'text', text: JSON.stringify(output) }],\n structuredContent: output,\n isError: false,\n })\n } catch (err) {\n const reason = err instanceof Error ? err.message : String(err)\n const code = err instanceof TypeError || err instanceof RangeError ? -32602 : -32000\n return rpcError(message.id ?? null, code, reason)\n }\n }\n if (message.id === undefined || message.id === null) return null\n return rpcError(message.id, -32601, `unknown method: ${message.method}`)\n }\n\n async function serve(transport?: McpTransport): Promise<void> {\n const input = transport?.input ?? process.stdin\n const output = transport?.output ?? process.stdout\n const rl = createInterface({ input, crlfDelay: Number.POSITIVE_INFINITY })\n activeReadline = rl\n return new Promise<void>((resolve, reject) => {\n rl.on('line', (line) => {\n const trimmed = line.trim()\n if (!trimmed) return\n let parsed: JsonRpcMessage | undefined\n try {\n parsed = JSON.parse(trimmed) as JsonRpcMessage\n } catch (err) {\n writeResponse(output, rpcError(null, -32700, `parse error: ${(err as Error).message}`))\n return\n }\n if (!parsed || parsed.jsonrpc !== '2.0' || typeof parsed.method !== 'string') {\n writeResponse(output, rpcError(parsed?.id ?? null, -32600, 'invalid request'))\n return\n }\n void handle(parsed).then((response) => {\n if (response) writeResponse(output, response)\n })\n })\n rl.on('close', () => resolve())\n rl.on('error', (err) => reject(err))\n if (stopped) {\n rl.close()\n resolve()\n }\n })\n }\n\n function stop(): void {\n stopped = true\n activeReadline?.close()\n activeReadline = undefined\n }\n\n return {\n tools,\n queue,\n feedbackStore,\n handle,\n serve,\n stop,\n }\n}\n\nfunction rpcResult(id: number | string | null, result: unknown): JsonRpcResponse {\n return { jsonrpc: '2.0', id, result }\n}\n\nfunction rpcError(\n id: number | string | null,\n code: number,\n message: string,\n data?: unknown,\n): JsonRpcResponse {\n return {\n jsonrpc: '2.0',\n id,\n error: data === undefined ? { code, message } : { code, message, data },\n }\n}\n\nfunction writeResponse(output: NodeJS.WritableStream, response: JsonRpcResponse): void {\n output.write(`${JSON.stringify(response)}\\n`)\n}\n\n/**\n * In-process pair of `Readable` + `Writable` streams suitable for driving\n * `server.serve(...)` from a test. Returns the agent-side stream (the\n * client writes to it) and the server-side stream (the test reads from it).\n *\n * @experimental\n */\nexport function createInProcessTransport(): {\n transport: McpTransport\n clientWrite(line: string): void\n clientClose(): void\n readServer(): Promise<JsonRpcResponse[]>\n} {\n const responses: JsonRpcResponse[] = []\n const input = new Readable({ read() {} })\n const output = new Writable({\n write(chunk, _enc, cb) {\n const text = chunk.toString('utf8')\n for (const line of text.split('\\n')) {\n const trimmed = line.trim()\n if (!trimmed) continue\n try {\n responses.push(JSON.parse(trimmed) as JsonRpcResponse)\n } catch {\n // Non-JSON output should never appear; drop it silently in the\n // test transport rather than crashing.\n }\n }\n cb()\n },\n })\n return {\n transport: { input, output },\n clientWrite(line: string) {\n input.push(`${line}\\n`)\n },\n clientClose() {\n input.push(null)\n },\n async readServer() {\n // Yield to the event loop a few times so async handlers drain.\n for (let i = 0; i < 5; i += 1) await new Promise((r) => setImmediate(r))\n return [...responses]\n },\n }\n}\n","/**\n * @experimental\n *\n * Trace context propagation for MCP subprocess.\n *\n * When the MCP server is launched as a child process by a sandbox harness,\n * the parent passes trace context via environment variables:\n *\n * TRACE_ID=<current-run-trace-id>\n * PARENT_SPAN_ID=<span-that-dispatched-the-delegation>\n *\n * The MCP server reads these at startup and uses them as the root of its\n * internal trace tree. All spans emitted by `runLoop` invocations inside\n * the MCP are children of the parent's delegation span.\n *\n * When these env vars are absent, the MCP generates a fresh trace root —\n * the server operates standalone without trace joining.\n */\n\nimport type { OtelExporter } from '../otel-export'\nimport { buildLoopOtelSpans, createOtelExporter } from '../otel-export'\nimport type { LoopTraceEmitter, LoopTraceEvent } from '../runtime/types'\n\nexport interface TraceContext {\n /** Trace id inherited from the parent process, or a fresh one. */\n traceId: string\n /** Parent span id from the delegation that launched this MCP server. */\n parentSpanId?: string\n}\n\n/**\n * Read trace context from the process environment.\n * Returns a context with inherited ids or a freshly generated root.\n */\nexport function readTraceContextFromEnv(): TraceContext {\n const traceId = process.env.TRACE_ID || generateTraceId()\n const parentSpanId = process.env.PARENT_SPAN_ID || undefined\n return { traceId, parentSpanId }\n}\n\n/**\n * Create a LoopTraceEmitter that:\n * 1. Parents all spans under the inherited PARENT_SPAN_ID.\n * 2. Exports spans to OTEL when OTEL_EXPORTER_OTLP_ENDPOINT is set.\n *\n * Returns both the emitter and the optional exporter handle for shutdown.\n */\nexport function createPropagatingTraceEmitter(ctx: TraceContext): {\n emitter: LoopTraceEmitter\n exporter: OtelExporter | undefined\n context: TraceContext\n} {\n const exporter = createOtelExporter()\n\n // Buffer events per loop run, then emit the full nested span tree on\n // `loop.ended` so the topology hierarchy (loop → round → branch) reaches the\n // OTLP collector — not a flat list of zero-duration point spans. A run that\n // never reaches `loop.ended` (hard abort) drops its buffer; acceptable for\n // the short-lived MCP subprocess.\n const buffers = new Map<string, LoopTraceEvent[]>()\n\n const emitter: LoopTraceEmitter = {\n emit(event: LoopTraceEvent) {\n if (!exporter) return\n const buf = buffers.get(event.runId)\n if (buf) buf.push(event)\n else buffers.set(event.runId, [event])\n if (event.kind === 'loop.ended') {\n const events = buffers.get(event.runId) ?? [event]\n buffers.delete(event.runId)\n for (const span of buildLoopOtelSpans(events, ctx.traceId, ctx.parentSpanId)) {\n exporter.exportSpan(span)\n }\n }\n },\n }\n\n return { emitter, exporter, context: ctx }\n}\n\n/**\n * Build env vars to pass to a child MCP subprocess so it inherits the\n * current trace context.\n */\nexport function traceContextToEnv(ctx: TraceContext): Record<string, string> {\n const env: Record<string, string> = { TRACE_ID: ctx.traceId }\n if (ctx.parentSpanId) env.PARENT_SPAN_ID = ctx.parentSpanId\n return env\n}\n\nfunction generateTraceId(): string {\n const bytes = new Uint8Array(16)\n if (typeof globalThis.crypto?.getRandomValues === 'function') {\n globalThis.crypto.getRandomValues(bytes)\n } else {\n for (let i = 0; i < 16; i++) bytes[i] = Math.floor(Math.random() * 256)\n }\n return Array.from(bytes)\n .map((b) => b.toString(16).padStart(2, '0'))\n .join('')\n}\n"],"mappings":"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","names":["path","spawn"]}