@tangle-network/agent-runtime 0.60.0 → 0.62.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 (58) hide show
  1. package/dist/agent.d.ts +3 -2
  2. package/dist/agent.js +2 -3
  3. package/dist/agent.js.map +1 -1
  4. package/dist/analyst-loop.d.ts +1 -1
  5. package/dist/chunk-DVQGYDN5.js +59 -0
  6. package/dist/chunk-DVQGYDN5.js.map +1 -0
  7. package/dist/{chunk-ZWGEA722.js → chunk-E4X4FNQZ.js} +2 -2
  8. package/dist/{chunk-45D64J7B.js → chunk-MT4XM3G6.js} +191 -100
  9. package/dist/chunk-MT4XM3G6.js.map +1 -0
  10. package/dist/{chunk-GLMFBUKT.js → chunk-O2UPHN7X.js} +1 -1
  11. package/dist/chunk-O2UPHN7X.js.map +1 -0
  12. package/dist/{chunk-MMDIORZY.js → chunk-RTNMMHWR.js} +4 -23
  13. package/dist/chunk-RTNMMHWR.js.map +1 -0
  14. package/dist/chunk-RYD7ND4A.js +475 -0
  15. package/dist/chunk-RYD7ND4A.js.map +1 -0
  16. package/dist/{chunk-IN7WHMGZ.js → chunk-YWO4H64E.js} +47 -7
  17. package/dist/chunk-YWO4H64E.js.map +1 -0
  18. package/dist/coder-2leJPOvC.d.ts +52 -0
  19. package/dist/coordination-Curpzeyc.d.ts +381 -0
  20. package/dist/{delegates-C94qchkz.d.ts → delegates-CLFNAKyi.d.ts} +650 -21
  21. package/dist/improvement.js +1 -1
  22. package/dist/index.d.ts +10 -6
  23. package/dist/index.js +10 -10
  24. package/dist/intelligence.d.ts +3 -2
  25. package/dist/intelligence.js.map +1 -1
  26. package/dist/{loop-runner-bin-Noz7P-mS.d.ts → loop-runner-bin-B6dzNZC8.d.ts} +46 -12
  27. package/dist/loop-runner-bin.d.ts +8 -4
  28. package/dist/loop-runner-bin.js +5 -5
  29. package/dist/loops.d.ts +8 -6
  30. package/dist/loops.js +18 -7
  31. package/dist/mcp/bin.js +11 -11
  32. package/dist/mcp/bin.js.map +1 -1
  33. package/dist/mcp/index.d.ts +13 -12
  34. package/dist/mcp/index.js +8 -8
  35. package/dist/{openai-tools-d4GKwgya.d.ts → openai-tools-CA2N3-Ak.d.ts} +1 -1
  36. package/dist/profiles.d.ts +9 -10
  37. package/dist/profiles.js +5 -7
  38. package/dist/profiles.js.map +1 -1
  39. package/dist/{run-loop-CcqfR_gy.d.ts → run-loop-D3PwlG7J.d.ts} +1 -1
  40. package/dist/runtime.d.ts +31 -1046
  41. package/dist/runtime.js +18 -7
  42. package/dist/{types-CUzjRFZ3.d.ts → types-Crxftafi.d.ts} +2 -2
  43. package/dist/workflow.d.ts +2 -2
  44. package/dist/workflow.js +2 -3
  45. package/dist/workflow.js.map +1 -1
  46. package/dist/worktree-fanout-DUiKPApb.d.ts +1099 -0
  47. package/package.json +6 -2
  48. package/dist/chunk-45D64J7B.js.map +0 -1
  49. package/dist/chunk-4FEUFYOY.js +0 -282
  50. package/dist/chunk-4FEUFYOY.js.map +0 -1
  51. package/dist/chunk-7QYOXFCD.js +0 -293
  52. package/dist/chunk-7QYOXFCD.js.map +0 -1
  53. package/dist/chunk-GLMFBUKT.js.map +0 -1
  54. package/dist/chunk-IN7WHMGZ.js.map +0 -1
  55. package/dist/chunk-MMDIORZY.js.map +0 -1
  56. package/dist/coder-CybltHEm.d.ts +0 -163
  57. package/dist/coordination-Biw19JzN.d.ts +0 -944
  58. /package/dist/{chunk-ZWGEA722.js.map → chunk-E4X4FNQZ.js.map} +0 -0
@@ -111,4 +111,4 @@ export {
111
111
  harnessInvocation,
112
112
  runLocalHarness
113
113
  };
114
- //# sourceMappingURL=chunk-GLMFBUKT.js.map
114
+ //# sourceMappingURL=chunk-O2UPHN7X.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/mcp/local-harness.ts"],"sourcesContent":["/**\n * @experimental\n *\n * Subprocess wrappers for the local coding-harness CLIs installed in the\n * sandbox image (claude-code, codex, opencode). Used by the in-process\n * delegation executor (`createInProcessExecutor`) so a `delegate_code` call\n * spawns a real harness on a real git worktree instead of provisioning a\n * sibling sandbox.\n *\n * All harness invocations:\n * - run with `cwd` set to the worktree\n * - inherit env from the parent (the MCP server inside the sandbox has\n * the harness's auth already)\n * - capture stdout/stderr\n * - support cancellation via AbortSignal\n * - enforce a wall-clock timeout\n */\n\nimport { type ChildProcess, spawn } from 'node:child_process'\nimport type { AgentProfile } from '@tangle-network/agent-interface'\n\n/** Local coding harness available inside the sandbox. */\nexport type LocalHarness = 'claude' | 'codex' | 'opencode'\n\n/**\n * Default per-harness command + arg shape. `buildArgs` takes ONLY the task prompt and\n * emits the prompt-only invocation (no model, no system prompt) — the historical shape\n * the in-process executor's `streamPrompt` drives. `modelArgs` maps a resolved model to\n * the harness's selector flag (every supported harness takes `-m <model>`). The §1.5\n * profile-aware mapper `harnessInvocation` composes these to thread the full\n * supervisor-authored profile (systemPrompt + model) into argv.\n */\nconst HARNESS_INVOCATIONS: Record<\n LocalHarness,\n {\n command: string\n buildArgs: (taskPrompt: string) => string[]\n /** Map a resolved model to the harness's model-selector flag. */\n modelArgs: (model: string) => string[]\n }\n> = {\n claude: {\n command: 'claude',\n buildArgs: (taskPrompt) => ['--headless', '-p', taskPrompt],\n modelArgs: (model) => ['-m', model],\n },\n codex: {\n command: 'codex',\n buildArgs: (taskPrompt) => ['run', taskPrompt],\n modelArgs: (model) => ['-m', model],\n },\n opencode: {\n command: 'opencode',\n buildArgs: (taskPrompt) => ['run', taskPrompt],\n modelArgs: (model) => ['-m', model],\n },\n}\n\n/** Result of mapping an `AgentProfile` + task prompt onto a harness invocation. */\nexport interface HarnessInvocation {\n command: string\n args: string[]\n}\n\n/**\n * Map a supervisor-authored `AgentProfile` + the per-task prompt onto a concrete harness\n * `command` + `args` (the §1.5 fix). UNLIKE the prompt-only `HARNESS_INVOCATIONS.buildArgs`\n * — which drops both the authored model and the system prompt — this threads the FULL\n * profile payload into argv:\n *\n * - `profile.prompt.systemPrompt` → the PROMPT channel: a portable, harness-agnostic\n * default that prepends the system prompt above the task prompt (`<system>\\n\\n<task>`),\n * so the authored standing instructions reach EVERY harness (none of the three CLIs\n * expose a portable replace-system-prompt flag for a one-shot non-interactive run).\n * - `profile.model.default` → the harness's `-m <model>` selector.\n *\n * The task prompt alone is the floor; an empty/absent profile yields exactly the legacy\n * `buildArgs(taskPrompt)` shape so existing callers are byte-identical.\n */\nexport function harnessInvocation(\n harness: LocalHarness,\n profile: AgentProfile,\n taskPrompt: string,\n): HarnessInvocation {\n const invocation = HARNESS_INVOCATIONS[harness]\n if (!invocation) {\n throw new Error(`harnessInvocation: unknown harness ${String(harness)}`)\n }\n\n const systemPrompt = profile.prompt?.systemPrompt\n const composedPrompt =\n typeof systemPrompt === 'string' && systemPrompt.trim().length > 0\n ? `${systemPrompt}\\n\\n${taskPrompt}`\n : taskPrompt\n\n const args = invocation.buildArgs(composedPrompt)\n\n const model = profile.model?.default\n if (typeof model === 'string' && model.length > 0) {\n args.push(...invocation.modelArgs(model))\n }\n\n return { command: invocation.command, args }\n}\n\n/** @experimental */\nexport interface RunLocalHarnessOptions {\n harness: LocalHarness\n /** Working directory for the subprocess (typically a worktree path). */\n cwd: string\n /** Prompt forwarded as the harness CLI's task argument. */\n taskPrompt: string\n /**\n * Pre-built command + args (e.g. from `harnessInvocation` so the full authored\n * `AgentProfile` — systemPrompt + model — reaches the harness). When set it OVERRIDES the\n * default prompt-only `buildArgs(taskPrompt)` path; `command` defaults to the harness's\n * default binary when only `args` is supplied. When absent the legacy prompt-only shape\n * is used unchanged.\n */\n invocation?: { command?: string; args: ReadonlyArray<string> }\n /** Wall-clock kill deadline (ms). Default 5 min. Subprocess SIGTERMed on expiry. */\n timeoutMs?: number\n /** Caller cancellation. SIGTERM is sent on abort. */\n signal?: AbortSignal\n /** Override env (defaults to inheriting from the parent). */\n env?: NodeJS.ProcessEnv\n /**\n * Test seam — inject a custom spawner so unit tests can mock the\n * subprocess without touching the OS. Defaults to node's `child_process.spawn`.\n */\n spawn?: (\n command: string,\n args: ReadonlyArray<string>,\n opts: {\n cwd: string\n env: NodeJS.ProcessEnv\n stdio: 'pipe'\n },\n ) => ChildProcess\n}\n\n/** @experimental */\nexport interface LocalHarnessResult {\n /** OS exit code. `null` when killed before exit. */\n exitCode: number | null\n /** Concatenated stdout. */\n stdout: string\n /** Concatenated stderr. */\n stderr: string\n /** Set when the process exited via signal (timeout / abort). */\n killedBySignal: NodeJS.Signals | null\n /** Wall-clock duration ms (spawn → exit). */\n durationMs: number\n /** Set when timeoutMs elapsed before exit. */\n timedOut: boolean\n}\n\nconst DEFAULT_TIMEOUT_MS = 5 * 60 * 1000\n\n/**\n * Spawn a local coding harness CLI as a subprocess + collect its output.\n *\n * NOT responsible for parsing the harness's output or extracting a diff —\n * the in-process executor's `streamPrompt` orchestrates `git diff` against\n * the worktree after this resolves. This function is intentionally narrow:\n * spawn, wait, capture, return.\n *\n * Fails loud — throws when:\n * - `cwd` doesn't exist (subprocess emits ENOENT; surfaced as Error)\n * - the harness binary is not on PATH (ENOENT)\n *\n * Does NOT throw when:\n * - the subprocess exits non-zero (`result.exitCode` carries the code)\n * - the subprocess is aborted / timed out (`result.killedBySignal` /\n * `result.timedOut` carries the reason)\n *\n * @experimental\n */\nexport function runLocalHarness(options: RunLocalHarnessOptions): Promise<LocalHarnessResult> {\n const { harness, cwd, taskPrompt } = options\n const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS\n const env = options.env ?? process.env\n const spawnImpl = options.spawn ?? spawn\n\n const invocation = HARNESS_INVOCATIONS[harness]\n if (!invocation) {\n return Promise.reject(new Error(`runLocalHarness: unknown harness ${String(harness)}`))\n }\n\n const startedAt = Date.now()\n const command = options.invocation?.command ?? invocation.command\n const args = options.invocation ? [...options.invocation.args] : invocation.buildArgs(taskPrompt)\n\n return new Promise<LocalHarnessResult>((resolve, reject) => {\n let child: ChildProcess\n try {\n child = spawnImpl(command, args, { cwd, env, stdio: 'pipe' })\n } catch (err) {\n reject(err instanceof Error ? err : new Error(String(err)))\n return\n }\n\n // The harness takes its task as an argv arg, not on stdin. Leaving stdin\n // OPEN makes a non-TTY `opencode run` (and likely the other harnesses)\n // BLOCK forever waiting on input — zero output, SIGTERM at the wall cap,\n // empty patch -> \"no candidate passed validation\". Close stdin so the\n // subprocess sees EOF and proceeds (the `cliExecutor` leaf does the same).\n child.stdin?.end()\n\n let stdout = ''\n let stderr = ''\n let timedOut = false\n let settled = false\n\n const timer =\n timeoutMs > 0\n ? setTimeout(() => {\n timedOut = true\n if (!child.killed) child.kill('SIGTERM')\n }, timeoutMs)\n : null\n if (timer && typeof (timer as { unref?: () => void }).unref === 'function') {\n ;(timer as { unref: () => void }).unref()\n }\n\n const onAbort = () => {\n if (!child.killed) child.kill('SIGTERM')\n }\n if (options.signal) {\n if (options.signal.aborted) onAbort()\n else options.signal.addEventListener('abort', onAbort, { once: true })\n }\n\n child.stdout?.on('data', (chunk) => {\n stdout += String(chunk)\n })\n child.stderr?.on('data', (chunk) => {\n stderr += String(chunk)\n })\n\n const finalize = (result: LocalHarnessResult) => {\n if (settled) return\n settled = true\n if (timer) clearTimeout(timer)\n options.signal?.removeEventListener('abort', onAbort)\n resolve(result)\n }\n\n child.on('error', (err) => {\n if (settled) return\n settled = true\n if (timer) clearTimeout(timer)\n options.signal?.removeEventListener('abort', onAbort)\n reject(err)\n })\n\n child.on('close', (code, signal) => {\n finalize({\n exitCode: code,\n stdout,\n stderr,\n killedBySignal: signal,\n durationMs: Date.now() - startedAt,\n timedOut,\n })\n })\n })\n}\n"],"mappings":"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","names":[]}
@@ -1,10 +1,10 @@
1
1
  import {
2
2
  createFleetWorkspaceExecutor,
3
3
  createSiblingSandboxExecutor
4
- } from "./chunk-4FEUFYOY.js";
4
+ } from "./chunk-RYD7ND4A.js";
5
5
  import {
6
6
  runWorktreeHarness
7
- } from "./chunk-45D64J7B.js";
7
+ } from "./chunk-MT4XM3G6.js";
8
8
  import {
9
9
  buildLoopOtelSpans,
10
10
  createOtelExporter
@@ -80,7 +80,7 @@ function createInProcessExecutor(options) {
80
80
  yield {
81
81
  type: "result",
82
82
  data: {
83
- result: toCoderOutput(run.result, harness),
83
+ result: run.result,
84
84
  source: "in-process-executor",
85
85
  harness,
86
86
  runId
@@ -112,21 +112,6 @@ function createInProcessExecutor(options) {
112
112
  }
113
113
  };
114
114
  }
115
- function toCoderOutput(result, harness) {
116
- const tests = result.checks?.tests;
117
- const typecheck = result.checks?.typecheck;
118
- return {
119
- branch: result.branch,
120
- patch: result.patch,
121
- testResult: { passed: tests ? tests.passed : true, output: tail(tests?.output ?? "", 4e3) },
122
- typecheckResult: {
123
- passed: typecheck ? typecheck.passed : true,
124
- output: tail(typecheck?.output ?? "", 4e3)
125
- },
126
- diffStats: result.stats,
127
- reviewerNotes: result.harness.exitCode === 0 ? void 0 : `harness ${harness} exited ${result.harness.exitCode}${result.harness.timedOut ? " (timed out)" : ""}`
128
- };
129
- }
130
115
  async function defaultRunPostCheck(cmd, cwd, signal) {
131
116
  const { spawn } = await import("child_process");
132
117
  return new Promise((resolve, reject) => {
@@ -163,10 +148,6 @@ async function defaultRunPostCheck(cmd, cwd, signal) {
163
148
  });
164
149
  });
165
150
  }
166
- function tail(text, max) {
167
- if (text.length <= max) return text;
168
- return text.slice(text.length - max);
169
- }
170
151
 
171
152
  // src/mcp/bin-helpers.ts
172
153
  async function detectExecutor(args) {
@@ -294,4 +275,4 @@ export {
294
275
  createPropagatingTraceEmitter,
295
276
  traceContextToEnv
296
277
  };
297
- //# sourceMappingURL=chunk-MMDIORZY.js.map
278
+ //# sourceMappingURL=chunk-RTNMMHWR.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/mcp/in-process-executor.ts","../src/mcp/bin-helpers.ts","../src/mcp/trace-propagation.ts"],"sourcesContent":["/**\n * @experimental\n *\n * In-process delegation executor — when `agent-runtime-mcp` runs inside a sandbox whose image\n * carries the local coding-harness CLIs (claude / codex / opencode), delegations spawn the harness\n * AS A SUBPROCESS against a git worktree on the SAME filesystem instead of provisioning a sibling\n * sandbox. Zero provisioning latency; worker diffs land in-place; multi-harness fanout = N parallel\n * subprocesses in N parallel worktrees (round-robin `harnesses`).\n *\n * This is a THIN adapter over `runWorktreeHarness` (`./worktree-harness`) — the SAME core the\n * `Scope` leaf `createWorktreeCliExecutor` uses. It only adapts the core to the `SandboxClient`\n * port: `create()` reads the authored profile from `CreateSandboxOptions.backend.profile`, and\n * `streamPrompt` runs the core then emits its raw `WorktreeHarnessResult` (the content-addressed\n * patch artifact) on the `result` event. The sandbox-session decode layer\n * (`./detached-coder`) projects that artifact onto `CoderOutput`; the generic `Scope` path settles\n * the artifact directly. The §1.5 payload (systemPrompt + model) reaches the harness inside the core.\n */\n\nimport { randomUUID } from 'node:crypto'\nimport type {\n AgentProfile,\n CreateSandboxOptions,\n SandboxEvent,\n SandboxInstance,\n} from '@tangle-network/sandbox'\nimport type { LoopSandboxPlacement, SandboxClient } from '../runtime'\nimport type { DelegationExecutor } from './executor'\nimport type { LocalHarness } from './local-harness'\nimport type { GitRunner, WorktreeHandle } from './worktree'\nimport { runWorktreeHarness } from './worktree-harness'\n\n/** @experimental */\nexport interface InProcessExecutorOptions {\n /** Absolute path to the git repo (the workspace). Worktrees go under `<repoRoot>/.agent-worktrees/`. */\n repoRoot: string\n /** Harnesses to round-robin across `create()` calls. One entry = no fanout. Default `['claude']`. */\n harnesses?: ReadonlyArray<LocalHarness>\n /** Optional per-delegation test command run in the worktree after the harness exits. */\n testCmd?: string\n /** Optional per-delegation typecheck command. Same shape as `testCmd`. */\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 /** Test seam — override the harness runner (defaults to the real CLI via `runLocalHarness`). */\n runHarness?: typeof import('./local-harness').runLocalHarness\n /** Test seam — override the post-check runner (defaults to a `sh -c` spawn). A throw is folded\n * into a non-fatal `{exitCode:-1}` so a broken check command fails the signal, not the run. */\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 /** Worktree path in the parent sandbox's filesystem (set so traces correlate to on-disk artifacts). */\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. Returns a {@link DelegationExecutor} whose `client.create()`\n * returns a minimal virtual `SandboxInstance`; the kernel calls `streamPrompt(msg)` on it, which\n * runs the shared worktree-harness core and emits one `result` event whose `data.result` is the\n * raw `WorktreeHarnessResult` (the content-addressed patch artifact). The authored profile\n * (`backend.profile`) threads its systemPrompt + model into the harness via the core.\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 runPostCheck = options.runPostCheck ?? defaultRunPostCheck\n // The core speaks one `runCommand` seam ({exitCode, output}); adapt the post-check seam\n // ({exitCode, stdout, stderr}) onto it, folding a throw into a non-fatal failure signal so a\n // broken check command fails the signal rather than aborting the whole delegation.\n const runCommand = async ({\n command,\n cwd,\n signal,\n }: {\n command: string\n cwd: string\n timeoutMs: number\n signal?: AbortSignal\n }): Promise<{ exitCode: number | null; output: string }> => {\n try {\n const r = await runPostCheck(command, cwd, signal)\n return { exitCode: r.exitCode, output: r.stderr || r.stdout }\n } catch (err) {\n return { exitCode: -1, output: err instanceof Error ? err.message : String(err) }\n }\n }\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 // §1.5: the authored profile rides in `backend.profile` (set by `buildBackendOptions`).\n // Without one (a direct test `create()`), fall back to a name-only profile → the harness\n // sees the task prompt with no system prepend, the pre-fix behavior.\n const profile =\n ((opts?.backend as { profile?: AgentProfile } | undefined)?.profile as\n | AgentProfile\n | undefined) ?? ({ name: `in-process-${harness}` } as AgentProfile)\n\n const virtual: VirtualSandbox = {\n id: `in-process-${runId}`,\n __inProcess: { runId, harness },\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 // The shared worktree-harness core: worktree → profile-aware harness → diff → checks.\n // A throw cleans up inside the core; on success we own the teardown (the finally).\n const run = await runWorktreeHarness({\n repoRoot: options.repoRoot,\n profile,\n harness,\n taskPrompt,\n runId,\n harnessTimeoutMs: options.harnessTimeoutMs ?? DEFAULT_HARNESS_TIMEOUT_MS,\n checkTimeoutMs: options.postCheckTimeoutMs ?? DEFAULT_POSTCHECK_TIMEOUT_MS,\n ...(options.testCmd !== undefined ? { testCmd: options.testCmd } : {}),\n ...(options.typecheckCmd !== undefined ? { typecheckCmd: options.typecheckCmd } : {}),\n ...(options.runGit ? { runGit: options.runGit } : {}),\n ...(options.runHarness ? { runHarness: options.runHarness } : {}),\n runCommand,\n ...(promptOpts?.signal ? { signal: promptOpts.signal } : {}),\n })\n this.__inProcess.worktree = run.worktree\n\n try {\n yield {\n type: 'in_process.harness.started',\n data: { runId, harness, worktreePath: run.worktree.path, command: harness },\n }\n const h = run.result.harness\n yield {\n type: 'in_process.harness.ended',\n data: {\n runId,\n exitCode: h.exitCode,\n durationMs: h.durationMs,\n killedBySignal: h.killedBySignal,\n timedOut: h.timedOut,\n stdoutBytes: h.stdout.length,\n stderrBytes: h.stderr.length,\n },\n }\n yield {\n type: 'result',\n data: {\n result: run.result,\n source: 'in-process-executor',\n harness,\n runId,\n },\n }\n } finally {\n await run.cleanup()\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 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","/**\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 * 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":[]}
@@ -0,0 +1,475 @@
1
+ import {
2
+ coderProfile,
3
+ coderTaskToPrompt
4
+ } from "./chunk-DVQGYDN5.js";
5
+ import {
6
+ composeLoopTraceEmitters,
7
+ detachedTurnEvents,
8
+ formatDetachedSessionRef,
9
+ parseDetachedSessionRef,
10
+ runCoderChecks,
11
+ runDetachedTurn,
12
+ runLoop,
13
+ selectValidWinner
14
+ } from "./chunk-MT4XM3G6.js";
15
+
16
+ // src/mcp/executor.ts
17
+ function createSiblingSandboxExecutor(options) {
18
+ const underlying = options.client;
19
+ const client = {
20
+ create(opts) {
21
+ return underlying.create(opts);
22
+ },
23
+ describePlacement(box) {
24
+ return { kind: "sibling", sandboxId: readId(box) };
25
+ }
26
+ };
27
+ return {
28
+ client,
29
+ placement: "sibling",
30
+ describe() {
31
+ return "sibling-sandbox (each delegation = fresh sandbox via client.create)";
32
+ }
33
+ };
34
+ }
35
+ function createFleetWorkspaceExecutor(options) {
36
+ const fleet = options.fleet;
37
+ const exclude = new Set(options.excludeMachineIds ?? []);
38
+ let callIndex = 0;
39
+ const placementBySandboxId = /* @__PURE__ */ new Map();
40
+ const client = {
41
+ async create() {
42
+ const ids = fleet.ids.filter((id) => !exclude.has(id));
43
+ if (ids.length === 0) {
44
+ throw new Error(
45
+ `agent-runtime: fleet ${fleet.fleetId} has no eligible worker machines (ids=[${fleet.ids.join(",")}], excluded=[${[...exclude].join(",")}])`
46
+ );
47
+ }
48
+ const selector = options.selectMachine;
49
+ const machineId = selector ? selector({ callIndex, ids }) : ids[callIndex % ids.length];
50
+ callIndex += 1;
51
+ if (typeof machineId !== "string" || machineId.length === 0) {
52
+ throw new Error("agent-runtime: fleet executor selectMachine returned an empty machine id");
53
+ }
54
+ const box = await fleet.sandbox(machineId);
55
+ const sandboxId = readId(box);
56
+ if (sandboxId) placementBySandboxId.set(sandboxId, { machineId });
57
+ return box;
58
+ },
59
+ describePlacement(box) {
60
+ const sandboxId = readId(box);
61
+ const recorded = sandboxId ? placementBySandboxId.get(sandboxId) : void 0;
62
+ return {
63
+ kind: "fleet",
64
+ sandboxId,
65
+ fleetId: fleet.fleetId,
66
+ machineId: recorded?.machineId
67
+ };
68
+ }
69
+ };
70
+ return {
71
+ client,
72
+ placement: "fleet",
73
+ describe() {
74
+ const excluded = exclude.size > 0 ? ` (excluded=[${[...exclude].join(",")}])` : "";
75
+ return `fleet-workspace (fleetId=${fleet.fleetId}, machines=[${fleet.ids.join(",")}]${excluded})`;
76
+ }
77
+ };
78
+ }
79
+ function readId(box) {
80
+ const raw = box.id;
81
+ return typeof raw === "string" && raw.length > 0 ? raw : void 0;
82
+ }
83
+
84
+ // src/mcp/detached-coder.ts
85
+ var DEFAULT_MAX_DIFF_LINES = 400;
86
+ function coderRunProfile(options) {
87
+ const harness = options.harness ?? "claude-code";
88
+ const name = options.name ?? `coder-${harness}`;
89
+ return {
90
+ ...coderProfile,
91
+ name,
92
+ ...options.systemPrompt ? { prompt: { systemPrompt: options.systemPrompt } } : {},
93
+ model: options.model ? { default: options.model } : void 0,
94
+ metadata: { ...coderProfile.metadata, backendType: harness }
95
+ };
96
+ }
97
+ function coderRunSpec(options = {}) {
98
+ return {
99
+ name: options.name ?? `coder-${options.harness ?? "claude-code"}`,
100
+ profile: coderRunProfile(options),
101
+ taskToPrompt: coderTaskToPrompt
102
+ };
103
+ }
104
+ var coderOutputAdapter = { parse: parseCoderEvents };
105
+ function multiHarnessCoderFanout(options = {}) {
106
+ const harnesses = options.harnesses && options.harnesses.length > 0 ? options.harnesses : ["claude-code", "codex", "opencode/zai-coding-plan/glm-5.1"];
107
+ const models = options.models ?? [];
108
+ const agentRuns = harnesses.map((harness, i) => coderRunSpec({ harness, model: models[i] }));
109
+ const driver = {
110
+ name: "fanout",
111
+ plan: async (task, history) => history.length === 0 ? agentRuns.map(() => task) : [],
112
+ decide: (history) => history.some((i) => i.verdict?.valid === true) ? "pick-winner" : "fail"
113
+ };
114
+ return { agentRuns, output: coderOutputAdapter, validator: defaultCoderValidator(), driver };
115
+ }
116
+ function createCoderValidator(task) {
117
+ const constraints = {
118
+ maxDiffLines: task.maxDiffLines ?? DEFAULT_MAX_DIFF_LINES,
119
+ forbiddenPaths: task.forbiddenPaths ?? []
120
+ };
121
+ return {
122
+ async validate(output) {
123
+ return runCoderChecks(
124
+ {
125
+ patch: output.patch,
126
+ testsPassed: output.testResult.passed,
127
+ typecheckPassed: output.typecheckResult.passed
128
+ },
129
+ constraints
130
+ );
131
+ }
132
+ };
133
+ }
134
+ function defaultCoderValidator() {
135
+ return createCoderValidator({
136
+ goal: "",
137
+ repoRoot: "",
138
+ forbiddenPaths: [],
139
+ maxDiffLines: DEFAULT_MAX_DIFF_LINES
140
+ });
141
+ }
142
+ function parseCoderEvents(events) {
143
+ for (let i = events.length - 1; i >= 0; i -= 1) {
144
+ const event = events[i];
145
+ if (!event) continue;
146
+ const type = String(event.type ?? "");
147
+ const data = isRecord(event.data) ? event.data : {};
148
+ if (type === "result" || type === "final" || type === "coder.result") {
149
+ const payload = data.result ?? data.output ?? data;
150
+ const projected = projectWorktreeArtifact(payload);
151
+ if (projected) return projected;
152
+ const direct = coerceCoderOutput(payload);
153
+ if (direct) return direct;
154
+ }
155
+ }
156
+ const transcript = collectAssistantText(events);
157
+ for (const candidate of fencedJsonBlocks(transcript)) {
158
+ const coerced = coerceCoderOutput(candidate);
159
+ if (coerced) return coerced;
160
+ }
161
+ return {
162
+ branch: "",
163
+ patch: "",
164
+ testResult: { passed: false, output: "" },
165
+ typecheckResult: { passed: false, output: "" },
166
+ diffStats: { filesChanged: 0, insertions: 0, deletions: 0 }
167
+ };
168
+ }
169
+ function projectWorktreeArtifact(value) {
170
+ if (!isRecord(value)) return void 0;
171
+ const stats = value.stats;
172
+ if (!isRecord(stats)) return void 0;
173
+ const branch = pickString(value.branch) ?? "";
174
+ const patch = pickString(value.patch) ?? "";
175
+ const checks = isRecord(value.checks) ? value.checks : {};
176
+ const tests = isRecord(checks.tests) ? checks.tests : void 0;
177
+ const typecheck = isRecord(checks.typecheck) ? checks.typecheck : void 0;
178
+ const harness = isRecord(value.harness) ? value.harness : void 0;
179
+ const exitCode = harness ? toFiniteInt(harness.exitCode) : 0;
180
+ const timedOut = harness?.timedOut === true;
181
+ const harnessName = harness ? pickString(harness.name) ?? "harness" : "harness";
182
+ return {
183
+ branch,
184
+ patch,
185
+ testResult: {
186
+ passed: tests ? tests.passed === true : true,
187
+ output: tail(pickString(tests?.output) ?? "", 4e3)
188
+ },
189
+ typecheckResult: {
190
+ passed: typecheck ? typecheck.passed === true : true,
191
+ output: tail(pickString(typecheck?.output) ?? "", 4e3)
192
+ },
193
+ diffStats: {
194
+ filesChanged: toFiniteInt(stats.filesChanged),
195
+ insertions: toFiniteInt(stats.insertions),
196
+ deletions: toFiniteInt(stats.deletions)
197
+ },
198
+ ...exitCode !== 0 ? {
199
+ reviewerNotes: `harness ${harnessName} exited ${exitCode}${timedOut ? " (timed out)" : ""}`
200
+ } : {}
201
+ };
202
+ }
203
+ function isRecord(value) {
204
+ return value !== null && typeof value === "object" && !Array.isArray(value);
205
+ }
206
+ function tail(text, max) {
207
+ return text.length <= max ? text : text.slice(text.length - max);
208
+ }
209
+ function pickString(value) {
210
+ return typeof value === "string" && value.length > 0 ? value : void 0;
211
+ }
212
+ function collectAssistantText(events) {
213
+ const chunks = [];
214
+ for (const event of events) {
215
+ if (!event) continue;
216
+ const data = isRecord(event.data) ? event.data : {};
217
+ if (String(event.type ?? "") === "message.part.updated") {
218
+ const part = isRecord(data.part) ? data.part : {};
219
+ const partType = String(part.type ?? "");
220
+ if (partType !== "text" && partType !== "") continue;
221
+ const text2 = pickString(data.delta) ?? pickString(part.text);
222
+ if (text2) chunks.push(text2);
223
+ continue;
224
+ }
225
+ const text = pickString(data.text) ?? pickString(data.delta);
226
+ if (text) chunks.push(text);
227
+ }
228
+ return chunks.join("");
229
+ }
230
+ function fencedJsonBlocks(text) {
231
+ const out = [];
232
+ const matches = [...text.matchAll(/```(?:json)?\s*([\s\S]*?)```/gi)];
233
+ for (let i = matches.length - 1; i >= 0; i -= 1) {
234
+ const body = (matches[i]?.[1] ?? "").trim();
235
+ if (!body) continue;
236
+ try {
237
+ out.push(JSON.parse(body));
238
+ } catch {
239
+ }
240
+ }
241
+ return out;
242
+ }
243
+ function coerceCoderOutput(value) {
244
+ if (!isRecord(value)) return void 0;
245
+ const branch = pickString(value.branch);
246
+ const patch = pickString(value.patch) ?? "";
247
+ if (branch === void 0) return void 0;
248
+ return {
249
+ branch,
250
+ patch,
251
+ testResult: coerceCmdResult(value.testResult),
252
+ typecheckResult: coerceCmdResult(value.typecheckResult),
253
+ diffStats: coerceDiffStats(value.diffStats),
254
+ reviewerNotes: pickString(value.reviewerNotes)
255
+ };
256
+ }
257
+ function coerceCmdResult(value) {
258
+ if (!isRecord(value)) return { passed: false, output: "" };
259
+ return { passed: value.passed === true, output: pickString(value.output) ?? "" };
260
+ }
261
+ function coerceDiffStats(value) {
262
+ if (!isRecord(value)) return { filesChanged: 0, insertions: 0, deletions: 0 };
263
+ return {
264
+ filesChanged: toFiniteInt(value.filesChanged),
265
+ insertions: toFiniteInt(value.insertions),
266
+ deletions: toFiniteInt(value.deletions)
267
+ };
268
+ }
269
+ function toFiniteInt(value) {
270
+ if (typeof value !== "number") return 0;
271
+ if (!Number.isFinite(value)) return 0;
272
+ return Math.max(0, Math.trunc(value));
273
+ }
274
+
275
+ // src/mcp/delegates.ts
276
+ function detachedSessionDelegate(options) {
277
+ const executor = resolveExecutor(options);
278
+ const sandboxClient = executor.client;
279
+ const fanoutHarnesses = options.fanoutHarnesses;
280
+ const maxConcurrency = options.maxConcurrency ?? 4;
281
+ const traceEmitter = options.traceEmitter;
282
+ return async (args, ctx) => {
283
+ const task = coderTaskFromArgs(args);
284
+ const variants = Math.max(1, Math.trunc(args.variants ?? 1));
285
+ const loopEmitter = composeLoopTraceEmitters(traceEmitter, ctx.traceEmitter);
286
+ ctx.report({ iteration: 0, phase: "starting" });
287
+ if (variants <= 1) {
288
+ const agentRunSpec = coderRunSpec({
289
+ ...options.harness ? { harness: options.harness } : {},
290
+ ...options.model ? { model: options.model } : {},
291
+ ...options.systemPrompt ? { systemPrompt: options.systemPrompt } : {}
292
+ });
293
+ const output = coderOutputAdapter;
294
+ const validator = createCoderValidator(task);
295
+ if (ctx.detachedSessionRef !== void 0 && ctx.updateDetachedSessionRef) {
296
+ const { sessionId } = parseDetachedSessionRef(ctx.detachedSessionRef);
297
+ const rebind = ctx.updateDetachedSessionRef;
298
+ const turn = await runDetachedTurn({
299
+ client: sandboxClient,
300
+ spec: agentRunSpec,
301
+ prompt: agentRunSpec.taskToPrompt(task),
302
+ sessionId,
303
+ bindSandbox: (sandboxId) => rebind(formatDetachedSessionRef({ sandboxId, sessionId })),
304
+ signal: ctx.signal,
305
+ report: ctx.report,
306
+ ...loopEmitter ? { traceEmitter: loopEmitter } : {},
307
+ ...executor.placement === "fleet" ? { placement: "fleet" } : {},
308
+ ...options.detachedTickIntervalMs !== void 0 ? { tickIntervalMs: options.detachedTickIntervalMs } : {},
309
+ ...options.detachedWallCapMs !== void 0 ? { wallCapMs: options.detachedWallCapMs } : {}
310
+ });
311
+ const chosen3 = await settleDetachedCoderTurn(turn, {
312
+ task,
313
+ sessionId,
314
+ signal: ctx.signal,
315
+ ...options.harness ? { harness: options.harness } : {},
316
+ ...options.model ? { model: options.model } : {},
317
+ ...options.reviewer ? { reviewer: options.reviewer } : {}
318
+ });
319
+ ctx.report({ iteration: 1, phase: "completed" });
320
+ return chosen3;
321
+ }
322
+ const result2 = await runLoop({
323
+ driver: singleShotDriver,
324
+ agentRun: agentRunSpec,
325
+ output,
326
+ validator,
327
+ task,
328
+ ctx: {
329
+ sandboxClient,
330
+ signal: ctx.signal,
331
+ ...loopEmitter ? { traceEmitter: loopEmitter } : {}
332
+ },
333
+ maxIterations: 1,
334
+ maxConcurrency
335
+ });
336
+ const chosen2 = await pickCoderWinner({
337
+ iterations: result2.iterations,
338
+ reviewer: options.reviewer,
339
+ selection: options.winnerSelection ?? "highest-score",
340
+ task,
341
+ signal: ctx.signal
342
+ });
343
+ if (!chosen2) throw new Error(noWinnerMessage(options.reviewer));
344
+ ctx.report({ iteration: 1, phase: "completed" });
345
+ return chosen2;
346
+ }
347
+ const fanout = multiHarnessCoderFanout({
348
+ ...fanoutHarnesses && fanoutHarnesses.length > 0 ? { harnesses: fanoutHarnesses.slice(0, variants) } : {},
349
+ ...options.fanoutModels ? { models: options.fanoutModels.slice(0, variants) } : {}
350
+ });
351
+ const agentRuns = fanout.agentRuns.slice(0, variants);
352
+ const result = await runLoop({
353
+ driver: fanout.driver,
354
+ agentRuns,
355
+ output: fanout.output,
356
+ validator: fanout.validator,
357
+ task,
358
+ ctx: {
359
+ sandboxClient,
360
+ signal: ctx.signal,
361
+ ...loopEmitter ? { traceEmitter: loopEmitter } : {}
362
+ },
363
+ maxIterations: variants,
364
+ maxConcurrency: Math.min(maxConcurrency, variants)
365
+ });
366
+ const chosen = await pickCoderWinner({
367
+ iterations: result.iterations,
368
+ reviewer: options.reviewer,
369
+ selection: options.winnerSelection ?? "highest-score",
370
+ task,
371
+ signal: ctx.signal
372
+ });
373
+ if (!chosen) throw new Error(noWinnerMessage(options.reviewer));
374
+ ctx.report({ iteration: agentRuns.length, phase: "completed" });
375
+ return chosen;
376
+ };
377
+ }
378
+ async function pickCoderWinner(args) {
379
+ const eligible = [];
380
+ for (const iter of args.iterations) {
381
+ if (iter.output === void 0 || iter.error || iter.verdict?.valid !== true) continue;
382
+ const readiness = iter.verdict.score ?? 0;
383
+ if (args.reviewer) {
384
+ const review = await args.reviewer(iter.output, args.task, { signal: args.signal });
385
+ if (!review.approved) continue;
386
+ eligible.push({ iter, readiness: review.readiness });
387
+ } else {
388
+ eligible.push({ iter, readiness });
389
+ }
390
+ }
391
+ if (eligible.length === 0) return void 0;
392
+ if (args.selection === "highest-readiness") {
393
+ const sorted = [...eligible].sort(
394
+ (a, b) => b.readiness - a.readiness || a.iter.index - b.iter.index
395
+ );
396
+ return sorted[0].iter.output;
397
+ }
398
+ const wrapped = eligible.map(({ iter }) => ({
399
+ ...iter,
400
+ output: { kind: "done", deliverable: iter.output }
401
+ }));
402
+ const winner = selectValidWinner({
403
+ strategy: baseStrategy(args.selection),
404
+ sizeOf: (o) => o.diffStats.insertions + o.diffStats.deletions
405
+ })(wrapped);
406
+ const out = winner?.output;
407
+ if (!out || out.kind !== "done") return void 0;
408
+ return out.deliverable;
409
+ }
410
+ function baseStrategy(selection) {
411
+ switch (selection) {
412
+ case "smallest-diff":
413
+ return "smallest-artifact";
414
+ case "first-approved":
415
+ return "first-valid";
416
+ default:
417
+ return "highest-score";
418
+ }
419
+ }
420
+ function noWinnerMessage(reviewer) {
421
+ return reviewer ? "coder delegate: no candidate passed validation + review" : "coder delegate: no candidate passed validation";
422
+ }
423
+ function coderTaskFromArgs(args) {
424
+ return {
425
+ goal: buildCoderGoal(args),
426
+ repoRoot: args.repoRoot,
427
+ testCmd: args.config?.testCmd,
428
+ typecheckCmd: args.config?.typecheckCmd,
429
+ forbiddenPaths: args.config?.forbiddenPaths,
430
+ maxDiffLines: args.config?.maxDiffLines
431
+ };
432
+ }
433
+ async function settleDetachedCoderTurn(turn, options) {
434
+ const parsed = coderOutputAdapter.parse(detachedTurnEvents(options.sessionId, turn));
435
+ const validator = createCoderValidator(options.task);
436
+ const verdict = await validator.validate(parsed, { iteration: 0, signal: options.signal });
437
+ if (verdict.valid !== true) throw new Error(noWinnerMessage(options.reviewer));
438
+ if (options.reviewer) {
439
+ const review = await options.reviewer(parsed, options.task, { signal: options.signal });
440
+ if (!review.approved) throw new Error(noWinnerMessage(options.reviewer));
441
+ }
442
+ return parsed;
443
+ }
444
+ function buildCoderGoal(args) {
445
+ if (!args.contextHint) return args.goal;
446
+ return [args.goal, "", "## Context", args.contextHint].join("\n");
447
+ }
448
+ function resolveExecutor(options) {
449
+ if (options.executor && options.sandboxClient) {
450
+ throw new Error("detachedSessionDelegate: pass exactly one of `executor` or `sandboxClient`");
451
+ }
452
+ if (options.executor) return options.executor;
453
+ if (options.sandboxClient) {
454
+ return createSiblingSandboxExecutor({ client: options.sandboxClient });
455
+ }
456
+ throw new Error("detachedSessionDelegate: `executor` or `sandboxClient` is required");
457
+ }
458
+ var singleShotDriver = {
459
+ name: "mcp-single-shot",
460
+ async plan(task, history) {
461
+ return history.length === 0 ? [task] : [];
462
+ },
463
+ decide(history) {
464
+ return history.length > 0 ? "pick-winner" : "fail";
465
+ }
466
+ };
467
+
468
+ export {
469
+ createSiblingSandboxExecutor,
470
+ createFleetWorkspaceExecutor,
471
+ detachedSessionDelegate,
472
+ coderTaskFromArgs,
473
+ settleDetachedCoderTurn
474
+ };
475
+ //# sourceMappingURL=chunk-RYD7ND4A.js.map