zidane 5.10.0 → 5.10.2

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 (79) hide show
  1. package/dist/{agent-AnumGPWj.d.ts → agent-Bt123Fdy.d.ts} +103 -3
  2. package/dist/agent-Bt123Fdy.d.ts.map +1 -0
  3. package/dist/chat/pure.d.ts +3 -3
  4. package/dist/chat/pure.js +1 -1
  5. package/dist/chat.d.ts +6 -6
  6. package/dist/chat.js +3 -3
  7. package/dist/contexts/docker.d.ts +1 -1
  8. package/dist/{contexts-CbI8dRfI.js → contexts-GKAWYq07.js} +113 -7
  9. package/dist/contexts-GKAWYq07.js.map +1 -0
  10. package/dist/contexts.d.ts +3 -3
  11. package/dist/contexts.js +2 -2
  12. package/dist/eval.d.ts +1 -1
  13. package/dist/eval.js +3 -3
  14. package/dist/{headless-D0qfvzG9.js → headless-Cn6XXmr3.js} +4 -4
  15. package/dist/{headless-D0qfvzG9.js.map → headless-Cn6XXmr3.js.map} +1 -1
  16. package/dist/headless.d.ts +1 -1
  17. package/dist/headless.js +1 -1
  18. package/dist/{index-B65HjjKZ.d.ts → index-4pQUnr2W.d.ts} +2 -2
  19. package/dist/{index-B65HjjKZ.d.ts.map → index-4pQUnr2W.d.ts.map} +1 -1
  20. package/dist/{index-DsvHiyYU.d.ts → index-BodGKXBV.d.ts} +3 -3
  21. package/dist/{index-DsvHiyYU.d.ts.map → index-BodGKXBV.d.ts.map} +1 -1
  22. package/dist/{index-LX8KCBXU.d.ts → index-C7BvI1Hi.d.ts} +14 -4
  23. package/dist/index-C7BvI1Hi.d.ts.map +1 -0
  24. package/dist/index.d.ts +6 -6
  25. package/dist/index.js +7 -7
  26. package/dist/{login-DocBwMVo.js → login-DoGslmKC.js} +2 -2
  27. package/dist/{login-DocBwMVo.js.map → login-DoGslmKC.js.map} +1 -1
  28. package/dist/{mcp-DzuTfq-I.js → mcp-BdN9UjTO.js} +32 -7
  29. package/dist/mcp-BdN9UjTO.js.map +1 -0
  30. package/dist/mcp.d.ts +1 -1
  31. package/dist/mcp.js +1 -1
  32. package/dist/output/stream-json.d.ts +2 -2
  33. package/dist/output/stream-json.js +1 -1
  34. package/dist/output/terminal.d.ts +2 -2
  35. package/dist/{presets-CTNbWXWz.js → presets-DCrQmY3b.js} +3 -2
  36. package/dist/presets-DCrQmY3b.js.map +1 -0
  37. package/dist/presets.d.ts +2 -2
  38. package/dist/presets.js +1 -1
  39. package/dist/providers.d.ts +1 -1
  40. package/dist/restate.d.ts +192 -16
  41. package/dist/restate.d.ts.map +1 -1
  42. package/dist/restate.js +133 -5
  43. package/dist/restate.js.map +1 -1
  44. package/dist/session/sqlite.d.ts +1 -1
  45. package/dist/session.d.ts +1 -1
  46. package/dist/skills.d.ts +2 -2
  47. package/dist/{tool-formatters-5nr1eXPn.d.ts → tool-formatters-BuB31L-c.d.ts} +2 -2
  48. package/dist/{tool-formatters-5nr1eXPn.d.ts.map → tool-formatters-BuB31L-c.d.ts.map} +1 -1
  49. package/dist/tools/fetch-url.d.ts +1 -1
  50. package/dist/tools/web-search.d.ts +1 -1
  51. package/dist/{tools-ycHDeHBZ.js → tools-Bk9TqmCV.js} +225 -18
  52. package/dist/tools-Bk9TqmCV.js.map +1 -0
  53. package/dist/tools.d.ts +3 -3
  54. package/dist/tools.js +2 -2
  55. package/dist/{transcript-anchors-D6Sw-Gzk.js → transcript-anchors-Bkuspqgn.js} +5 -5
  56. package/dist/{transcript-anchors-D6Sw-Gzk.js.map → transcript-anchors-Bkuspqgn.js.map} +1 -1
  57. package/dist/{transcript-anchors-DezrH1sp.d.ts → transcript-anchors-DhVgKmEl.d.ts} +4 -4
  58. package/dist/{transcript-anchors-DezrH1sp.d.ts.map → transcript-anchors-DhVgKmEl.d.ts.map} +1 -1
  59. package/dist/tui.d.ts +3 -3
  60. package/dist/tui.js +7 -7
  61. package/dist/{turn-operations-C70p-7Nn.js → turn-operations-DLWN2J7f.js} +13 -1
  62. package/dist/{turn-operations-C70p-7Nn.js.map → turn-operations-DLWN2J7f.js.map} +1 -1
  63. package/dist/{turn-operations-CICEEhrU.d.ts → turn-operations-DYKtoVd9.d.ts} +3 -3
  64. package/dist/{turn-operations-CICEEhrU.d.ts.map → turn-operations-DYKtoVd9.d.ts.map} +1 -1
  65. package/dist/{types-CRf_uTpK.d.ts → types-BMwiYRIJ.d.ts} +100 -6
  66. package/dist/types-BMwiYRIJ.d.ts.map +1 -0
  67. package/dist/types-BiobHM1D.js.map +1 -1
  68. package/dist/types.d.ts +5 -5
  69. package/docs/RESTATE.md +79 -5
  70. package/docs/RUN_IN_BACKGROUND.md +10 -1
  71. package/docs/SKILL.md +11 -4
  72. package/package.json +1 -1
  73. package/dist/agent-AnumGPWj.d.ts.map +0 -1
  74. package/dist/contexts-CbI8dRfI.js.map +0 -1
  75. package/dist/index-LX8KCBXU.d.ts.map +0 -1
  76. package/dist/mcp-DzuTfq-I.js.map +0 -1
  77. package/dist/presets-CTNbWXWz.js.map +0 -1
  78. package/dist/tools-ycHDeHBZ.js.map +0 -1
  79. package/dist/types-CRf_uTpK.d.ts.map +0 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"contexts-GKAWYq07.js","names":["spawnChild"],"sources":["../src/contexts/process.ts","../src/contexts/sandbox.ts","../src/contexts/types.ts"],"sourcesContent":["/**\n * In-process execution context.\n *\n * Runs everything in the current Node/Bun process.\n * No isolation — fastest, used as the default.\n */\n\nimport type { Buffer } from 'node:buffer'\nimport type { ChildProcess } from 'node:child_process'\nimport type { WriteStream } from 'node:fs'\nimport type { ContextCapabilities, ExecResult, ExecutionContext, ExecutionHandle, SpawnConfig, TaskEntry, TaskExitInfo, TaskHandle, TaskStallInfo } from './types'\nimport { spawn as spawnChild } from 'node:child_process'\nimport { createWriteStream } from 'node:fs'\nimport { mkdir, readdir, readFile, writeFile } from 'node:fs/promises'\nimport { dirname, resolve } from 'node:path'\n\n/**\n * Whether the host supports POSIX process groups (the `detached: true` +\n * `process.kill(-pid)` combination). Windows doesn't — its job-object\n * model is shaped differently — so on win32 we fall back to killing the\n * shell wrapper alone (matches pre-fix behavior; better than nothing).\n */\nconst SUPPORTS_PROCESS_GROUPS = process.platform !== 'win32'\n\n/**\n * Default cap on captured stdout / stderr per child. Matches the\n * pre-fix `child_process.exec` setting so existing callers see the\n * same buffer envelope. Output beyond this is truncated and a\n * marker is appended to stderr.\n */\nconst DEFAULT_MAX_BUFFER = 10 * 1024 * 1024\n\n/**\n * Sanitize a task id before it's joined into a filesystem path.\n *\n * We mint `bash_<n>` ids ourselves (no user input flows into the path\n * for `ProcessContext`), so this is defensive — but third-party contexts\n * MAY accept caller-provided ids, so the helper exists for them too.\n * Anything that doesn't match the expected shape is rejected — never\n * coerced — so the call site sees a clear error rather than a\n * traversal-shaped path.\n */\nconst TASK_ID_RE = /^[a-z][\\w-]*$/i\n\nfunction assertSafeTaskId(taskId: string): void {\n if (!TASK_ID_RE.test(taskId))\n throw new Error(`Invalid task id \"${taskId}\" — must match ${TASK_ID_RE}.`)\n}\n\n/**\n * Format `date` as `YYYYMMDD-HHMMSS-mmm` in UTC.\n *\n * Pinned to UTC so the lexical sort of two timestamps always matches\n * their chronological order (local time + DST does not). Used as the\n * per-context suffix on background-task log filenames; see the field\n * doc on `contextTimestamp` for the why.\n */\nexport function formatContextTimestamp(date: Date): string {\n const pad2 = (n: number): string => n.toString().padStart(2, '0')\n const pad3 = (n: number): string => n.toString().padStart(3, '0')\n const y = date.getUTCFullYear()\n const M = pad2(date.getUTCMonth() + 1)\n const d = pad2(date.getUTCDate())\n const h = pad2(date.getUTCHours())\n const m = pad2(date.getUTCMinutes())\n const s = pad2(date.getUTCSeconds())\n const ms = pad3(date.getUTCMilliseconds())\n return `${y}${M}${d}-${h}${m}${s}-${ms}`\n}\n\n/** Pattern of a background-task log filename. Used by tests + tooling. */\nexport const TASK_LOG_FILENAME_RE = /^(bash_\\d+)\\.(\\d{8}-\\d{6}-\\d{3})\\.log$/\n\nexport function createProcessContext(config?: SpawnConfig): ExecutionContext {\n let counter = 0\n const handles = new Map<string, ExecutionHandle>()\n const defaultCwd = config?.cwd ?? process.cwd()\n const defaultEnv = config?.env\n\n /**\n * Per-context background-task registry. Entries live for the context's\n * lifetime — even after the child exits — so the model can read output\n * of completed tasks until `destroy()` tears everything down. Kept as\n * a plain Map (not a class) per code-quality checklist #7: no premature\n * abstraction. The state and the operations on it live inline.\n */\n const tasks = new Map<string, TaskState>()\n let taskCounter = 0\n\n /**\n * Per-context UTC timestamp segment baked into every background task's\n * log filename. Same `taskCounter` value across two contexts (e.g. a\n * TUI restart) would otherwise re-open the SAME `bash_<n>.log` file —\n * we open with `flags: 'a'` so the new task would APPEND into the old\n * log, producing scrambled output. The timestamp guarantees each\n * context owns a distinct log filename without forcing the\n * model-facing task id (`bash_<n>`) to grow longer.\n *\n * Format: `YYYYMMDD-HHMMSS-mmm` in UTC.\n * - Sortable (lexical sort = chronological sort).\n * - Unambiguous (UTC sidesteps DST / locale shifts).\n * - Filesystem-safe (digits + hyphens only).\n * - Millisecond precision avoids same-second-restart collisions.\n *\n * The timestamp is computed once per context, NOT per task — all of a\n * context's tasks share the same suffix so a directory listing groups\n * cleanly by \"which run produced these\".\n */\n const contextTimestamp = formatContextTimestamp(new Date())\n\n /**\n * Last-resort orphan reaper. With `detached: true`, background tasks\n * are in their OWN process group — when the parent (zidane TUI) dies,\n * the OS does NOT send them SIGHUP and they keep running indefinitely.\n * The user's \"Ctrl+C the TUI\" intent is \"stop everything I started\",\n * not \"leak a `sleep 60` into the background\".\n *\n * `process.on('exit')` fires SYNCHRONOUSLY on `process.exit()` AND on\n * natural shutdown — exactly the seam we need. The handler can only\n * do synchronous work (Node ignores async), but `process.kill` IS\n * synchronous, so a SIGTERM-the-group sweep lands cleanly. The kill\n * is best-effort: already-dead children throw ESRCH (swallowed).\n *\n * Registered lazily on first `execBackground` so contexts that never\n * background a task don't pay the listener cost. Deregistered in\n * `destroy()` so reconstructed contexts don't accumulate listeners\n * (Node warns past 10 — a long-running session that switches sessions\n * frequently would otherwise hit that).\n */\n let exitHandlerRegistered = false\n const exitHandler = (): void => {\n for (const task of tasks.values()) {\n if (task.status !== 'running')\n continue\n const pid = task.child.pid\n if (pid === undefined)\n continue\n try {\n if (SUPPORTS_PROCESS_GROUPS)\n process.kill(-pid, 'SIGTERM')\n else\n process.kill(pid, 'SIGTERM')\n }\n catch {\n // ESRCH (already dead) / EPERM (lost ownership). Swallow —\n // the process either won't kill cleanly OR is already gone,\n // both acceptable at shutdown.\n }\n }\n }\n\n return {\n type: 'process',\n\n capabilities: {\n shell: true,\n filesystem: true,\n network: true,\n gpu: false,\n // Background tasks are OS children of this process's machine —\n // they survive `agent.destroy()` reassignment games but NOT a\n // host machine reboot, and the orphan reaper kills them on\n // process exit. That's the 'process-lifetime' tier.\n detachedTasks: 'process-lifetime',\n } satisfies ContextCapabilities,\n\n async spawn(overrides?: SpawnConfig): Promise<ExecutionHandle> {\n const id = `process-${++counter}`\n const cwd = overrides?.cwd ?? defaultCwd\n\n await mkdir(cwd, { recursive: true })\n\n const handle: ExecutionHandle = { id, type: 'process', cwd }\n handles.set(id, handle)\n return handle\n },\n\n async exec(\n handle: ExecutionHandle,\n command: string,\n options?: { cwd?: string, env?: Record<string, string>, timeout?: number, signal?: AbortSignal },\n ): Promise<ExecResult> {\n const cwd = options?.cwd ? resolve(handle.cwd, options.cwd) : handle.cwd\n\n // Pre-aborted fast path: skip the spawn entirely and synthesize a\n // killed-by-signal result. Saves a spawn round-trip + dodges Node\n // emitting an immediate `AbortError`.\n if (options?.signal?.aborted) {\n return { stdout: '', stderr: 'aborted by signal before spawn', exitCode: 143 }\n }\n\n const timeoutMs = (options?.timeout ?? config?.limits?.timeout ?? 30) * 1000\n const maxBuffer = DEFAULT_MAX_BUFFER\n\n return new Promise<ExecResult>((resolveP) => {\n // Spawn as a NEW process group leader so we can kill the whole\n // subtree on abort. Without `detached: true`, sending SIGTERM to\n // the shell's pid only kills the shell wrapper — its child\n // processes (the actual `sleep`, `npm`, `python`, …) get\n // reparented to init and keep running. `process.kill(-pid, …)`\n // with a NEGATIVE pid targets the whole process group; that's\n // the POSIX idiom for \"shut down everything I started\".\n //\n // On Windows there are no process groups in the POSIX sense, so\n // we leave `detached` off and accept the shell-only kill — the\n // platform's job-object machinery is the path forward there if\n // we ever need it, but it's not the bug zidane's users are\n // hitting today.\n const child = spawnChild('/bin/sh', ['-c', command], {\n cwd,\n env: { ...process.env, ...defaultEnv, ...options?.env },\n stdio: ['ignore', 'pipe', 'pipe'],\n detached: SUPPORTS_PROCESS_GROUPS,\n })\n\n let stdout = ''\n let stderr = ''\n // Byte accounting per slot. `maxBuffer` is a BYTE budget — comparing\n // it against the accumulated string's UTF-16 `.length` undercounts\n // multi-byte UTF-8 (overshooting the budget) and slicing the Buffer\n // at a char-derived offset can split a codepoint mid-sequence.\n let stdoutBytes = 0\n let stderrBytes = 0\n let bufferTruncated = false\n let timedOut = false\n let killedByAbort = false\n let settled = false\n\n const appendCapped = (slot: 'stdout' | 'stderr', chunk: Buffer): void => {\n const used = slot === 'stdout' ? stdoutBytes : stderrBytes\n if (used >= maxBuffer) {\n // Buffer filled exactly on a prior chunk boundary — this chunk\n // is data we're dropping, so it still counts as truncation.\n if (!bufferTruncated) {\n bufferTruncated = true\n killProcessGroup(child, 'SIGTERM')\n }\n return\n }\n const room = maxBuffer - used\n let piece = chunk\n if (chunk.length > room) {\n // Cut on a UTF-8 codepoint boundary: if the byte right after the\n // cut is a continuation byte (0b10xxxxxx), the cut would land\n // mid-codepoint — back off until it doesn't.\n let end = room\n while (end > 0 && (chunk[end] & 0xC0) === 0x80)\n end--\n piece = chunk.subarray(0, end)\n }\n if (slot === 'stdout') {\n stdout += piece.toString('utf8')\n stdoutBytes += piece.length\n }\n else {\n stderr += piece.toString('utf8')\n stderrBytes += piece.length\n }\n if (chunk.length > room) {\n bufferTruncated = true\n // Kill on overflow — matches `execAsync`'s `maxBuffer`\n // behavior (which kills the child and surfaces an error).\n killProcessGroup(child, 'SIGTERM')\n }\n }\n\n child.stdout?.on('data', chunk => appendCapped('stdout', chunk as Buffer))\n child.stderr?.on('data', chunk => appendCapped('stderr', chunk as Buffer))\n\n const timeoutTimer = timeoutMs > 0\n ? setTimeout(() => {\n timedOut = true\n killProcessGroup(child, 'SIGTERM')\n }, timeoutMs)\n : undefined\n\n const onAbort = (): void => {\n killedByAbort = true\n killProcessGroup(child, 'SIGTERM')\n }\n const userSignal = options?.signal\n if (userSignal)\n userSignal.addEventListener('abort', onAbort, { once: true })\n\n const settle = (exitCode: number, extraStderr?: string): void => {\n if (settled)\n return\n settled = true\n if (timeoutTimer)\n clearTimeout(timeoutTimer)\n if (userSignal)\n userSignal.removeEventListener('abort', onAbort)\n const finalStderr = extraStderr\n ? (stderr ? `${stderr}\\n${extraStderr}` : extraStderr)\n : stderr\n resolveP({ stdout, stderr: finalStderr, exitCode })\n }\n\n child.on('error', (err) => {\n // Spawn failure (ENOENT on `/bin/sh`, EACCES, …). Mirror\n // `execAsync`'s \"rejects with an Error\" shape by surfacing\n // the message on stderr and a non-zero exit.\n settle(1, err.message)\n })\n\n child.on('close', (code, signal) => {\n // Order matters: killed-by-our-abort wins over timeout wins\n // over natural exit, because the abort listener fires first\n // when the user cancels mid-timeout-window. All three land\n // here through `close`, but we tag the stderr suffix /\n // exit-code differently so callers can tell the cause.\n if (killedByAbort) {\n settle(143, 'aborted by signal')\n return\n }\n if (timedOut) {\n settle(124, `command timed out after ${timeoutMs}ms`)\n return\n }\n if (bufferTruncated) {\n settle(143, `output exceeded ${maxBuffer}-byte buffer; process killed`)\n return\n }\n if (signal) {\n // Killed by some other signal we didn't issue. Treat as\n // signal-killed for consumer compat.\n settle(128 + 15, `terminated by signal ${signal}`)\n return\n }\n settle(typeof code === 'number' ? code : 1)\n })\n })\n },\n\n async readFile(handle: ExecutionHandle, path: string): Promise<string> {\n return readFile(resolve(handle.cwd, path), 'utf-8')\n },\n\n async readFileBinary(handle: ExecutionHandle, path: string): Promise<Uint8Array> {\n // No encoding → returns a Buffer (which is a Uint8Array). Used by\n // read_file to ferry image / binary content into the multimodal route.\n const buf = await readFile(resolve(handle.cwd, path))\n return new Uint8Array(buf)\n },\n\n async writeFile(handle: ExecutionHandle, path: string, content: string): Promise<void> {\n const fullPath = resolve(handle.cwd, path)\n await mkdir(dirname(fullPath), { recursive: true })\n await writeFile(fullPath, content, 'utf-8')\n },\n\n async listFiles(handle: ExecutionHandle, path: string): Promise<string[]> {\n return readdir(resolve(handle.cwd, path))\n },\n\n async execBackground(\n handle: ExecutionHandle,\n command: string,\n options: {\n cwd?: string\n env?: Record<string, string>\n outputDir: string\n onExit?: (info: TaskExitInfo) => void\n maxOutputBytes?: number\n stallTimeoutMs?: number\n onStall?: (info: TaskStallInfo) => void\n },\n ): Promise<TaskHandle> {\n const cwd = options.cwd ? resolve(handle.cwd, options.cwd) : handle.cwd\n\n await mkdir(options.outputDir, { recursive: true })\n\n // Mint id + path. The id is sequential per context (model-facing,\n // short, ergonomic for `shell_kill`). The log FILENAME embeds the\n // context's start timestamp so two contexts sharing an `outputDir`\n // (TUI restart on the same session, concurrent zidane instances,\n // …) never resolve to the same file — we open with `flags: 'a'`\n // and a name collision would interleave their output. Path\n // validation is defensive — we mint our own ids so it never trips\n // today, but it pins the invariant for forks / third parties.\n const taskId = `bash_${++taskCounter}`\n assertSafeTaskId(taskId)\n const outputPath = resolve(options.outputDir, `${taskId}.${contextTimestamp}.log`)\n\n // Install the orphan reaper on first task. See `exitHandler`'s\n // JSDoc for the kill-on-shutdown rationale.\n if (!exitHandlerRegistered) {\n process.on('exit', exitHandler)\n exitHandlerRegistered = true\n }\n\n // Open the output file. The timestamped path is unique per context\n // so a brand-new file is the expected outcome; `flags: 'a'` is kept\n // as the safe default (preserves bytes if the path collides for any\n // reason — same-millisecond context creation, manual pre-population,\n // etc.) rather than blindly truncating. Streams are opened BEFORE\n // the spawn to avoid a race where the child writes before the stream\n // is ready — `child_process` buffers stdio until the consumer\n // attaches, but the FS handle has to exist either way for our pipe.\n const outputStream: WriteStream = createWriteStream(outputPath, { flags: 'a' })\n // Surface FS errors (ENOSPC, EACCES on a remounted FS, etc.)\n // under ZIDANE_DEBUG instead of crashing the host via an\n // unhandled 'error' event. Without a listener Node escalates\n // any stream-level error to an uncaughtException and the whole\n // process exits — the model and the user would lose every\n // unrelated in-flight piece of work to one bad task's disk\n // hiccup. Swallow + log is the safer default for a fire-and-\n // forget log writer; the task's exit code still reports.\n outputStream.on('error', (err) => {\n if (process.env.ZIDANE_DEBUG)\n process.stderr.write(`[zidane/contexts] task ${taskId} log stream error: ${err.message}\\n`)\n })\n\n // Spawn as a NEW process group leader so we can kill the whole\n // subtree on demand. Same primitive `exec` uses for foreground\n // shells — see the long comment in that method for the\n // process-group rationale.\n const child = spawnChild('/bin/sh', ['-c', command], {\n cwd,\n env: { ...process.env, ...defaultEnv, ...options.env },\n stdio: ['ignore', 'pipe', 'pipe'],\n detached: SUPPORTS_PROCESS_GROUPS,\n })\n\n const state: TaskState = {\n taskId,\n handleId: handle.id,\n pid: child.pid ?? -1,\n command,\n cwd,\n startedAt: Date.now(),\n outputPath,\n outputStream,\n child,\n status: 'running',\n bytesWritten: 0,\n settled: false,\n onExit: options.onExit,\n }\n tasks.set(taskId, state)\n\n // Output cap (optional). Beyond the cap the process keeps\n // running — head-priority retention: bytes already on disk stay,\n // subsequent bytes are counted but dropped, and a structured\n // truncation marker is appended at settle so the model can\n // pattern-match the loss. Killing on overflow (what the\n // foreground `exec` does) would be wrong here: long-running\n // servers legitimately log forever.\n const maxOutputBytes = typeof options.maxOutputBytes === 'number' && options.maxOutputBytes > 0\n ? options.maxOutputBytes\n : undefined\n let droppedBytes = 0\n // Latched on the first overflowing chunk. Without it, a codepoint-\n // boundary backoff (below) leaves a few bytes of \"room\" that later\n // chunks would dribble into — appending disjoint fragments from\n // much-later output right after the cut point.\n let capExhausted = false\n\n // Stall watchdog (optional). One-shot per quiet period: fires\n // `onStall` after `stallTimeoutMs` of no output, then stays quiet\n // until fresh output re-arms it. `unref()` so a pending timer\n // never holds the host process open.\n const stallTimeoutMs = typeof options.stallTimeoutMs === 'number' && options.stallTimeoutMs > 0\n ? options.stallTimeoutMs\n : undefined\n const onStall = options.onStall\n let stallTimer: NodeJS.Timeout | undefined\n let lastOutputAt = Date.now()\n const armStallTimer = (): void => {\n if (!stallTimeoutMs || !onStall)\n return\n if (stallTimer)\n clearTimeout(stallTimer)\n stallTimer = setTimeout(() => {\n stallTimer = undefined\n if (state.settled)\n return\n try {\n onStall({\n taskId,\n command,\n outputPath,\n stalledForMs: Date.now() - lastOutputAt,\n bytesWritten: state.bytesWritten,\n })\n }\n catch (err) {\n if (process.env.ZIDANE_DEBUG)\n process.stderr.write(`[zidane/contexts] task ${taskId} onStall threw: ${err instanceof Error ? err.message : String(err)}\\n`)\n }\n }, stallTimeoutMs)\n stallTimer.unref?.()\n }\n const clearStallTimer = (): void => {\n if (stallTimer) {\n clearTimeout(stallTimer)\n stallTimer = undefined\n }\n }\n armStallTimer()\n\n // Pipe both streams into the same file. Order between stdout and\n // stderr is preserved per-stream; cross-stream ordering depends on\n // Node's event loop — acceptable interleaving for log-shaped\n // output. Tracked-bytes is updated on every chunk for the\n // listBackground UX (counted even past the cap, so the UX shows\n // the task's TRUE output volume).\n const appendChunk = (chunk: Buffer): void => {\n state.bytesWritten += chunk.length\n lastOutputAt = Date.now()\n armStallTimer()\n if (maxOutputBytes !== undefined) {\n const onDisk = state.bytesWritten - droppedBytes - chunk.length\n if (capExhausted || onDisk >= maxOutputBytes) {\n capExhausted = true\n droppedBytes += chunk.length\n return\n }\n const room = maxOutputBytes - onDisk\n if (chunk.length > room) {\n capExhausted = true\n // Cut on a UTF-8 codepoint boundary, same as the foreground\n // exec cap: back off while the byte after the cut is a\n // continuation byte (0b10xxxxxx) so the log never ends in a\n // mangled half-codepoint right before the truncation marker.\n let end = room\n while (end > 0 && (chunk[end] & 0xC0) === 0x80)\n end--\n droppedBytes += chunk.length - end\n if (end > 0)\n outputStream.write(chunk.subarray(0, end))\n return\n }\n }\n outputStream.write(chunk)\n }\n child.stdout?.on('data', chunk => appendChunk(chunk as Buffer))\n child.stderr?.on('data', chunk => appendChunk(chunk as Buffer))\n\n // Settle path — at-most-once via `settled` flag (checklist #14).\n // Three trigger sources: `close` (natural OR signal-killed),\n // `error` (spawn failure), explicit `killBackground` (which\n // routes through `close` itself after the SIGTERM lands).\n const settle = (cause: 'close' | 'error', code: number | null, signal: NodeJS.Signals | null, errMessage?: string): void => {\n if (state.settled)\n return\n state.settled = true\n clearStallTimer()\n state.endedAt = Date.now()\n\n // Determine final status from cause + signal.\n const status: TaskExitInfo['status']\n = signal === 'SIGTERM' || state.killRequested\n ? 'killed'\n : 'exited'\n // Signal-killed children report null `code` from Node; map back\n // to the POSIX `128 + signum` convention so consumers can read\n // an integer either way.\n const exitCode = code !== null\n ? code\n : signal === 'SIGTERM'\n ? 143\n : signal\n ? 128\n : 1\n state.status = status\n state.exitCode = exitCode\n if (signal)\n state.signal = signal\n\n // Flush + close the WriteStream BEFORE firing onExit — model\n // may read the file in the same turn it receives the\n // notification, and a still-open stream can hold tail bytes\n // back from disk. `stream.end(callback)` is the documented\n // \"all queued writes are flushed when this fires\" idiom.\n //\n // ORDER MATTERS: any error preamble we want in the log file\n // (spawn failures with no stdout, buffer overflows) MUST be\n // written BEFORE `end()` — once `end()` is called the stream\n // is closed for writing and subsequent `.write()` calls are\n // dropped. Earlier revisions had this reversed and silently\n // lost ENOENT-on-`/bin/sh` messages.\n if (errMessage) {\n try {\n outputStream.write(`\\n${errMessage}\\n`)\n }\n catch {\n // Stream may have errored before this — best-effort only.\n }\n }\n if (droppedBytes > 0) {\n try {\n // Structured + loud — the model can pattern-match the tag\n // rather than misread a truncated log as the full output.\n outputStream.write(`\\n<output-truncated bytes-dropped=\"${droppedBytes}\"/>\\n`)\n }\n catch {\n // Best-effort only, same as the error preamble above.\n }\n }\n outputStream.end(() => {\n // `stateToTaskExitInfo` reads the same fields we just set\n // on `state`, so the snapshot the consumer gets matches the\n // post-settle state exactly. `onExit` is optional — pull-based\n // consumers reconcile via `listBackground` instead.\n try {\n state.onExit?.(stateToTaskExitInfo(state))\n }\n catch (err) {\n // Defensive — a buggy onExit callback shouldn't crash the\n // host. Surface via stderr under ZIDANE_DEBUG; otherwise\n // swallow. Matches the spawn-tool's bubbleError pattern.\n if (process.env.ZIDANE_DEBUG)\n process.stderr.write(`[zidane/contexts] task ${taskId} onExit threw: ${err instanceof Error ? err.message : String(err)}\\n`)\n }\n })\n }\n\n child.on('close', (code, signal) => settle('close', code, signal))\n child.on('error', err => settle('error', null, null, `[spawn error] ${err.message}`))\n\n return { taskId, pid: state.pid, outputPath }\n },\n\n async killBackground(handle: ExecutionHandle, taskId: string): Promise<TaskExitInfo | null> {\n const state = tasks.get(taskId)\n // Two miss cases collapse into one `null` return: unknown id, AND\n // known-id-but-not-owned-by-this-handle. The second case is the\n // subagent-can't-kill-parent-tasks defense; surfacing it as a\n // distinct error would leak the existence of the parent's task\n // to the subagent's model, which violates the per-handle\n // isolation contract.\n if (!state || state.handleId !== handle.id)\n return null\n // Already exited — return the cached info. We don't keep a\n // separate cached exit; the state itself carries every field\n // `TaskExitInfo` needs and `stateToTaskExitInfo` projects it.\n if (state.status !== 'running')\n return stateToTaskExitInfo(state)\n\n // Mark the intent BEFORE issuing the kill so the close handler\n // classifies the exit as `'killed'` even on platforms where the\n // SIGTERM-via-group lands faster than the close event drains.\n // (Checklist #14: at-most-once settle, plus correct status\n // classification regardless of event ordering.)\n state.killRequested = true\n\n // Wait for the existing close listener to fire — `settle()` does\n // all the flushing + onExit work. We just sit on a one-shot\n // promise tied to the `child.on('close')` we already registered\n // at spawn time.\n const closed = new Promise<void>((resolveP) => {\n if (state.settled) {\n resolveP()\n return\n }\n const originalOnExit = state.onExit\n state.onExit = (info) => {\n originalOnExit?.(info)\n resolveP()\n }\n })\n\n killProcessGroup(state.child, 'SIGTERM')\n await closed\n return stateToTaskExitInfo(state)\n },\n\n async waitBackground(\n handle: ExecutionHandle,\n taskId: string,\n options?: { timeoutMs?: number, signal?: AbortSignal },\n ): Promise<TaskExitInfo | null> {\n const state = tasks.get(taskId)\n // Same two-misses-collapse-to-null contract as `killBackground`:\n // unknown id AND known-but-other-handle both return `null` so a\n // subagent can't observe the parent's tasks through the wait seam.\n if (!state || state.handleId !== handle.id)\n return null\n if (state.settled)\n return stateToTaskExitInfo(state)\n\n return new Promise<TaskExitInfo | null>((resolveP) => {\n let done = false\n let timer: NodeJS.Timeout | undefined\n const signal = options?.signal\n const finish = (value: TaskExitInfo | null): void => {\n if (done)\n return\n done = true\n if (timer)\n clearTimeout(timer)\n signal?.removeEventListener('abort', onAbort)\n resolveP(value)\n }\n function onAbort(): void {\n finish(null)\n }\n\n // Chain onto the live exit callback rather than replacing it —\n // the original (notification enqueue, kill-waiter, a sibling\n // waiter) must keep firing. The `done` latch makes a late exit\n // after timeout a no-op for THIS waiter.\n const originalOnExit = state.onExit\n state.onExit = (info) => {\n originalOnExit?.(info)\n finish(stateToTaskExitInfo(state))\n }\n\n if (signal) {\n if (signal.aborted) {\n finish(null)\n return\n }\n signal.addEventListener('abort', onAbort, { once: true })\n }\n const timeoutMs = options?.timeoutMs\n if (typeof timeoutMs === 'number' && Number.isFinite(timeoutMs) && timeoutMs > 0) {\n timer = setTimeout(finish, timeoutMs, null)\n timer.unref?.()\n }\n })\n },\n\n async reassignBackgroundTasks(\n fromHandle: ExecutionHandle,\n toHandle: ExecutionHandle,\n newOnExit?: (info: TaskExitInfo) => void,\n ): Promise<readonly TaskEntry[]> {\n // No-op when source = destination — keeps the spawn.ts call site\n // unconditional without forcing it to dedupe.\n if (fromHandle.id === toHandle.id)\n return []\n const promoted: TaskEntry[] = []\n for (const state of tasks.values()) {\n if (state.handleId !== fromHandle.id || state.status !== 'running')\n continue\n state.handleId = toHandle.id\n // Replace the natural-exit callback. The original closed over\n // the spawning agent's hook bus, which is about to be destroyed\n // — without rewiring, the task's eventual `background:exit`\n // fires into a torn-down hookable and the parent never learns.\n if (newOnExit)\n state.onExit = newOnExit\n promoted.push(stateToTaskEntry(state))\n }\n return promoted\n },\n\n async listBackground(handle: ExecutionHandle): Promise<readonly TaskEntry[]> {\n // Snapshot — callers must not assume the returned array stays\n // in sync with the live registry. Sorted by startedAt so the\n // model / UI sees consistent ordering across calls. Scoped to\n // the calling handle so subagents don't see the parent's tasks\n // (and vice versa) in their listing.\n return [...tasks.values()]\n .filter(s => s.handleId === handle.id)\n .sort((a, b) => a.startedAt - b.startedAt)\n .map(stateToTaskEntry)\n },\n\n async destroy(handle: ExecutionHandle): Promise<void> {\n // Kill every still-running background task SPAWNED THROUGH THIS\n // HANDLE before tearing the handle down. SIGTERM the groups,\n // await the close + flush, THEN drop the registry entries.\n // Sequential — destroy is one-shot teardown, the few ms of extra\n // latency aren't worth the synchronization complexity.\n //\n // The handle scope matters when the same `ExecutionContext` is\n // shared across a parent agent and its `spawn`-ed subagents (the\n // default — `spawn.ts` passes `execution: ctx.execution`). Each\n // agent mints its own `ExecutionHandle` and registers its\n // background tasks under that handle's id. Without the filter,\n // a child agent's `destroy()` (fired by `spawn.ts`'s `finally`\n // when the subagent finishes / is cancelled) would walk the\n // shared registry and SIGTERM the parent's tasks too. So\n // cancelling a subagent that has its own background shells now\n // correctly kills JUST those subagent shells, leaving the\n // parent's intact.\n const survivors = [...tasks.values()].filter(s => s.handleId === handle.id && !s.settled)\n await Promise.all(survivors.map(async (state) => {\n state.killRequested = true\n await new Promise<void>((resolveP) => {\n const originalOnExit = state.onExit\n state.onExit = (info) => {\n originalOnExit?.(info)\n resolveP()\n }\n killProcessGroup(state.child, 'SIGTERM')\n })\n }))\n // Drop only this handle's tasks from the registry. Other handles\n // (siblings, parent) keep their entries.\n for (const [taskId, state] of tasks) {\n if (state.handleId === handle.id)\n tasks.delete(taskId)\n }\n handles.delete(handle.id)\n // Drop the orphan reaper ONLY when no handles remain — otherwise\n // a child's `destroy()` would strip the parent's safety net. The\n // reaper protects every still-tracked task in the context, so it\n // sticks around until the LAST handle is gone.\n //\n // Without this guard, the spawn-tool sequence\n // parent.spawn(child) → child.run() → child.destroy() (auto)\n // would deregister the handler mid-parent-lifetime. The parent's\n // own subsequent Ctrl+C orphan-kill safety would silently degrade.\n if (exitHandlerRegistered && handles.size === 0) {\n process.off('exit', exitHandler)\n exitHandlerRegistered = false\n }\n },\n }\n}\n\n/**\n * Per-task state. Lives in the context's `tasks` registry. Fields are\n * mutated in place by the spawn / close / kill / destroy code paths —\n * the registry isn't immutable. Treat the type as a record-of-cells,\n * not a value.\n */\ninterface TaskState {\n taskId: string\n /**\n * `ExecutionHandle.id` of the spawning agent. The registry is\n * context-scoped (one Map shared across all handles a context minted),\n * so a per-task owner tag is what scopes `listBackground` /\n * `killBackground` / `destroy` to the calling handle's slice.\n *\n * Without this, a subagent spawned via `spawn` tool — which inherits\n * the parent's `ExecutionContext` but mints its OWN handle — would\n * see (and accidentally kill on `destroy()`) every task the parent\n * had running. Came up the first time the model spawned a subagent\n * that ran a background task: the subagent's run-end `destroy()`\n * SIGTERMed the parent's `npm run dev` mid-flight.\n */\n handleId: string\n pid: number\n command: string\n cwd: string\n startedAt: number\n outputPath: string\n outputStream: WriteStream\n child: ChildProcess\n status: 'running' | 'exited' | 'killed'\n exitCode?: number\n signal?: NodeJS.Signals\n bytesWritten: number\n /**\n * `at-most-once` settle latch. Multiple trigger sources (`close`,\n * `error`, `kill`) can race; the flag dedupes so `onExit` fires\n * exactly once per task (checklist #14).\n */\n settled: boolean\n /**\n * Set by `killBackground` / `destroy` before issuing SIGTERM so the\n * close handler classifies the exit as `'killed'` even when the\n * platform delivers the close event ahead of our intent record.\n */\n killRequested?: boolean\n /**\n * `Date.now()` at settle time. Lets `durationMs` (and pull-based\n * reconcile consumers reading `TaskEntry.endedAt`) report the task's\n * real lifetime instead of a projection-time delta.\n */\n endedAt?: number\n /**\n * Optional push-style exit callback. Undefined for pull-based\n * consumers (remote / durable hosts reconcile via `listBackground`).\n * Mutated in place by the kill / destroy / wait chains.\n */\n onExit?: (info: TaskExitInfo) => void\n}\n\n/**\n * Send `signal` to the child's whole process group. Falls back to a\n * single-process kill on Windows (no POSIX process groups). Shared\n * across the foreground `exec` path, the background spawn / kill\n * paths, and the shutdown-time orphan reaper — keeping one definition\n * so the kill semantics can't drift between them.\n */\nfunction killProcessGroup(child: ChildProcess, signal: NodeJS.Signals): void {\n const pid = child.pid\n if (pid === undefined)\n return\n try {\n if (SUPPORTS_PROCESS_GROUPS)\n process.kill(-pid, signal)\n else\n process.kill(pid, signal)\n }\n catch {\n // ESRCH / EPERM — process is already gone (race with natural exit)\n // or we lost the right to kill it. Both are safe to swallow.\n }\n}\n\n/**\n * Project a `TaskState` to the `TaskEntry` shape `listBackground` and\n * `reassignBackgroundTasks` return. Single helper keeps the shape\n * consistent across both call sites (and a future addition of fields\n * to `TaskEntry` only needs to land here).\n */\nfunction stateToTaskEntry(state: TaskState): TaskEntry {\n return {\n taskId: state.taskId,\n pid: state.pid,\n command: state.command,\n cwd: state.cwd,\n startedAt: state.startedAt,\n ...(state.endedAt !== undefined ? { endedAt: state.endedAt } : {}),\n outputPath: state.outputPath,\n status: state.status,\n ...(state.exitCode !== undefined ? { exitCode: state.exitCode } : {}),\n ...(state.signal ? { signal: state.signal } : {}),\n bytesWritten: state.bytesWritten,\n }\n}\n\n/**\n * Project a settled `TaskState` to the `TaskExitInfo` shape `settle()`\n * fires from `onExit` and `killBackground` returns on the\n * cached-exit path. Pre-condition: `state.status !== 'running'`\n * (callers gate on this).\n */\nfunction stateToTaskExitInfo(state: TaskState): TaskExitInfo {\n return {\n taskId: state.taskId,\n status: state.status as Exclude<TaskState['status'], 'running'>,\n exitCode: state.exitCode ?? 0,\n ...(state.signal ? { signal: state.signal } : {}),\n outputPath: state.outputPath,\n // Settle-time delta when available (always set since `endedAt` was\n // introduced); the `Date.now()` fallback covers defensive callers\n // projecting an unsettled state.\n durationMs: (state.endedAt ?? Date.now()) - state.startedAt,\n command: state.command,\n }\n}\n","/**\n * Remote sandbox execution context.\n *\n * Offloads execution to a remote sandbox API (e.g. Rivet, E2B).\n * Specific providers implement the SandboxProvider interface.\n */\n\nimport type { ContextCapabilities, ExecResult, ExecutionContext, ExecutionHandle, SpawnConfig } from './types'\n\n// ---------------------------------------------------------------------------\n// Sandbox provider interface\n// ---------------------------------------------------------------------------\n\nexport interface SandboxProvider {\n name: string\n spawn: (config: SpawnConfig) => Promise<{ id: string, cwd: string }>\n exec: (sandboxId: string, command: string, options?: { cwd?: string, env?: Record<string, string>, timeout?: number }) => Promise<ExecResult>\n readFile: (sandboxId: string, path: string) => Promise<string>\n writeFile: (sandboxId: string, path: string, content: string) => Promise<void>\n listFiles: (sandboxId: string, path: string) => Promise<string[]>\n destroy: (sandboxId: string) => Promise<void>\n}\n\n// ---------------------------------------------------------------------------\n// Sandbox execution context\n// ---------------------------------------------------------------------------\n\nexport function createSandboxContext(provider: SandboxProvider): ExecutionContext {\n const sandboxes = new Map<string, string>()\n\n function getSandboxId(handle: ExecutionHandle): string {\n const id = sandboxes.get(handle.id)\n if (!id)\n throw new Error(`Sandbox ${handle.id} not found`)\n return id\n }\n\n return {\n type: 'sandbox',\n\n capabilities: {\n shell: true,\n filesystem: true,\n network: true,\n gpu: false,\n } satisfies ContextCapabilities,\n\n async spawn(config?: SpawnConfig): Promise<ExecutionHandle> {\n const result = await provider.spawn(config ?? {})\n const handle: ExecutionHandle = { id: result.id, type: 'sandbox', cwd: result.cwd }\n sandboxes.set(handle.id, result.id)\n return handle\n },\n\n async exec(handle: ExecutionHandle, command: string, options?): Promise<ExecResult> {\n return provider.exec(getSandboxId(handle), command, options)\n },\n\n async readFile(handle: ExecutionHandle, path: string): Promise<string> {\n return provider.readFile(getSandboxId(handle), path)\n },\n\n async writeFile(handle: ExecutionHandle, path: string, content: string): Promise<void> {\n return provider.writeFile(getSandboxId(handle), path, content)\n },\n\n async listFiles(handle: ExecutionHandle, path: string): Promise<string[]> {\n return provider.listFiles(getSandboxId(handle), path)\n },\n\n async destroy(handle: ExecutionHandle): Promise<void> {\n const id = sandboxes.get(handle.id)\n if (!id)\n return\n await provider.destroy(id)\n sandboxes.delete(handle.id)\n },\n }\n}\n","/**\n * Execution context types.\n *\n * An execution context defines *where* and *how* an agent's tools run.\n * The agent loop and tools interact through this interface without knowing\n * whether they're running in-process, in a Docker container, or in a\n * remote sandbox.\n */\n\n// ---------------------------------------------------------------------------\n// Capabilities\n// ---------------------------------------------------------------------------\n\n/**\n * Lifetime guarantee of background tasks started through\n * {@link ExecutionContext.execBackground}.\n *\n * - `'none'` — the context cannot detach tasks at all.\n * - `'process-lifetime'` — tasks live as long as the HOST process; a\n * crash/restart of the host orphans or kills them (`ProcessContext`).\n * - `'durable'` — tasks live on a remote runner and survive host process\n * death (remote execution contexts driven by durable runtimes).\n *\n * Durable-execution adapters (e.g. `zidane/restate`) consult this to\n * decide whether backgrounding is safe to expose: a `'durable'` context\n * keeps its tasks across worker crashes, so there is no reason to strip\n * the capability from the model.\n */\nexport type DetachedTasksCapability = 'none' | 'process-lifetime' | 'durable'\n\nexport interface ContextCapabilities {\n /** Can execute shell commands */\n shell: boolean\n /** Can read/write files in a workspace */\n filesystem: boolean\n /** Can make outbound network requests */\n network: boolean\n /** Has GPU access */\n gpu: boolean\n /**\n * Background-task lifetime guarantee. Optional for backward\n * compatibility — when absent, callers infer `'process-lifetime'`\n * if the context implements `execBackground`, `'none'` otherwise\n * (see {@link resolveDetachedTasksCapability}).\n */\n detachedTasks?: DetachedTasksCapability\n}\n\n/**\n * Effective {@link DetachedTasksCapability} of a context, with the\n * backward-compatible inference for contexts that predate the field.\n */\nexport function resolveDetachedTasksCapability(context: ExecutionContext): DetachedTasksCapability {\n return context.capabilities.detachedTasks\n ?? (context.execBackground ? 'process-lifetime' : 'none')\n}\n\n// ---------------------------------------------------------------------------\n// Execution handle\n// ---------------------------------------------------------------------------\n\n/** Opaque handle to a running execution context instance */\nexport interface ExecutionHandle {\n id: string\n type: ContextType\n /** Working directory within the context */\n cwd: string\n}\n\n// ---------------------------------------------------------------------------\n// Exec result\n// ---------------------------------------------------------------------------\n\nexport interface ExecResult {\n stdout: string\n stderr: string\n exitCode: number\n}\n\n// ---------------------------------------------------------------------------\n// Spawn config\n// ---------------------------------------------------------------------------\n\nexport interface SpawnConfig {\n /** Working directory (created if it doesn't exist) */\n cwd?: string\n /** Environment variables */\n env?: Record<string, string>\n /** Docker image (only for 'docker' context) */\n image?: string\n /** Docker container name prefix (only for 'docker' context) */\n name?: string\n /** Host paths mounted into the context (only for 'docker' context today) */\n mounts?: ContextMount[]\n /** Resource limits */\n limits?: {\n /** Memory limit in MB */\n memory?: number\n /** CPU limit (e.g. '1.0' = 1 core) */\n cpu?: string\n /** Timeout in seconds for the entire context lifetime */\n timeout?: number\n }\n /** Sandbox provider config (only for 'sandbox' context) */\n sandbox?: {\n provider: string\n apiKey?: string\n [key: string]: unknown\n }\n\n /**\n * Publish container ports on the host (docker context only).\n *\n * Each entry maps a container port to either an explicit host port or\n * (when `host` is omitted) a Docker-assigned random port. Retrieve the\n * actual host port at runtime via `getMappedPort(container)` on the\n * docker context.\n */\n ports?: Array<{ container: number, host?: number, proto?: 'tcp' | 'udp' }>\n\n /**\n * UID/GID the container should run as (docker context only).\n *\n * Accepts the same forms Docker's `--user` does: `uid`, `uid:gid`, or a\n * named user that exists in the image. Default is the image's default\n * user (typically root). Setting this to the host user's `uid:gid`\n * avoids the EACCES-on-cleanup problem when sharing a workspace via\n * a `shared` mount.\n */\n user?: string\n\n /**\n * User-defined Docker network to join (docker context only).\n *\n * Defaults to Docker's default bridge. Use a user network when you\n * need multiple sibling containers (e.g. agent + database + dev\n * server) to discover each other by name.\n */\n network?: string\n\n /**\n * Docker labels to attach to the container (docker context only).\n *\n * Useful for ownership tracking — callers can sweep abandoned\n * containers (e.g. from a crashed parent process) by filtering on\n * a label they own: `docker ps -aq --filter label=my-app=true`.\n */\n labels?: Record<string, string>\n}\n\nexport interface ContextMount {\n /** Absolute host path to mount. */\n source: string\n /** Absolute path inside the execution context. */\n target: string\n /** Mount read-only. Defaults to false for Docker's native bind behavior. */\n readonly?: boolean\n /**\n * Apply the SELinux shared label (`:z`) so the host user and the container\n * user can both read/write the mount (docker context only). No-op on\n * non-SELinux hosts. Combine with `SpawnConfig.user` to avoid root-owned\n * files leaking onto the host. Mutually exclusive with `readonly`.\n */\n shared?: boolean\n}\n\n// ---------------------------------------------------------------------------\n// Execution context interface\n// ---------------------------------------------------------------------------\n\nexport type ContextType = 'process' | 'docker' | 'sandbox'\n\nexport interface ExecutionContext {\n /** Context type identifier */\n readonly type: ContextType\n\n /** What this context supports */\n readonly capabilities: ContextCapabilities\n\n /** Spawn a new execution environment */\n spawn: (config?: SpawnConfig) => Promise<ExecutionHandle>\n\n /**\n * Execute a shell command in the context.\n *\n * `signal` propagates abort all the way down to the underlying child\n * process — the implementation is expected to wire it into whatever\n * spawn primitive it uses so the OS receives a SIGTERM (or equivalent)\n * when the caller aborts. Without it, a cancelled tool returns its\n * cancellation marker to the model but the underlying process keeps\n * running in the background, orphaning compute / locks / IO. Pass\n * `ctx.signal` from a tool body to inherit the per-call + run-level\n * abort union; pass a freshly-built one for a host-driven kill.\n *\n * Implementations are free to ignore `signal` (the contract degrades\n * gracefully — the process simply won't be killed), but the in-process\n * default DOES honor it via `child_process.exec`'s native `signal`\n * option.\n */\n exec: (handle: ExecutionHandle, command: string, options?: { cwd?: string, env?: Record<string, string>, timeout?: number, signal?: AbortSignal }) => Promise<ExecResult>\n\n /**\n * Start a process in the background. Settles as soon as `spawn` returns\n * — does NOT wait for the child to exit. Stdout + stderr stream\n * interleaved to the file at the returned `outputPath`. The caller\n * (typically the agent) reads incremental output via the regular\n * {@link ExecutionContext.readFile} seam.\n *\n * Optional — contexts without background support (some remote sandboxes)\n * just don't implement it. The shell tool surfaces a clean\n * \"background mode is not supported in this execution context\" error\n * when this is undefined.\n *\n * `onExit` is called once when the child terminates (natural, killed,\n * or error). The same instance that called `execBackground` is the\n * exclusive owner of the callback — it's not a multi-cast bus. Hosts\n * wire this to the agent's pending-notification queue so the model\n * gets a `<task-notification>` on its next turn.\n *\n * `onExit` is OPTIONAL: remote / durable contexts may have no legal\n * way to push a callback from a timer back into the host (a Restate\n * journal, for instance, forbids out-of-band writes). Such contexts\n * simply record the exit in their registry; the agent loop reconciles\n * by polling {@link ExecutionContext.listBackground} at run\n * boundaries, where a host journal wrapper is legal (the\n * `background:reconcile` hook is the journalable seam).\n *\n * See `docs/RUN_IN_BACKGROUND.md` for the broader design contract\n * (file location, replay semantics, suppression rules).\n */\n execBackground?: (\n handle: ExecutionHandle,\n command: string,\n options: {\n cwd?: string\n env?: Record<string, string>\n /**\n * Absolute directory the context appends `<task-id>.log` to. The\n * agent owns this path because it carries session-shaped knowledge\n * (`<userDir>/<sessionId>/tasks/`) the context can't synthesize.\n * Must already exist OR be creatable by the context — the\n * implementation handles `mkdir -p` defensively.\n */\n outputDir: string\n /** Push-style exit callback. Optional — see the method doc. */\n onExit?: (info: TaskExitInfo) => void\n /**\n * Cap on bytes written to the output file. Beyond the cap the\n * process KEEPS RUNNING; further output is counted but dropped,\n * and a `<output-truncated bytes-dropped=\"N\"/>` marker is\n * appended when the task settles. Unset / non-positive = no cap.\n */\n maxOutputBytes?: number\n /**\n * Stall watchdog: when the task produces no output for this many\n * milliseconds, `onStall` fires ONCE (one-shot — re-arms only\n * after fresh output arrives). The process is NOT killed; the\n * consumer decides (typically by telling the model the task may\n * be stuck at an interactive prompt). Unset = no watchdog.\n */\n stallTimeoutMs?: number\n /** One-shot stall callback — see `stallTimeoutMs`. */\n onStall?: (info: TaskStallInfo) => void\n },\n ) => Promise<TaskHandle>\n\n /**\n * SIGTERM the whole process group of a running background task.\n * Idempotent — second call returns `null` (or the cached exit info).\n * Resolves once the process has exited AND its output stream has\n * been flushed + closed.\n *\n * `null` return on miss (unknown id, already cleaned up) so the\n * shell_kill tool can surface a clean \"no such task\" message\n * without throwing.\n */\n killBackground?: (\n handle: ExecutionHandle,\n taskId: string,\n ) => Promise<TaskExitInfo | null>\n\n /**\n * Snapshot of every task in the context's registry — running AND\n * terminated (entries remain until the next context destroy, so\n * the model can still read output of exited tasks).\n */\n listBackground?: (\n handle: ExecutionHandle,\n ) => Promise<readonly TaskEntry[]>\n\n /**\n * Block until a background task terminates, then resolve with its\n * exit info. Resolves immediately for already-terminated tasks.\n *\n * Returns `null` when:\n * - the task id is unknown (or owned by another handle — same\n * isolation contract as `killBackground`), OR\n * - `timeoutMs` elapsed / `signal` aborted before the task exited.\n *\n * Callers that need to distinguish \"unknown\" from \"still running\"\n * should consult `listBackground` first (the `wait_task` tool does).\n *\n * This is the injectable wait seam for the `wait_task` tool:\n * in-process contexts implement it on the existing `onExit`\n * machinery; durable hosts implement it as an awakeable park\n * (runner fires task-exit over their bridge → resolveAwakeable).\n */\n waitBackground?: (\n handle: ExecutionHandle,\n taskId: string,\n options?: { timeoutMs?: number, signal?: AbortSignal },\n ) => Promise<TaskExitInfo | null>\n\n /**\n * Transfer ownership of every still-running task from `fromHandle`\n * to `toHandle`. Used by the spawn tool to \"promote\" a subagent's\n * background tasks up to the parent's handle so they outlive the\n * subagent's destroy() — matching shell semantics, where a `&`-ed\n * command outlives the parent process.\n *\n * Side effects:\n * - The task's `handleId` is rewritten, so subsequent\n * `listBackground(toHandle)` / `killBackground(toHandle, …)` /\n * `destroy(toHandle)` see it (and `fromHandle`-scoped operations\n * don't).\n * - When `newOnExit` is provided, the original `onExit` is\n * REPLACED with it. Critical: the task's natural exit callback\n * was captured against the spawning agent's hook bus, which is\n * about to be destroyed; without rewiring, the parent never\n * learns when the task terminates. Pass a closure that fires\n * the parent agent's `background:exit` hook.\n *\n * Returns the entries that were actually reassigned (running tasks\n * only; terminated ones stay where they are). Implementations that\n * don't support reassignment can leave this undefined; the caller\n * (spawn.ts) falls back to the current behavior of killing child\n * tasks at subagent shutdown.\n */\n reassignBackgroundTasks?: (\n fromHandle: ExecutionHandle,\n toHandle: ExecutionHandle,\n newOnExit?: (info: TaskExitInfo) => void,\n ) => Promise<readonly TaskEntry[]>\n\n /** Read a file from the context's filesystem */\n readFile: (handle: ExecutionHandle, path: string) => Promise<string>\n\n /**\n * Read a file from the context's filesystem as raw bytes.\n *\n * Used by `read_file` to dispatch image / binary files into the multimodal\n * `ToolResultContent[]` route. Optional — when not implemented, the tool\n * falls back to `base64 < path` via the `exec` seam, which works in any\n * shell-capable context. Implementations that already have a native\n * binary read (in-process `fs.readFile` without encoding, container API,\n * sandbox SDK) should override for the latency win.\n */\n readFileBinary?: (handle: ExecutionHandle, path: string) => Promise<Uint8Array>\n\n /** Write a file to the context's filesystem */\n writeFile: (handle: ExecutionHandle, path: string, content: string) => Promise<void>\n\n /** List files in a directory */\n listFiles: (handle: ExecutionHandle, path: string) => Promise<string[]>\n\n /**\n * Resolve the host-side port that a container port was published on\n * (docker context only, and only when the matching entry was created\n * with `host` omitted in `SpawnConfig.ports`).\n *\n * Resolves to `null` if the container port isn't published. Other\n * contexts (process, sandbox) don't implement this; they don't have\n * a port-mapping concept.\n */\n getMappedPort?: (handle: ExecutionHandle, containerPort: number) => Promise<number | null>\n\n /** Destroy the execution environment and clean up resources */\n destroy: (handle: ExecutionHandle) => Promise<void>\n}\n\n// ---------------------------------------------------------------------------\n// Background task types\n// ---------------------------------------------------------------------------\n\n/**\n * Lifecycle status of a background task.\n *\n * - `'running'` — process is still live; `exitCode` / `signal` unset.\n * - `'exited'` — process terminated on its own (clean or non-zero exit).\n * - `'killed'` — the host issued `killBackground` (SIGTERM to the group).\n *\n * The status is a coarse-grained signal; the exit code carries the\n * fine-grained detail (e.g. `143 = SIGTERM` for `'killed'`).\n */\nexport type BackgroundTaskStatus = 'running' | 'exited' | 'killed'\n\n/**\n * Returned synchronously by `execBackground` — the handle the model and\n * the framework use to refer to the task until it terminates.\n *\n * `outputPath` is an absolute path to the log file the context is\n * appending stdout + stderr to (interleaved by emit order). The model\n * reads it via the normal `read_file` tool; no special tool is required.\n */\nexport interface TaskHandle {\n /**\n * Stable id minted by the context — typically `bash_<n>` for\n * `ProcessContext`. Sequential within a single context instance,\n * resets when a new context is constructed. Forwarded to `killBackground` /\n * `listBackground` and stamped into every `<task-notification>` block.\n */\n taskId: string\n /** OS pid of the spawned shell wrapper (process-group leader on POSIX). */\n pid: number\n /** Absolute path to the log file the context is streaming output into. */\n outputPath: string\n}\n\n/**\n * Fired exactly once per task when the child process terminates, via\n * `execBackground`'s `onExit` callback. The agent layer translates this\n * into a queued `<task-notification>` for the next turn.\n *\n * `signal` is set when the child was terminated by a signal (e.g.\n * SIGTERM from our own kill-tree, SIGKILL from oom-killer); absent on\n * natural exit. `exitCode` is `128 + signal-number` on signal-killed\n * children, matching POSIX shell conventions — `143` for SIGTERM, etc.\n */\nexport interface TaskExitInfo {\n taskId: string\n status: Exclude<BackgroundTaskStatus, 'running'>\n exitCode: number\n signal?: NodeJS.Signals\n outputPath: string\n /** `Date.now()` delta between spawn and exit. */\n durationMs: number\n /** The original command string the model invoked — useful for telemetry / banner summary. */\n command: string\n}\n\n/**\n * One row in `listBackground`'s snapshot. Living entries (status `'running'`)\n * have `exitCode` / `signal` / `endedAt` unset; terminated entries carry the\n * same data `TaskExitInfo` returned at exit time.\n */\nexport interface TaskEntry {\n taskId: string\n pid: number\n command: string\n cwd: string\n startedAt: number\n /**\n * Set when the task terminated. Lets pull-based consumers (the agent's\n * run-boundary reconcile) derive `durationMs = endedAt - startedAt`\n * without depending on when the snapshot was taken.\n */\n endedAt?: number\n outputPath: string\n status: BackgroundTaskStatus\n exitCode?: number\n signal?: NodeJS.Signals\n /** Total bytes written to the output file so far — useful for \"task X has produced N KB\" UX hints. */\n bytesWritten: number\n}\n\n/**\n * Fired by the optional stall watchdog (see `execBackground`'s\n * `stallTimeoutMs`) when a running task has produced no output for the\n * configured window. The process is still alive — this is a signal, not\n * a state transition.\n */\nexport interface TaskStallInfo {\n taskId: string\n command: string\n outputPath: string\n /** Milliseconds since the last output chunk (>= the configured window). */\n stalledForMs: number\n /** Total bytes the task has written so far. */\n bytesWritten: number\n}\n"],"mappings":"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"}
@@ -1,3 +1,3 @@
1
- import { a as ExecResult, c as SpawnConfig, d as TaskHandle, i as ContextType, l as TaskEntry, n as ContextCapabilities, o as ExecutionContext, r as ContextMount, s as ExecutionHandle, t as BackgroundTaskStatus, u as TaskExitInfo } from "./types-CRf_uTpK.js";
2
- import { n as createSandboxContext, r as createProcessContext, t as SandboxProvider } from "./index-B65HjjKZ.js";
3
- export { type BackgroundTaskStatus, type ContextCapabilities, type ContextMount, type ContextType, type ExecResult, type ExecutionContext, type ExecutionHandle, type SandboxProvider, type SpawnConfig, type TaskEntry, type TaskExitInfo, type TaskHandle, createProcessContext, createSandboxContext };
1
+ import { a as DetachedTasksCapability, c as ExecutionHandle, d as TaskExitInfo, f as TaskHandle, i as ContextType, l as SpawnConfig, m as resolveDetachedTasksCapability, n as ContextCapabilities, o as ExecResult, p as TaskStallInfo, r as ContextMount, s as ExecutionContext, t as BackgroundTaskStatus, u as TaskEntry } from "./types-BMwiYRIJ.js";
2
+ import { n as createSandboxContext, r as createProcessContext, t as SandboxProvider } from "./index-4pQUnr2W.js";
3
+ export { type BackgroundTaskStatus, type ContextCapabilities, type ContextMount, type ContextType, type DetachedTasksCapability, type ExecResult, type ExecutionContext, type ExecutionHandle, type SandboxProvider, type SpawnConfig, type TaskEntry, type TaskExitInfo, type TaskHandle, type TaskStallInfo, createProcessContext, createSandboxContext, resolveDetachedTasksCapability };
package/dist/contexts.js CHANGED
@@ -1,2 +1,2 @@
1
- import { n as createProcessContext, t as createSandboxContext } from "./contexts-CbI8dRfI.js";
2
- export { createProcessContext, createSandboxContext };
1
+ import { n as createSandboxContext, r as createProcessContext, t as resolveDetachedTasksCapability } from "./contexts-GKAWYq07.js";
2
+ export { createProcessContext, createSandboxContext, resolveDetachedTasksCapability };
package/dist/eval.d.ts CHANGED
@@ -1,2 +1,2 @@
1
- import { $t as ReusableExecutionContext, An as statusCompleted, At as EvalDefinitionContext, Bt as EvalScorer, Cn as formatTrajectoryLine, Ct as EvalAgentRunResult, Dn as registerEvalTests, Dt as EvalCaseOptions, En as normalizeMetric, Et as EvalArtifacts, Ft as EvalRunReporterOptions, Gt as EvalWorkspaceSnapshot, Ht as EvalTestRunner, It as EvalRunSummary, Jt as MetricEmitter, Kt as LlmJudgeOptions, Lt as EvalRunSummaryCase, Mt as EvalMetricError, Nt as EvalRunMetricAggregate, On as relativeArtifactPath, Ot as EvalCaseResult, Pt as EvalRunReporter, Qt as RegisterEvalTestsOptions, Rt as EvalRunUsage, Sn as formatEvalRunSummary, St as EvalAgentRunOptions, Tn as llmJudge, Tt as EvalAgentStats, Ut as EvalWorkspaceFile, Vt as EvalScorerContext, Wt as EvalWorkspaceOptions, Xt as MetricSpecMap, Yt as MetricSpec, Zt as MetricStats, _n as fileContentQuality, an as buildRegisteredEvals, bn as finalizeEvalMetrics, bt as EvalAgent, cn as computeEvalTagScores, dn as createReusableExecutionContext, en as Trajectory, fn as defineEval, gn as fileContains, hn as emitEfficiencyMetrics, in as buildEvalRunSummary, jt as EvalMetric, kn as runEvalCase, kt as EvalDefinition, ln as createEvalAgent, mn as efficiencyMetricValues, nn as TrajectoryStepKind, on as buildTrajectory, pn as defineMetrics, qt as MetricDirection, rn as artifactPath, sn as clearRegisteredEvals, tn as TrajectoryStep, un as createEvalRunReporter, vn as fileExists, vt as CreateEvalAgentOptions, wn as functionalityMetric, wt as EvalAgentRunStats, xn as formatEvalCaseSummary, xt as EvalAgentMcpServers, yn as fileExistsOneOf, yt as EFFICIENCY_METRICS, zt as EvalScore } from "./index-LX8KCBXU.js";
1
+ import { $t as ReusableExecutionContext, An as statusCompleted, At as EvalDefinitionContext, Bt as EvalScorer, Cn as formatTrajectoryLine, Ct as EvalAgentRunResult, Dn as registerEvalTests, Dt as EvalCaseOptions, En as normalizeMetric, Et as EvalArtifacts, Ft as EvalRunReporterOptions, Gt as EvalWorkspaceSnapshot, Ht as EvalTestRunner, It as EvalRunSummary, Jt as MetricEmitter, Kt as LlmJudgeOptions, Lt as EvalRunSummaryCase, Mt as EvalMetricError, Nt as EvalRunMetricAggregate, On as relativeArtifactPath, Ot as EvalCaseResult, Pt as EvalRunReporter, Qt as RegisterEvalTestsOptions, Rt as EvalRunUsage, Sn as formatEvalRunSummary, St as EvalAgentRunOptions, Tn as llmJudge, Tt as EvalAgentStats, Ut as EvalWorkspaceFile, Vt as EvalScorerContext, Wt as EvalWorkspaceOptions, Xt as MetricSpecMap, Yt as MetricSpec, Zt as MetricStats, _n as fileContentQuality, an as buildRegisteredEvals, bn as finalizeEvalMetrics, bt as EvalAgent, cn as computeEvalTagScores, dn as createReusableExecutionContext, en as Trajectory, fn as defineEval, gn as fileContains, hn as emitEfficiencyMetrics, in as buildEvalRunSummary, jt as EvalMetric, kn as runEvalCase, kt as EvalDefinition, ln as createEvalAgent, mn as efficiencyMetricValues, nn as TrajectoryStepKind, on as buildTrajectory, pn as defineMetrics, qt as MetricDirection, rn as artifactPath, sn as clearRegisteredEvals, tn as TrajectoryStep, un as createEvalRunReporter, vn as fileExists, vt as CreateEvalAgentOptions, wn as functionalityMetric, wt as EvalAgentRunStats, xn as formatEvalCaseSummary, xt as EvalAgentMcpServers, yn as fileExistsOneOf, yt as EFFICIENCY_METRICS, zt as EvalScore } from "./index-C7BvI1Hi.js";
2
2
  export { CreateEvalAgentOptions, EFFICIENCY_METRICS, EvalAgent, EvalAgentMcpServers, EvalAgentRunOptions, EvalAgentRunResult, EvalAgentRunStats, EvalAgentStats, EvalArtifacts, EvalCaseOptions, EvalCaseResult, EvalDefinition, EvalDefinitionContext, EvalMetric, EvalMetricError, EvalRunMetricAggregate, EvalRunReporter, EvalRunReporterOptions, EvalRunSummary, EvalRunSummaryCase, EvalRunUsage, EvalScore, EvalScorer, EvalScorerContext, EvalTestRunner, EvalWorkspaceFile, EvalWorkspaceOptions, EvalWorkspaceSnapshot, LlmJudgeOptions, MetricDirection, MetricEmitter, MetricSpec, MetricSpecMap, MetricStats, RegisterEvalTestsOptions, ReusableExecutionContext, Trajectory, TrajectoryStep, TrajectoryStepKind, artifactPath, buildEvalRunSummary, buildRegisteredEvals, buildTrajectory, clearRegisteredEvals, computeEvalTagScores, createEvalAgent, createEvalRunReporter, createReusableExecutionContext, defineEval, defineMetrics, efficiencyMetricValues, emitEfficiencyMetrics, fileContains, fileContentQuality, fileExists, fileExistsOneOf, finalizeEvalMetrics, formatEvalCaseSummary, formatEvalRunSummary, formatTrajectoryLine, functionalityMetric, llmJudge, normalizeMetric, registerEvalTests, relativeArtifactPath, runEvalCase, statusCompleted };
package/dist/eval.js CHANGED
@@ -1,6 +1,6 @@
1
- import { h as alwaysQuote } from "./tools-ycHDeHBZ.js";
2
- import { n as createProcessContext } from "./contexts-CbI8dRfI.js";
3
- import { a as headlessEventToJsonl, c as runHeadless } from "./headless-D0qfvzG9.js";
1
+ import { _ as alwaysQuote } from "./tools-Bk9TqmCV.js";
2
+ import { r as createProcessContext } from "./contexts-GKAWYq07.js";
3
+ import { a as headlessEventToJsonl, c as runHeadless } from "./headless-Cn6XXmr3.js";
4
4
  import { i as createMemoryStore, t as createSession } from "./session-C0uGIWm_.js";
5
5
  import { join, relative, resolve } from "node:path";
6
6
  import { tmpdir } from "node:os";
@@ -1,9 +1,9 @@
1
- import { d as createAgent } from "./tools-ycHDeHBZ.js";
1
+ import { d as createAgent } from "./tools-Bk9TqmCV.js";
2
2
  import { d as toAnthropic, s as ensureToolResultPairing } from "./messages-DdfOKKx_.js";
3
3
  import { a as toolResultToText, n as documentBlockMarker } from "./types-BiobHM1D.js";
4
- import { n as createProcessContext } from "./contexts-CbI8dRfI.js";
4
+ import { r as createProcessContext } from "./contexts-GKAWYq07.js";
5
5
  import { i as statsByModel } from "./stats-DAKBEKjc.js";
6
- import { i as basic_default } from "./presets-CTNbWXWz.js";
6
+ import { i as basic_default } from "./presets-DCrQmY3b.js";
7
7
  import { i as createMemoryStore, t as createSession } from "./session-C0uGIWm_.js";
8
8
  //#region src/run-summary.ts
9
9
  /**
@@ -630,4 +630,4 @@ function installHeadlessEventAdapter(hooks, onEvent) {
630
630
  //#endregion
631
631
  export { headlessEventToJsonl as a, runHeadless as c, createRunSummaryCollector as d, formattedHeadlessTurnEventToJsonl as i, transcriptToOpenAIMessages as l, formatHeadlessResult as n, installHeadlessEventAdapter as o, formatHeadlessTurnEvent as r, providerTranscriptFormatForProvider as s, exitCodeForHeadlessResult as t, transcriptToProviderMessages as u };
632
632
 
633
- //# sourceMappingURL=headless-D0qfvzG9.js.map
633
+ //# sourceMappingURL=headless-Cn6XXmr3.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"headless-D0qfvzG9.js","names":["basic"],"sources":["../src/run-summary.ts","../src/headless.ts"],"sourcesContent":["/**\n * Run summary collector — one JSON postmortem per `agent.run()`.\n *\n * `AgentStats` is great for billing and per-turn accounting but doesn't\n * carry a record of the *interesting* events that happened during a run\n * (errors, gate blocks, validation rejects, budget hits, MCP failures).\n * Reconstructing that from a hook stream after the fact is exactly the\n * kind of plumbing every consumer ends up re-implementing.\n *\n * This module ships:\n *\n * - {@link RunSummary} — a serializable shape that bundles totals,\n * per-model breakdown, and all the per-run incident lists.\n * - {@link createRunSummaryCollector} — installs hook listeners that\n * build up a {@link RunSummary} during the run and surfaces it on\n * `agent:done`. Calls an optional `onSummary` callback so the host can\n * forward to a log aggregator / DB / postmortem dashboard without\n * touching `agent.run()`'s return value.\n *\n * The collector is purely additive — it doesn't touch the agent loop,\n * doesn't change return shapes, and doesn't suppress any other hook\n * handlers. Drop it in alongside tracing / metrics / logging or use it\n * standalone when you only need the summary.\n */\n\nimport type { Hookable } from 'hookable'\nimport type { AgentHooks } from './agent'\nimport type { AgentStats } from './types'\nimport { statsByModel } from './stats'\n\n// ---------------------------------------------------------------------------\n// Public shape\n// ---------------------------------------------------------------------------\n\nexport interface RunSummaryTokens {\n input: number\n output: number\n cacheRead: number\n cacheCreation: number\n cost?: number\n /** First observable byte from the provider, ms from run start. */\n ttftMs?: number\n}\n\nexport interface RunSummaryByModel {\n modelId: string\n input: number\n output: number\n cacheRead: number\n cacheCreation: number\n cost: number\n turns: number\n}\n\nexport interface RunSummaryError {\n kind: 'stream' | 'tool' | 'mcp-tool' | 'mcp' | 'spawn'\n message: string\n errorType?: string\n turnId?: string\n callId?: string\n server?: string\n toolName?: string\n childId?: string\n statusCode?: number\n requestId?: string\n}\n\nexport interface RunSummaryBlock {\n callId: string\n toolName: string\n outcome: 'gate-block' | 'unknown' | 'invalid-input'\n reason?: string\n}\n\nexport interface RunSummaryValidation {\n callId: string\n toolName: string\n reason: string\n}\n\nexport interface RunSummaryBudget {\n kind: 'bytes' | 'tool-count'\n /** Tool name (for `'tool-count'`); absent for byte budgets. */\n toolName?: string\n /** `mode` for `'tool-count'`; absent for byte budgets. */\n mode?: 'steer' | 'block'\n observed: number\n limit: number\n turnId?: string\n}\n\nexport interface RunSummaryRepeatGuard {\n toolName: string\n /** Consecutive identical calls including the one that tripped the guard. */\n count: number\n threshold: number\n action: 'block' | 'abort'\n turnId?: string\n}\n\n/**\n * Postmortem snapshot of one `agent.run()`. Strictly serializable — every\n * field round-trips through `JSON.stringify` / `JSON.parse` without loss\n * so a log aggregator can ingest it as-is.\n */\nexport interface RunSummary {\n runId?: string\n parentRunId?: string\n depth: number\n agentName?: string\n startedAt: number\n endedAt: number\n durationMs: number\n status: 'completed' | 'aborted'\n turns: number\n totals: RunSummaryTokens\n byModel: RunSummaryByModel[]\n errors: RunSummaryError[]\n blocks: RunSummaryBlock[]\n validationRejects: RunSummaryValidation[]\n budgetEvents: RunSummaryBudget[]\n /**\n * Consecutive-identical repeat-guard escalations. An `action: 'abort'`\n * entry means the guard terminated the run via the agent's\n * `AbortController` (the run finalizes as `'aborted'`).\n */\n repeatGuardEvents: RunSummaryRepeatGuard[]\n /** Counts of pairing repairs, keyed by repair mode. */\n pairingRepairs: Record<string, number>\n /**\n * Postmortem snapshots of child runs that bubbled their stats up via\n * `spawn:complete`. Only present when the run actually spawned.\n */\n children?: RunSummary[]\n}\n\n// ---------------------------------------------------------------------------\n// Collector\n// ---------------------------------------------------------------------------\n\nexport interface RunSummaryCollectorOptions {\n /**\n * Called with the assembled {@link RunSummary} on every `agent:done`.\n * Synchronous — heavy I/O should be deferred (e.g. via `setImmediate`).\n */\n onSummary?: (summary: RunSummary) => void\n}\n\nexport interface RunSummaryCollector {\n /** Install the collector's hook handlers. Returns an uninstall fn. */\n install: (hooks: Hookable<AgentHooks>) => () => void\n /** Most-recent summary; `undefined` until the first `agent:done` fires. */\n latest: () => RunSummary | undefined\n}\n\n/**\n * Build a run-summary collector. State is created fresh inside each\n * `install()` call, so a single collector instance can be installed\n * across multiple agents without attribution cross-talk. `latest()`\n * returns the most-recent summary across **any** install — install\n * per-agent collectors if you need separate post-run snapshots.\n *\n * @example\n * ```ts\n * const collector = createRunSummaryCollector({\n * onSummary: s => console.log(JSON.stringify(s)),\n * })\n * const uninstall = collector.install(agent.hooks)\n * try { await agent.run({ prompt }) }\n * finally { uninstall() }\n * ```\n */\nexport function createRunSummaryCollector(\n options: RunSummaryCollectorOptions = {},\n): RunSummaryCollector {\n let last: RunSummary | undefined\n\n return {\n latest: () => last,\n install(hooks: Hookable<AgentHooks>): () => void {\n // Per-run in-progress accumulators. The hookable bus serializes\n // agent.run lifecycles per agent — we reset between runs on\n // `agent:start` and consume on `agent:done`.\n let runId: string | undefined\n let parentRunId: string | undefined\n let depth = 0\n let agentName: string | undefined\n let startedAt = Date.now()\n let aborted = false\n const errors: RunSummaryError[] = []\n const blocks: RunSummaryBlock[] = []\n const validationRejects: RunSummaryValidation[] = []\n const budgetEvents: RunSummaryBudget[] = []\n const repeatGuardEvents: RunSummaryRepeatGuard[] = []\n const pairingRepairs: Record<string, number> = {}\n const children: RunSummary[] = []\n\n function resetForNewRun(): void {\n aborted = false\n errors.length = 0\n blocks.length = 0\n validationRejects.length = 0\n budgetEvents.length = 0\n repeatGuardEvents.length = 0\n for (const k of Object.keys(pairingRepairs))\n delete pairingRepairs[k]\n children.length = 0\n }\n\n const unregisters: Array<() => void> = []\n\n unregisters.push(hooks.hook('agent:start', (ctx) => {\n resetForNewRun()\n runId = ctx.runId\n parentRunId = ctx.parentRunId\n depth = ctx.depth\n agentName = ctx.agentName\n startedAt = ctx.startedAt\n }))\n\n unregisters.push(hooks.hook('agent:abort', () => {\n aborted = true\n }))\n\n unregisters.push(hooks.hook('stream:error', (ctx) => {\n const msg = ctx.err instanceof Error ? ctx.err.message : String(ctx.err)\n const errorType = ctx.err instanceof Error ? ctx.err.name : 'unknown'\n errors.push({\n kind: 'stream',\n message: msg,\n errorType,\n turnId: ctx.turnId,\n ...(ctx.statusCode !== undefined ? { statusCode: ctx.statusCode } : {}),\n ...(ctx.requestId !== undefined ? { requestId: ctx.requestId } : {}),\n })\n }))\n\n unregisters.push(hooks.hook('tool:error', (ctx) => {\n errors.push({\n kind: 'tool',\n message: ctx.error.message,\n errorType: ctx.error.name,\n turnId: ctx.turnId,\n callId: ctx.callId,\n toolName: ctx.name,\n })\n }))\n\n unregisters.push(hooks.hook('mcp:tool:error', (ctx) => {\n errors.push({\n kind: 'mcp-tool',\n message: ctx.error.message,\n errorType: ctx.error.name,\n turnId: ctx.turnId,\n callId: ctx.callId,\n server: ctx.server,\n toolName: ctx.displayName,\n })\n }))\n\n unregisters.push(hooks.hook('mcp:error', (ctx) => {\n errors.push({\n kind: 'mcp',\n message: ctx.error.message,\n errorType: ctx.error.name,\n server: ctx.name,\n })\n }))\n\n unregisters.push(hooks.hook('spawn:error', (ctx) => {\n errors.push({\n kind: 'spawn',\n message: ctx.error.message,\n errorType: ctx.error.name,\n childId: ctx.id,\n })\n }))\n\n unregisters.push(hooks.hook('tool:dispatched', (ctx) => {\n if (ctx.outcome === 'gate-block' || ctx.outcome === 'unknown' || ctx.outcome === 'invalid-input') {\n blocks.push({\n callId: ctx.callId,\n toolName: ctx.name,\n outcome: ctx.outcome,\n ...(ctx.reason ? { reason: ctx.reason } : {}),\n })\n }\n }))\n\n unregisters.push(hooks.hook('validation:reject', (ctx) => {\n validationRejects.push({\n callId: ctx.callId,\n toolName: ctx.name,\n reason: ctx.reason,\n })\n }))\n\n unregisters.push(hooks.hook('budget:exceeded', (ctx) => {\n budgetEvents.push({\n kind: 'bytes',\n observed: ctx.bytes,\n limit: ctx.budget,\n turnId: ctx.turnId,\n })\n }))\n\n unregisters.push(hooks.hook('tool-budget:exceeded', (ctx) => {\n budgetEvents.push({\n kind: 'tool-count',\n toolName: ctx.tool,\n mode: ctx.mode,\n observed: ctx.count,\n limit: ctx.max,\n turnId: ctx.turnId,\n })\n }))\n\n unregisters.push(hooks.hook('repeat-guard:exceeded', (ctx) => {\n repeatGuardEvents.push({\n toolName: ctx.tool,\n count: ctx.count,\n threshold: ctx.threshold,\n action: ctx.action,\n turnId: ctx.turnId,\n })\n }))\n\n unregisters.push(hooks.hook('pairing:repair', (ctx) => {\n pairingRepairs[ctx.mode] = (pairingRepairs[ctx.mode] ?? 0) + 1\n }))\n\n unregisters.push(hooks.hook('agent:done', (stats) => {\n const endedAt = Date.now()\n\n // Build per-model rollup via the existing `statsByModel` helper\n // so we stay in lockstep with how the rest of the harness\n // attributes cost and tokens by model id.\n const byModel: RunSummaryByModel[] = []\n for (const [modelId, usage] of statsByModel(stats)) {\n byModel.push({\n modelId,\n input: usage.input,\n output: usage.output,\n cacheRead: usage.cacheRead,\n cacheCreation: usage.cacheCreation,\n cost: usage.cost,\n turns: usage.turns,\n })\n }\n\n // Flatten child stats into nested summaries. We don't have the\n // child's full event lists here (those landed on the child's\n // own hooks), so the nested entry is a minimal totals-only\n // record — enough for a flat per-run audit trail; consumers\n // wanting full per-child event lists install a collector on\n // each subagent.\n for (const c of stats.children ?? []) {\n children.push(minimalSummaryFromStats(c.stats, {\n depth: c.depth ?? depth + 1,\n status: c.status === 'aborted' ? 'aborted' : 'completed',\n }))\n }\n\n const summary: RunSummary = {\n ...(runId ? { runId } : {}),\n ...(parentRunId ? { parentRunId } : {}),\n depth,\n ...(agentName ? { agentName } : {}),\n startedAt,\n endedAt,\n durationMs: endedAt - startedAt,\n status: aborted ? 'aborted' : 'completed',\n turns: stats.turns,\n totals: buildTotals(stats),\n byModel,\n errors: errors.slice(),\n blocks: blocks.slice(),\n validationRejects: validationRejects.slice(),\n budgetEvents: budgetEvents.slice(),\n repeatGuardEvents: repeatGuardEvents.slice(),\n pairingRepairs: { ...pairingRepairs },\n ...(children.length > 0 ? { children: children.slice() } : {}),\n }\n\n last = summary\n try {\n options.onSummary?.(summary)\n }\n catch {\n // Sink errors are not the collector's concern.\n }\n }))\n\n let disposed = false\n return function uninstall() {\n if (disposed)\n return\n disposed = true\n for (const un of unregisters) {\n try {\n un()\n }\n catch { /* ignore */ }\n }\n }\n },\n }\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nfunction buildTotals(stats: AgentStats): RunSummaryTokens {\n return {\n input: stats.totalIn,\n output: stats.totalOut,\n cacheRead: stats.totalCacheRead,\n cacheCreation: stats.totalCacheCreation,\n ...(typeof stats.cost === 'number' ? { cost: stats.cost } : {}),\n ...(typeof stats.timeTillFirstTokenMs === 'number' ? { ttftMs: stats.timeTillFirstTokenMs } : {}),\n }\n}\n\nfunction minimalSummaryFromStats(\n stats: AgentStats,\n meta: { depth: number, status: 'completed' | 'aborted' },\n): RunSummary {\n const byModel: RunSummaryByModel[] = []\n for (const [modelId, usage] of statsByModel(stats)) {\n byModel.push({\n modelId,\n input: usage.input,\n output: usage.output,\n cacheRead: usage.cacheRead,\n cacheCreation: usage.cacheCreation,\n cost: usage.cost,\n turns: usage.turns,\n })\n }\n const children: RunSummary[] = []\n for (const c of stats.children ?? []) {\n children.push(minimalSummaryFromStats(c.stats, {\n depth: c.depth ?? meta.depth + 1,\n status: c.status === 'aborted' ? 'aborted' : 'completed',\n }))\n }\n return {\n depth: meta.depth,\n startedAt: 0,\n endedAt: 0,\n durationMs: stats.elapsed,\n status: meta.status,\n turns: stats.turns,\n totals: buildTotals(stats),\n byModel,\n errors: [],\n blocks: [],\n validationRejects: [],\n budgetEvents: [],\n repeatGuardEvents: [],\n pairingRepairs: {},\n ...(children.length > 0 ? { children } : {}),\n }\n}\n","/**\n * Headless mode — a non-interactive, fully-structured way to drive the agent\n * for automation and RL rollouts.\n *\n * `agent.run()` already returns rich {@link AgentStats}, the session captures a\n * lossless transcript (`session.turns`), and the hook bus exposes every\n * streaming/tool/turn event. What this module adds is the *contract* an RL loop\n * needs: one call (`runHeadless`) that runs a prompt to completion in a chosen\n * workdir, bounded by turns + wall-clock, and hands back a single serializable\n * {@link HeadlessResult} — final answer, verifiable structured output, usage,\n * and the complete trajectory — plus an optional live {@link HeadlessEvent}\n * stream (the in-process equivalent of `--output-format stream-json`).\n *\n * It is provider-agnostic: the caller passes a built {@link Provider} (e.g.\n * `local()` pointed at a vLLM server, or any of the named providers). The thin\n * CLI in `src/headless-cli.ts` wraps this with arg parsing, file/stdin I/O, and\n * JSONL transcript writing.\n */\n\nimport type { Hookable } from 'hookable'\nimport type { AgentHooks, AgentOptions } from './agent'\nimport type { ExecutionContext } from './contexts'\nimport type { Provider } from './providers'\nimport type { RunSummary } from './run-summary'\nimport type { Session, SessionStore } from './session'\nimport type { PairingRepair } from './session/messages'\nimport type { ToolDef } from './tools'\nimport type {\n AgentStats,\n McpServerConfig,\n PromptPart,\n SessionContentBlock,\n SessionTurn,\n ThinkingLevel,\n ToolResultContent,\n ToolResultDocumentContent,\n TurnFinishReason,\n} from './types'\nimport { createAgent } from './agent'\nimport { createProcessContext } from './contexts'\nimport { basic } from './presets'\nimport { createRunSummaryCollector } from './run-summary'\nimport { createMemoryStore, createSession, toAnthropic } from './session'\nimport { ensureToolResultPairing } from './session/messages'\nimport { documentBlockMarker, toolResultToText } from './types'\n\n// ---------------------------------------------------------------------------\n// Public shapes\n// ---------------------------------------------------------------------------\n\nexport type HeadlessStatus = 'completed' | 'aborted' | 'error' | 'timeout'\n\nexport interface HeadlessUsage {\n input: number\n output: number\n cacheRead: number\n cacheCreation: number\n /** Cumulative USD cost when the provider/registry could price the run. */\n cost?: number\n}\n\nexport interface HeadlessErrorInfo {\n message: string\n /** Typed-error class name (`AgentAbortedError`, `AgentContextExceededError`, …). */\n type: string\n}\n\n/**\n * Strictly JSON-serializable postmortem of one headless run. Everything an RL\n * reward function needs: the final answer (`finalText`), the verifiable\n * structured output (`output`, present iff a `schema` was set), usage/turns,\n * and the lossless `transcript` (the SFT training data).\n */\nexport interface HeadlessResult {\n status: HeadlessStatus\n /** Concatenated text of the last assistant turn that produced any text. */\n finalText: string\n /** Schema-enforced structured output (only when `opts.schema` is set). */\n output?: Record<string, unknown>\n usage: HeadlessUsage\n turns: number\n durationMs: number\n /** Total `tool_call` blocks across the whole transcript. */\n numToolCalls: number\n /** Finish reason of the final turn that reported one. */\n finishReason?: TurnFinishReason\n error?: HeadlessErrorInfo\n sessionId: string\n /** Incident postmortem (errors, gate blocks, budget events) via run-summary. */\n summary?: RunSummary\n /** Lossless transcript — raw `session.turns`. Thinking stripped when `includeThinking: false`. */\n transcript: SessionTurn[]\n}\n\nexport type HeadlessOutputFormat = 'zidane' | 'provider'\n\nexport type ProviderTranscriptFormat = 'anthropic' | 'openai'\n\nexport type FormattedHeadlessResult\n = | HeadlessResult\n | unknown[]\n\nexport type FormattedHeadlessTurnEvent\n = | Extract<HeadlessEvent, { type: 'turn' }>\n | unknown[]\n\nexport function exitCodeForHeadlessResult(result: HeadlessResult): number {\n switch (result.status) {\n case 'completed': return 0\n case 'timeout': return 124\n case 'aborted': return 130\n case 'error':\n default: return 1\n }\n}\n\n/**\n * Live event union — the in-process equivalent of a `stream-json` line. Every\n * member is JSON-serializable; render to JSONL with {@link headlessEventToJsonl}.\n */\nexport type HeadlessEvent\n = | { type: 'start', runId: string, provider?: string }\n | { type: 'text', delta: string }\n | { type: 'thinking', delta: string }\n | { type: 'tool_call', callId: string, name: string, input: Record<string, unknown> }\n | { type: 'tool_result', callId: string, name: string, output: string, isError: boolean }\n | { type: 'turn', index: number, turn: SessionTurn }\n | { type: 'spawn', event: 'before' | 'complete' | 'error', id: string, info?: Record<string, unknown> }\n | { type: 'error', message: string, errorType?: string }\n | { type: 'result', result: HeadlessResult }\n\n/** Serialize one event as a newline-terminated JSON line (stream-json). */\nexport function headlessEventToJsonl(event: HeadlessEvent): string {\n return `${JSON.stringify(event)}\\n`\n}\n\nexport function formattedHeadlessTurnEventToJsonl(event: FormattedHeadlessTurnEvent): string {\n return `${JSON.stringify(event)}\\n`\n}\n\nexport function providerTranscriptFormatForProvider(providerName: string): ProviderTranscriptFormat {\n return providerName === 'anthropic' ? 'anthropic' : 'openai'\n}\n\nexport function transcriptToProviderMessages(\n turns: SessionTurn[],\n providerName: string,\n): unknown[] {\n const format = providerTranscriptFormatForProvider(providerName)\n if (format === 'anthropic') {\n return turns\n .filter((turn): turn is SessionTurn & { role: 'user' | 'assistant' } => turn.role === 'user' || turn.role === 'assistant')\n .map(turn => toAnthropic({ role: turn.role, content: turn.content }))\n }\n return transcriptToOpenAIMessages(turns, { strictToolPairing: false })\n}\n\nexport function formatHeadlessResult(\n result: HeadlessResult,\n options: { format: HeadlessOutputFormat, providerName: string },\n): FormattedHeadlessResult {\n if (options.format === 'zidane')\n return result\n return transcriptToProviderMessages(result.transcript, options.providerName)\n}\n\nexport function formatHeadlessTurnEvent(\n event: Extract<HeadlessEvent, { type: 'turn' }>,\n options: { format: HeadlessOutputFormat, providerName: string },\n): FormattedHeadlessTurnEvent {\n if (options.format === 'zidane')\n return event\n return transcriptToProviderMessages([event.turn], options.providerName)\n}\n\nexport interface HeadlessOptions {\n /** User prompt — plain string or multimodal `PromptPart[]`. */\n prompt: string | PromptPart[]\n /** Built provider (e.g. `local()`, `openaiCompat(...)`, `anthropic(...)`). */\n provider: Provider\n model?: string\n /** Override the preset system prompt for this run. */\n system?: string\n thinking?: ThinkingLevel\n maxTurns?: number\n maxTokens?: number\n /** Wall-clock cap; on expiry the run is aborted and `status` becomes `'timeout'`. */\n timeoutMs?: number\n /** External abort signal — chained with the internal timeout controller. */\n signal?: AbortSignal\n /** JSON Schema for structured-output enforcement → populates `result.output`. */\n schema?: Record<string, unknown>\n /** Agent behavior defaults for this headless run. Top-level shortcuts below override matching fields. */\n behavior?: AgentOptions['behavior']\n /** Tool overrides. Omit to use the basic preset's tools. */\n tools?: Record<string, ToolDef>\n mcpServers?: McpServerConfig[]\n skills?: AgentOptions['skills']\n /** Execution context. Defaults to a process context rooted at `cwd`. */\n execution?: ExecutionContext\n /** Working directory for the default process context (ignored if `execution` is set). */\n cwd?: string\n /** Reuse / resume an existing session. */\n session?: Session\n /** Session store for a fresh session (defaults to an in-memory store). */\n store?: SessionStore\n /** Keep `thinking` blocks in `result.transcript` (default true). */\n includeThinking?: boolean\n /** Live event callback — the in-process stream-json equivalent. */\n onEvent?: (event: HeadlessEvent) => void\n}\n\n// ---------------------------------------------------------------------------\n// runHeadless\n// ---------------------------------------------------------------------------\n\n/**\n * Run a prompt to completion, headless, and return a single serializable\n * {@link HeadlessResult}. Safe to call concurrently for parallel rollouts —\n * each call builds its own agent + session and tears them down in `finally`.\n */\nexport async function runHeadless(opts: HeadlessOptions): Promise<HeadlessResult> {\n const startedAt = Date.now()\n const execution = opts.execution ?? createProcessContext({ cwd: opts.cwd })\n const session = opts.session ?? await createSession({ store: opts.store ?? createMemoryStore() })\n\n const behavior: NonNullable<AgentOptions['behavior']> = { ...(opts.behavior ?? {}) }\n if (opts.maxTurns !== undefined)\n behavior.maxTurns = opts.maxTurns\n if (opts.maxTokens !== undefined)\n behavior.maxTokens = opts.maxTokens\n if (opts.schema !== undefined)\n behavior.schema = opts.schema\n\n const agent = createAgent({\n ...basic,\n provider: opts.provider,\n execution,\n session,\n behavior,\n ...(opts.tools !== undefined ? { tools: opts.tools } : {}),\n ...(opts.mcpServers !== undefined ? { mcpServers: opts.mcpServers } : {}),\n ...(opts.skills !== undefined ? { skills: opts.skills } : {}),\n })\n\n const collector = createRunSummaryCollector()\n const uninstallSummary = collector.install(agent.hooks)\n const uninstallEvents = opts.onEvent ? installHeadlessEventAdapter(agent.hooks, opts.onEvent) : noop\n\n // Resumed sessions carry turns from prior runs — remember where this run\n // starts so transcript/finalText/numToolCalls stay per-run, matching the\n // per-run usage/turns/durationMs stats.\n const runStartIndex = session.turns.length\n\n // Internal controller drives the wall-clock timeout and chains any external\n // signal so a host cancel and a timeout both abort the same run. The\n // listener is removed in `finally` — a long-lived host signal must not\n // accumulate one listener per rollout.\n const controller = new AbortController()\n const onExternalAbort = (): void => controller.abort(opts.signal!.reason)\n if (opts.signal) {\n if (opts.signal.aborted)\n controller.abort(opts.signal.reason)\n else\n opts.signal.addEventListener('abort', onExternalAbort, { once: true })\n }\n let timedOut = false\n const timer = opts.timeoutMs && opts.timeoutMs > 0\n ? setTimeout(() => {\n timedOut = true\n controller.abort(new Error(`Headless run timed out after ${opts.timeoutMs}ms`))\n }, opts.timeoutMs)\n : undefined\n\n let stats: AgentStats | undefined\n let error: HeadlessErrorInfo | undefined\n\n try {\n stats = await agent.run({\n prompt: opts.prompt,\n signal: controller.signal,\n ...(opts.model !== undefined ? { model: opts.model } : {}),\n ...(opts.system !== undefined ? { system: opts.system } : {}),\n ...(opts.thinking !== undefined ? { thinking: opts.thinking } : {}),\n })\n }\n catch (err) {\n // The agent loop throws typed errors (AgentAbortedError,\n // AgentContextExceededError, AgentProviderError, …) — their `name` already\n // classifies the failure, so surface it directly.\n const e = err instanceof Error ? err : new Error(String(err))\n error = { message: e.message, type: e.name }\n }\n finally {\n if (timer)\n clearTimeout(timer)\n opts.signal?.removeEventListener('abort', onExternalAbort)\n uninstallEvents()\n uninstallSummary()\n await agent.destroy()\n }\n\n // Status: a thrown error wins, but an abort/timeout can also surface as a\n // clean resolve (the loop returns partial stats), so check the signal too.\n let status: HeadlessStatus\n if (error)\n status = timedOut ? 'timeout' : controller.signal.aborted ? 'aborted' : 'error'\n else if (controller.signal.aborted)\n status = timedOut ? 'timeout' : 'aborted'\n else\n status = 'completed'\n\n const rawTranscript = session.turns.slice(runStartIndex)\n const transcript = opts.includeThinking === false ? stripThinking(rawTranscript) : rawTranscript\n const summary = collector.latest()\n\n const result: HeadlessResult = {\n status,\n finalText: extractFinalText(rawTranscript),\n ...(stats?.output ? { output: stats.output } : {}),\n usage: {\n input: stats?.totalIn ?? 0,\n output: stats?.totalOut ?? 0,\n cacheRead: stats?.totalCacheRead ?? 0,\n cacheCreation: stats?.totalCacheCreation ?? 0,\n ...(typeof stats?.cost === 'number' ? { cost: stats.cost } : {}),\n },\n turns: stats?.turns ?? 0,\n durationMs: stats?.elapsed ?? (Date.now() - startedAt),\n numToolCalls: countToolCalls(rawTranscript),\n // Suppress `finishReason` on abort/timeout: the last persisted turn can\n // carry `finishReason: 'stop'` (e.g. the model finished a turn, a follow-up\n // continued, then the run was cancelled), which would make a consumer\n // keyed on `finishReason === 'stop'` misread a cancel as \"model finished\".\n // `status` is authoritative for those exits; let it speak alone. The\n // `'pause'` exhaustion exit keeps `status: 'completed'` so it still\n // surfaces its `finishReason: 'pause'` here.\n ...(status !== 'aborted' && status !== 'timeout' && lastFinishReason(stats)\n ? { finishReason: lastFinishReason(stats) }\n : {}),\n ...(error ? { error } : {}),\n sessionId: session.id,\n ...(summary ? { summary } : {}),\n transcript,\n }\n\n opts.onEvent?.({ type: 'result', result })\n return result\n}\n\n// ---------------------------------------------------------------------------\n// Transcript → OpenAI chat messages (SFT-ready)\n// ---------------------------------------------------------------------------\n\nexport interface OpenAIChatMessage {\n role: 'system' | 'user' | 'assistant' | 'tool'\n content: string | null | OpenAIChatContentPart[]\n tool_calls?: Array<{ id: string, type: 'function', function: { name: string, arguments: string } }>\n tool_call_id?: string\n}\n\nexport type OpenAIChatContentPart\n = | { type: 'text', text: string }\n | { type: 'image_url', image_url: { url: string } }\n\n/**\n * Convert raw `session.turns` into standard OpenAI chat-completion messages:\n * assistant turns carry `tool_calls`, and each `tool_result` becomes its own\n * `role: 'tool'` message. This is the drop-in shape for an SFT renderer —\n * unlike `toOpenAI` (session/messages.ts), which emits an internal `_tag`\n * envelope meant for re-sending to a provider, not for training data.\n *\n * Fails closed on corrupt raw turns instead of fabricating tool results; silent\n * placeholders would create structurally-valid but semantically-poisoned SFT\n * examples.\n */\nexport function transcriptToOpenAIMessages(\n turns: SessionTurn[],\n options: { strictToolPairing?: boolean } = {},\n): OpenAIChatMessage[] {\n const { strictToolPairing = true } = options\n const messages: OpenAIChatMessage[] = []\n let chunk: Array<{ role: 'user' | 'assistant', content: SessionContentBlock[] }> = []\n const flushChunk = () => {\n if (chunk.length === 0)\n return\n let paired = chunk\n if (strictToolPairing) {\n const repairs: PairingRepair[] = []\n paired = ensureToolResultPairing(chunk, { onRepair: repair => repairs.push(repair) })\n if (repairs.length > 0) {\n throw new Error(\n `Cannot convert transcript to OpenAI messages: tool pairing repair required (${repairs.map(r => r.mode).join(', ')})`,\n )\n }\n }\n for (const msg of paired)\n pushOpenAIChatMessage(messages, msg)\n chunk = []\n }\n\n for (const turn of turns) {\n if (turn.role === 'system') {\n flushChunk()\n messages.push({ role: 'system', content: textOf(turn.content) })\n continue\n }\n chunk.push({ role: turn.role, content: turn.content })\n }\n flushChunk()\n\n return messages\n}\n\nfunction pushOpenAIChatMessage(messages: OpenAIChatMessage[], msg: { role: 'user' | 'assistant', content: SessionContentBlock[] }): void {\n const text = textOf(msg.content)\n\n if (msg.role === 'assistant') {\n const toolCalls = msg.content.filter((b): b is ToolCallBlock => b.type === 'tool_call')\n const out: OpenAIChatMessage = { role: 'assistant', content: text || null }\n if (toolCalls.length > 0) {\n out.tool_calls = toolCalls.map(b => ({\n id: b.id,\n type: 'function' as const,\n function: { name: b.name, arguments: JSON.stringify(b.input) },\n }))\n }\n messages.push(out)\n return\n }\n\n // user turn — may carry prompt text/images and/or tool_result blocks\n const userParts = contentPartsFromBlocks(msg.content)\n if (userParts.length > 0)\n messages.push({ role: 'user', content: simplifyOpenAIContent(userParts) })\n\n for (const tr of msg.content.filter((b): b is ToolResultBlock => b.type === 'tool_result')) {\n const { text, images } = summarizeToolResultForOpenAI(tr.output)\n if (images.length === 0) {\n messages.push({ role: 'tool', tool_call_id: tr.callId, content: text })\n continue\n }\n\n const noun = images.length === 1 ? 'image' : 'images'\n const marker = `[${images.length} ${noun} attached — see next user message]`\n messages.push({\n role: 'tool',\n tool_call_id: tr.callId,\n content: text.length > 0 ? `${text}\\n\\n${marker}` : marker,\n })\n messages.push({\n role: 'user',\n content: [\n ...images.map(toOpenAIImagePart),\n { type: 'text', text: `(${noun} returned by tool call ${tr.callId})` },\n ],\n })\n }\n}\n\n// ---------------------------------------------------------------------------\n// Internals\n// ---------------------------------------------------------------------------\n\ntype TextBlock = Extract<SessionContentBlock, { type: 'text' }>\ntype ToolCallBlock = Extract<SessionContentBlock, { type: 'tool_call' }>\ntype ToolResultBlock = Extract<SessionContentBlock, { type: 'tool_result' }>\n\nfunction noop(): void {}\n\nfunction textOf(content: SessionContentBlock[]): string {\n return content.filter((b): b is TextBlock => b.type === 'text').map(b => b.text).join('')\n}\n\nfunction contentPartsFromBlocks(content: SessionContentBlock[]): OpenAIChatContentPart[] {\n const parts: OpenAIChatContentPart[] = []\n for (const block of content) {\n if (block.type === 'image')\n parts.push(toOpenAIImagePart(block))\n else if (block.type === 'document')\n parts.push({ type: 'text', text: documentMarker(block) })\n else if (block.type === 'text' && block.text.length > 0)\n parts.push({ type: 'text', text: block.text })\n }\n return parts\n}\n\nfunction simplifyOpenAIContent(parts: OpenAIChatContentPart[]): string | OpenAIChatContentPart[] {\n if (parts.length === 1 && parts[0].type === 'text')\n return parts[0].text\n return parts\n}\n\nfunction toOpenAIImagePart(image: { mediaType: string, data: string }): OpenAIChatContentPart {\n return {\n type: 'image_url',\n image_url: { url: `data:${image.mediaType};base64,${image.data}` },\n }\n}\n\nfunction summarizeToolResultForOpenAI(output: string | ToolResultContent[]): {\n text: string\n images: Array<{ mediaType: string, data: string }>\n} {\n if (typeof output === 'string')\n return { text: output, images: [] }\n\n const texts: string[] = []\n const images: Array<{ mediaType: string, data: string }> = []\n for (const block of output) {\n if (block.type === 'text') {\n texts.push(block.text)\n }\n else if (block.type === 'image') {\n images.push({ mediaType: block.mediaType, data: block.data })\n }\n else {\n texts.push(documentMarker(block))\n }\n }\n return { text: texts.join('\\n'), images }\n}\n\nfunction documentMarker(doc: ToolResultDocumentContent): string {\n return documentBlockMarker(doc, 'document omitted')\n}\n\nfunction extractFinalText(turns: SessionTurn[]): string {\n for (let i = turns.length - 1; i >= 0; i--) {\n const turn = turns[i]\n if (turn.role !== 'assistant')\n continue\n const text = textOf(turn.content)\n if (text)\n return text\n }\n return ''\n}\n\nfunction countToolCalls(turns: SessionTurn[]): number {\n let n = 0\n for (const turn of turns) {\n for (const block of turn.content) {\n if (block.type === 'tool_call')\n n++\n }\n }\n return n\n}\n\nfunction lastFinishReason(stats?: AgentStats): TurnFinishReason | undefined {\n const usages = stats?.turnUsage\n if (!usages)\n return undefined\n for (let i = usages.length - 1; i >= 0; i--) {\n if (usages[i].finishReason)\n return usages[i].finishReason\n }\n return undefined\n}\n\nfunction stripThinking(turns: SessionTurn[]): SessionTurn[] {\n return turns.map(turn => ({\n ...turn,\n content: turn.content.filter(b => b.type !== 'thinking' && b.type !== 'redacted_thinking'),\n }))\n}\n\nexport function installHeadlessEventAdapter(\n hooks: Hookable<AgentHooks>,\n onEvent: (event: HeadlessEvent) => void,\n): () => void {\n const unregs: Array<() => void> = []\n const emittedTurnIds = new Set<string>()\n const emit = (event: HeadlessEvent): void => {\n try {\n onEvent(event)\n }\n catch {\n // A consumer's sink throwing is not the adapter's concern.\n }\n }\n\n unregs.push(hooks.hook('agent:start', ctx => emit({\n type: 'start',\n runId: ctx.runId,\n ...(ctx.providerName ? { provider: ctx.providerName } : {}),\n })))\n unregs.push(hooks.hook('stream:text', ctx => emit({ type: 'text', delta: ctx.delta })))\n unregs.push(hooks.hook('stream:thinking', ctx => emit({ type: 'thinking', delta: ctx.delta })))\n unregs.push(hooks.hook('tool:before', ctx => emit({\n type: 'tool_call',\n callId: ctx.callId,\n name: ctx.name,\n input: ctx.input,\n })))\n unregs.push(hooks.hook('tool:after', ctx => emit({\n type: 'tool_result',\n callId: ctx.callId,\n name: ctx.name,\n output: toolResultToText(ctx.result),\n isError: false,\n })))\n unregs.push(hooks.hook('tool:error', ctx => emit({\n type: 'tool_result',\n callId: ctx.callId,\n name: ctx.name,\n output: ctx.error.message,\n isError: true,\n })))\n unregs.push(hooks.hook('session:turns', (ctx) => {\n const startIndex = ctx.count - ctx.turns.length\n ctx.turns.forEach((turn, i) => {\n if (emittedTurnIds.has(turn.id))\n return\n emittedTurnIds.add(turn.id)\n emit({ type: 'turn', index: startIndex + i, turn })\n })\n }))\n unregs.push(hooks.hook('spawn:before', ctx => emit({ type: 'spawn', event: 'before', id: ctx.id })))\n unregs.push(hooks.hook('spawn:complete', ctx => emit({\n type: 'spawn',\n event: 'complete',\n id: ctx.id,\n info: { status: ctx.status ?? 'completed' },\n })))\n unregs.push(hooks.hook('spawn:error', ctx => emit({\n type: 'spawn',\n event: 'error',\n id: ctx.id,\n info: { message: ctx.error.message },\n })))\n unregs.push(hooks.hook('stream:error', ctx => emit({\n type: 'error',\n message: ctx.err instanceof Error ? ctx.err.message : String(ctx.err),\n ...(ctx.err instanceof Error ? { errorType: ctx.err.name } : {}),\n })))\n\n return () => {\n for (const un of unregs) {\n try {\n un()\n }\n catch {\n // ignore\n }\n }\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"headless-Cn6XXmr3.js","names":["basic"],"sources":["../src/run-summary.ts","../src/headless.ts"],"sourcesContent":["/**\n * Run summary collector — one JSON postmortem per `agent.run()`.\n *\n * `AgentStats` is great for billing and per-turn accounting but doesn't\n * carry a record of the *interesting* events that happened during a run\n * (errors, gate blocks, validation rejects, budget hits, MCP failures).\n * Reconstructing that from a hook stream after the fact is exactly the\n * kind of plumbing every consumer ends up re-implementing.\n *\n * This module ships:\n *\n * - {@link RunSummary} — a serializable shape that bundles totals,\n * per-model breakdown, and all the per-run incident lists.\n * - {@link createRunSummaryCollector} — installs hook listeners that\n * build up a {@link RunSummary} during the run and surfaces it on\n * `agent:done`. Calls an optional `onSummary` callback so the host can\n * forward to a log aggregator / DB / postmortem dashboard without\n * touching `agent.run()`'s return value.\n *\n * The collector is purely additive — it doesn't touch the agent loop,\n * doesn't change return shapes, and doesn't suppress any other hook\n * handlers. Drop it in alongside tracing / metrics / logging or use it\n * standalone when you only need the summary.\n */\n\nimport type { Hookable } from 'hookable'\nimport type { AgentHooks } from './agent'\nimport type { AgentStats } from './types'\nimport { statsByModel } from './stats'\n\n// ---------------------------------------------------------------------------\n// Public shape\n// ---------------------------------------------------------------------------\n\nexport interface RunSummaryTokens {\n input: number\n output: number\n cacheRead: number\n cacheCreation: number\n cost?: number\n /** First observable byte from the provider, ms from run start. */\n ttftMs?: number\n}\n\nexport interface RunSummaryByModel {\n modelId: string\n input: number\n output: number\n cacheRead: number\n cacheCreation: number\n cost: number\n turns: number\n}\n\nexport interface RunSummaryError {\n kind: 'stream' | 'tool' | 'mcp-tool' | 'mcp' | 'spawn'\n message: string\n errorType?: string\n turnId?: string\n callId?: string\n server?: string\n toolName?: string\n childId?: string\n statusCode?: number\n requestId?: string\n}\n\nexport interface RunSummaryBlock {\n callId: string\n toolName: string\n outcome: 'gate-block' | 'unknown' | 'invalid-input'\n reason?: string\n}\n\nexport interface RunSummaryValidation {\n callId: string\n toolName: string\n reason: string\n}\n\nexport interface RunSummaryBudget {\n kind: 'bytes' | 'tool-count'\n /** Tool name (for `'tool-count'`); absent for byte budgets. */\n toolName?: string\n /** `mode` for `'tool-count'`; absent for byte budgets. */\n mode?: 'steer' | 'block'\n observed: number\n limit: number\n turnId?: string\n}\n\nexport interface RunSummaryRepeatGuard {\n toolName: string\n /** Consecutive identical calls including the one that tripped the guard. */\n count: number\n threshold: number\n action: 'block' | 'abort'\n turnId?: string\n}\n\n/**\n * Postmortem snapshot of one `agent.run()`. Strictly serializable — every\n * field round-trips through `JSON.stringify` / `JSON.parse` without loss\n * so a log aggregator can ingest it as-is.\n */\nexport interface RunSummary {\n runId?: string\n parentRunId?: string\n depth: number\n agentName?: string\n startedAt: number\n endedAt: number\n durationMs: number\n status: 'completed' | 'aborted'\n turns: number\n totals: RunSummaryTokens\n byModel: RunSummaryByModel[]\n errors: RunSummaryError[]\n blocks: RunSummaryBlock[]\n validationRejects: RunSummaryValidation[]\n budgetEvents: RunSummaryBudget[]\n /**\n * Consecutive-identical repeat-guard escalations. An `action: 'abort'`\n * entry means the guard terminated the run via the agent's\n * `AbortController` (the run finalizes as `'aborted'`).\n */\n repeatGuardEvents: RunSummaryRepeatGuard[]\n /** Counts of pairing repairs, keyed by repair mode. */\n pairingRepairs: Record<string, number>\n /**\n * Postmortem snapshots of child runs that bubbled their stats up via\n * `spawn:complete`. Only present when the run actually spawned.\n */\n children?: RunSummary[]\n}\n\n// ---------------------------------------------------------------------------\n// Collector\n// ---------------------------------------------------------------------------\n\nexport interface RunSummaryCollectorOptions {\n /**\n * Called with the assembled {@link RunSummary} on every `agent:done`.\n * Synchronous — heavy I/O should be deferred (e.g. via `setImmediate`).\n */\n onSummary?: (summary: RunSummary) => void\n}\n\nexport interface RunSummaryCollector {\n /** Install the collector's hook handlers. Returns an uninstall fn. */\n install: (hooks: Hookable<AgentHooks>) => () => void\n /** Most-recent summary; `undefined` until the first `agent:done` fires. */\n latest: () => RunSummary | undefined\n}\n\n/**\n * Build a run-summary collector. State is created fresh inside each\n * `install()` call, so a single collector instance can be installed\n * across multiple agents without attribution cross-talk. `latest()`\n * returns the most-recent summary across **any** install — install\n * per-agent collectors if you need separate post-run snapshots.\n *\n * @example\n * ```ts\n * const collector = createRunSummaryCollector({\n * onSummary: s => console.log(JSON.stringify(s)),\n * })\n * const uninstall = collector.install(agent.hooks)\n * try { await agent.run({ prompt }) }\n * finally { uninstall() }\n * ```\n */\nexport function createRunSummaryCollector(\n options: RunSummaryCollectorOptions = {},\n): RunSummaryCollector {\n let last: RunSummary | undefined\n\n return {\n latest: () => last,\n install(hooks: Hookable<AgentHooks>): () => void {\n // Per-run in-progress accumulators. The hookable bus serializes\n // agent.run lifecycles per agent — we reset between runs on\n // `agent:start` and consume on `agent:done`.\n let runId: string | undefined\n let parentRunId: string | undefined\n let depth = 0\n let agentName: string | undefined\n let startedAt = Date.now()\n let aborted = false\n const errors: RunSummaryError[] = []\n const blocks: RunSummaryBlock[] = []\n const validationRejects: RunSummaryValidation[] = []\n const budgetEvents: RunSummaryBudget[] = []\n const repeatGuardEvents: RunSummaryRepeatGuard[] = []\n const pairingRepairs: Record<string, number> = {}\n const children: RunSummary[] = []\n\n function resetForNewRun(): void {\n aborted = false\n errors.length = 0\n blocks.length = 0\n validationRejects.length = 0\n budgetEvents.length = 0\n repeatGuardEvents.length = 0\n for (const k of Object.keys(pairingRepairs))\n delete pairingRepairs[k]\n children.length = 0\n }\n\n const unregisters: Array<() => void> = []\n\n unregisters.push(hooks.hook('agent:start', (ctx) => {\n resetForNewRun()\n runId = ctx.runId\n parentRunId = ctx.parentRunId\n depth = ctx.depth\n agentName = ctx.agentName\n startedAt = ctx.startedAt\n }))\n\n unregisters.push(hooks.hook('agent:abort', () => {\n aborted = true\n }))\n\n unregisters.push(hooks.hook('stream:error', (ctx) => {\n const msg = ctx.err instanceof Error ? ctx.err.message : String(ctx.err)\n const errorType = ctx.err instanceof Error ? ctx.err.name : 'unknown'\n errors.push({\n kind: 'stream',\n message: msg,\n errorType,\n turnId: ctx.turnId,\n ...(ctx.statusCode !== undefined ? { statusCode: ctx.statusCode } : {}),\n ...(ctx.requestId !== undefined ? { requestId: ctx.requestId } : {}),\n })\n }))\n\n unregisters.push(hooks.hook('tool:error', (ctx) => {\n errors.push({\n kind: 'tool',\n message: ctx.error.message,\n errorType: ctx.error.name,\n turnId: ctx.turnId,\n callId: ctx.callId,\n toolName: ctx.name,\n })\n }))\n\n unregisters.push(hooks.hook('mcp:tool:error', (ctx) => {\n errors.push({\n kind: 'mcp-tool',\n message: ctx.error.message,\n errorType: ctx.error.name,\n turnId: ctx.turnId,\n callId: ctx.callId,\n server: ctx.server,\n toolName: ctx.displayName,\n })\n }))\n\n unregisters.push(hooks.hook('mcp:error', (ctx) => {\n errors.push({\n kind: 'mcp',\n message: ctx.error.message,\n errorType: ctx.error.name,\n server: ctx.name,\n })\n }))\n\n unregisters.push(hooks.hook('spawn:error', (ctx) => {\n errors.push({\n kind: 'spawn',\n message: ctx.error.message,\n errorType: ctx.error.name,\n childId: ctx.id,\n })\n }))\n\n unregisters.push(hooks.hook('tool:dispatched', (ctx) => {\n if (ctx.outcome === 'gate-block' || ctx.outcome === 'unknown' || ctx.outcome === 'invalid-input') {\n blocks.push({\n callId: ctx.callId,\n toolName: ctx.name,\n outcome: ctx.outcome,\n ...(ctx.reason ? { reason: ctx.reason } : {}),\n })\n }\n }))\n\n unregisters.push(hooks.hook('validation:reject', (ctx) => {\n validationRejects.push({\n callId: ctx.callId,\n toolName: ctx.name,\n reason: ctx.reason,\n })\n }))\n\n unregisters.push(hooks.hook('budget:exceeded', (ctx) => {\n budgetEvents.push({\n kind: 'bytes',\n observed: ctx.bytes,\n limit: ctx.budget,\n turnId: ctx.turnId,\n })\n }))\n\n unregisters.push(hooks.hook('tool-budget:exceeded', (ctx) => {\n budgetEvents.push({\n kind: 'tool-count',\n toolName: ctx.tool,\n mode: ctx.mode,\n observed: ctx.count,\n limit: ctx.max,\n turnId: ctx.turnId,\n })\n }))\n\n unregisters.push(hooks.hook('repeat-guard:exceeded', (ctx) => {\n repeatGuardEvents.push({\n toolName: ctx.tool,\n count: ctx.count,\n threshold: ctx.threshold,\n action: ctx.action,\n turnId: ctx.turnId,\n })\n }))\n\n unregisters.push(hooks.hook('pairing:repair', (ctx) => {\n pairingRepairs[ctx.mode] = (pairingRepairs[ctx.mode] ?? 0) + 1\n }))\n\n unregisters.push(hooks.hook('agent:done', (stats) => {\n const endedAt = Date.now()\n\n // Build per-model rollup via the existing `statsByModel` helper\n // so we stay in lockstep with how the rest of the harness\n // attributes cost and tokens by model id.\n const byModel: RunSummaryByModel[] = []\n for (const [modelId, usage] of statsByModel(stats)) {\n byModel.push({\n modelId,\n input: usage.input,\n output: usage.output,\n cacheRead: usage.cacheRead,\n cacheCreation: usage.cacheCreation,\n cost: usage.cost,\n turns: usage.turns,\n })\n }\n\n // Flatten child stats into nested summaries. We don't have the\n // child's full event lists here (those landed on the child's\n // own hooks), so the nested entry is a minimal totals-only\n // record — enough for a flat per-run audit trail; consumers\n // wanting full per-child event lists install a collector on\n // each subagent.\n for (const c of stats.children ?? []) {\n children.push(minimalSummaryFromStats(c.stats, {\n depth: c.depth ?? depth + 1,\n status: c.status === 'aborted' ? 'aborted' : 'completed',\n }))\n }\n\n const summary: RunSummary = {\n ...(runId ? { runId } : {}),\n ...(parentRunId ? { parentRunId } : {}),\n depth,\n ...(agentName ? { agentName } : {}),\n startedAt,\n endedAt,\n durationMs: endedAt - startedAt,\n status: aborted ? 'aborted' : 'completed',\n turns: stats.turns,\n totals: buildTotals(stats),\n byModel,\n errors: errors.slice(),\n blocks: blocks.slice(),\n validationRejects: validationRejects.slice(),\n budgetEvents: budgetEvents.slice(),\n repeatGuardEvents: repeatGuardEvents.slice(),\n pairingRepairs: { ...pairingRepairs },\n ...(children.length > 0 ? { children: children.slice() } : {}),\n }\n\n last = summary\n try {\n options.onSummary?.(summary)\n }\n catch {\n // Sink errors are not the collector's concern.\n }\n }))\n\n let disposed = false\n return function uninstall() {\n if (disposed)\n return\n disposed = true\n for (const un of unregisters) {\n try {\n un()\n }\n catch { /* ignore */ }\n }\n }\n },\n }\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nfunction buildTotals(stats: AgentStats): RunSummaryTokens {\n return {\n input: stats.totalIn,\n output: stats.totalOut,\n cacheRead: stats.totalCacheRead,\n cacheCreation: stats.totalCacheCreation,\n ...(typeof stats.cost === 'number' ? { cost: stats.cost } : {}),\n ...(typeof stats.timeTillFirstTokenMs === 'number' ? { ttftMs: stats.timeTillFirstTokenMs } : {}),\n }\n}\n\nfunction minimalSummaryFromStats(\n stats: AgentStats,\n meta: { depth: number, status: 'completed' | 'aborted' },\n): RunSummary {\n const byModel: RunSummaryByModel[] = []\n for (const [modelId, usage] of statsByModel(stats)) {\n byModel.push({\n modelId,\n input: usage.input,\n output: usage.output,\n cacheRead: usage.cacheRead,\n cacheCreation: usage.cacheCreation,\n cost: usage.cost,\n turns: usage.turns,\n })\n }\n const children: RunSummary[] = []\n for (const c of stats.children ?? []) {\n children.push(minimalSummaryFromStats(c.stats, {\n depth: c.depth ?? meta.depth + 1,\n status: c.status === 'aborted' ? 'aborted' : 'completed',\n }))\n }\n return {\n depth: meta.depth,\n startedAt: 0,\n endedAt: 0,\n durationMs: stats.elapsed,\n status: meta.status,\n turns: stats.turns,\n totals: buildTotals(stats),\n byModel,\n errors: [],\n blocks: [],\n validationRejects: [],\n budgetEvents: [],\n repeatGuardEvents: [],\n pairingRepairs: {},\n ...(children.length > 0 ? { children } : {}),\n }\n}\n","/**\n * Headless mode — a non-interactive, fully-structured way to drive the agent\n * for automation and RL rollouts.\n *\n * `agent.run()` already returns rich {@link AgentStats}, the session captures a\n * lossless transcript (`session.turns`), and the hook bus exposes every\n * streaming/tool/turn event. What this module adds is the *contract* an RL loop\n * needs: one call (`runHeadless`) that runs a prompt to completion in a chosen\n * workdir, bounded by turns + wall-clock, and hands back a single serializable\n * {@link HeadlessResult} — final answer, verifiable structured output, usage,\n * and the complete trajectory — plus an optional live {@link HeadlessEvent}\n * stream (the in-process equivalent of `--output-format stream-json`).\n *\n * It is provider-agnostic: the caller passes a built {@link Provider} (e.g.\n * `local()` pointed at a vLLM server, or any of the named providers). The thin\n * CLI in `src/headless-cli.ts` wraps this with arg parsing, file/stdin I/O, and\n * JSONL transcript writing.\n */\n\nimport type { Hookable } from 'hookable'\nimport type { AgentHooks, AgentOptions } from './agent'\nimport type { ExecutionContext } from './contexts'\nimport type { Provider } from './providers'\nimport type { RunSummary } from './run-summary'\nimport type { Session, SessionStore } from './session'\nimport type { PairingRepair } from './session/messages'\nimport type { ToolDef } from './tools'\nimport type {\n AgentStats,\n McpServerConfig,\n PromptPart,\n SessionContentBlock,\n SessionTurn,\n ThinkingLevel,\n ToolResultContent,\n ToolResultDocumentContent,\n TurnFinishReason,\n} from './types'\nimport { createAgent } from './agent'\nimport { createProcessContext } from './contexts'\nimport { basic } from './presets'\nimport { createRunSummaryCollector } from './run-summary'\nimport { createMemoryStore, createSession, toAnthropic } from './session'\nimport { ensureToolResultPairing } from './session/messages'\nimport { documentBlockMarker, toolResultToText } from './types'\n\n// ---------------------------------------------------------------------------\n// Public shapes\n// ---------------------------------------------------------------------------\n\nexport type HeadlessStatus = 'completed' | 'aborted' | 'error' | 'timeout'\n\nexport interface HeadlessUsage {\n input: number\n output: number\n cacheRead: number\n cacheCreation: number\n /** Cumulative USD cost when the provider/registry could price the run. */\n cost?: number\n}\n\nexport interface HeadlessErrorInfo {\n message: string\n /** Typed-error class name (`AgentAbortedError`, `AgentContextExceededError`, …). */\n type: string\n}\n\n/**\n * Strictly JSON-serializable postmortem of one headless run. Everything an RL\n * reward function needs: the final answer (`finalText`), the verifiable\n * structured output (`output`, present iff a `schema` was set), usage/turns,\n * and the lossless `transcript` (the SFT training data).\n */\nexport interface HeadlessResult {\n status: HeadlessStatus\n /** Concatenated text of the last assistant turn that produced any text. */\n finalText: string\n /** Schema-enforced structured output (only when `opts.schema` is set). */\n output?: Record<string, unknown>\n usage: HeadlessUsage\n turns: number\n durationMs: number\n /** Total `tool_call` blocks across the whole transcript. */\n numToolCalls: number\n /** Finish reason of the final turn that reported one. */\n finishReason?: TurnFinishReason\n error?: HeadlessErrorInfo\n sessionId: string\n /** Incident postmortem (errors, gate blocks, budget events) via run-summary. */\n summary?: RunSummary\n /** Lossless transcript — raw `session.turns`. Thinking stripped when `includeThinking: false`. */\n transcript: SessionTurn[]\n}\n\nexport type HeadlessOutputFormat = 'zidane' | 'provider'\n\nexport type ProviderTranscriptFormat = 'anthropic' | 'openai'\n\nexport type FormattedHeadlessResult\n = | HeadlessResult\n | unknown[]\n\nexport type FormattedHeadlessTurnEvent\n = | Extract<HeadlessEvent, { type: 'turn' }>\n | unknown[]\n\nexport function exitCodeForHeadlessResult(result: HeadlessResult): number {\n switch (result.status) {\n case 'completed': return 0\n case 'timeout': return 124\n case 'aborted': return 130\n case 'error':\n default: return 1\n }\n}\n\n/**\n * Live event union — the in-process equivalent of a `stream-json` line. Every\n * member is JSON-serializable; render to JSONL with {@link headlessEventToJsonl}.\n */\nexport type HeadlessEvent\n = | { type: 'start', runId: string, provider?: string }\n | { type: 'text', delta: string }\n | { type: 'thinking', delta: string }\n | { type: 'tool_call', callId: string, name: string, input: Record<string, unknown> }\n | { type: 'tool_result', callId: string, name: string, output: string, isError: boolean }\n | { type: 'turn', index: number, turn: SessionTurn }\n | { type: 'spawn', event: 'before' | 'complete' | 'error', id: string, info?: Record<string, unknown> }\n | { type: 'error', message: string, errorType?: string }\n | { type: 'result', result: HeadlessResult }\n\n/** Serialize one event as a newline-terminated JSON line (stream-json). */\nexport function headlessEventToJsonl(event: HeadlessEvent): string {\n return `${JSON.stringify(event)}\\n`\n}\n\nexport function formattedHeadlessTurnEventToJsonl(event: FormattedHeadlessTurnEvent): string {\n return `${JSON.stringify(event)}\\n`\n}\n\nexport function providerTranscriptFormatForProvider(providerName: string): ProviderTranscriptFormat {\n return providerName === 'anthropic' ? 'anthropic' : 'openai'\n}\n\nexport function transcriptToProviderMessages(\n turns: SessionTurn[],\n providerName: string,\n): unknown[] {\n const format = providerTranscriptFormatForProvider(providerName)\n if (format === 'anthropic') {\n return turns\n .filter((turn): turn is SessionTurn & { role: 'user' | 'assistant' } => turn.role === 'user' || turn.role === 'assistant')\n .map(turn => toAnthropic({ role: turn.role, content: turn.content }))\n }\n return transcriptToOpenAIMessages(turns, { strictToolPairing: false })\n}\n\nexport function formatHeadlessResult(\n result: HeadlessResult,\n options: { format: HeadlessOutputFormat, providerName: string },\n): FormattedHeadlessResult {\n if (options.format === 'zidane')\n return result\n return transcriptToProviderMessages(result.transcript, options.providerName)\n}\n\nexport function formatHeadlessTurnEvent(\n event: Extract<HeadlessEvent, { type: 'turn' }>,\n options: { format: HeadlessOutputFormat, providerName: string },\n): FormattedHeadlessTurnEvent {\n if (options.format === 'zidane')\n return event\n return transcriptToProviderMessages([event.turn], options.providerName)\n}\n\nexport interface HeadlessOptions {\n /** User prompt — plain string or multimodal `PromptPart[]`. */\n prompt: string | PromptPart[]\n /** Built provider (e.g. `local()`, `openaiCompat(...)`, `anthropic(...)`). */\n provider: Provider\n model?: string\n /** Override the preset system prompt for this run. */\n system?: string\n thinking?: ThinkingLevel\n maxTurns?: number\n maxTokens?: number\n /** Wall-clock cap; on expiry the run is aborted and `status` becomes `'timeout'`. */\n timeoutMs?: number\n /** External abort signal — chained with the internal timeout controller. */\n signal?: AbortSignal\n /** JSON Schema for structured-output enforcement → populates `result.output`. */\n schema?: Record<string, unknown>\n /** Agent behavior defaults for this headless run. Top-level shortcuts below override matching fields. */\n behavior?: AgentOptions['behavior']\n /** Tool overrides. Omit to use the basic preset's tools. */\n tools?: Record<string, ToolDef>\n mcpServers?: McpServerConfig[]\n skills?: AgentOptions['skills']\n /** Execution context. Defaults to a process context rooted at `cwd`. */\n execution?: ExecutionContext\n /** Working directory for the default process context (ignored if `execution` is set). */\n cwd?: string\n /** Reuse / resume an existing session. */\n session?: Session\n /** Session store for a fresh session (defaults to an in-memory store). */\n store?: SessionStore\n /** Keep `thinking` blocks in `result.transcript` (default true). */\n includeThinking?: boolean\n /** Live event callback — the in-process stream-json equivalent. */\n onEvent?: (event: HeadlessEvent) => void\n}\n\n// ---------------------------------------------------------------------------\n// runHeadless\n// ---------------------------------------------------------------------------\n\n/**\n * Run a prompt to completion, headless, and return a single serializable\n * {@link HeadlessResult}. Safe to call concurrently for parallel rollouts —\n * each call builds its own agent + session and tears them down in `finally`.\n */\nexport async function runHeadless(opts: HeadlessOptions): Promise<HeadlessResult> {\n const startedAt = Date.now()\n const execution = opts.execution ?? createProcessContext({ cwd: opts.cwd })\n const session = opts.session ?? await createSession({ store: opts.store ?? createMemoryStore() })\n\n const behavior: NonNullable<AgentOptions['behavior']> = { ...(opts.behavior ?? {}) }\n if (opts.maxTurns !== undefined)\n behavior.maxTurns = opts.maxTurns\n if (opts.maxTokens !== undefined)\n behavior.maxTokens = opts.maxTokens\n if (opts.schema !== undefined)\n behavior.schema = opts.schema\n\n const agent = createAgent({\n ...basic,\n provider: opts.provider,\n execution,\n session,\n behavior,\n ...(opts.tools !== undefined ? { tools: opts.tools } : {}),\n ...(opts.mcpServers !== undefined ? { mcpServers: opts.mcpServers } : {}),\n ...(opts.skills !== undefined ? { skills: opts.skills } : {}),\n })\n\n const collector = createRunSummaryCollector()\n const uninstallSummary = collector.install(agent.hooks)\n const uninstallEvents = opts.onEvent ? installHeadlessEventAdapter(agent.hooks, opts.onEvent) : noop\n\n // Resumed sessions carry turns from prior runs — remember where this run\n // starts so transcript/finalText/numToolCalls stay per-run, matching the\n // per-run usage/turns/durationMs stats.\n const runStartIndex = session.turns.length\n\n // Internal controller drives the wall-clock timeout and chains any external\n // signal so a host cancel and a timeout both abort the same run. The\n // listener is removed in `finally` — a long-lived host signal must not\n // accumulate one listener per rollout.\n const controller = new AbortController()\n const onExternalAbort = (): void => controller.abort(opts.signal!.reason)\n if (opts.signal) {\n if (opts.signal.aborted)\n controller.abort(opts.signal.reason)\n else\n opts.signal.addEventListener('abort', onExternalAbort, { once: true })\n }\n let timedOut = false\n const timer = opts.timeoutMs && opts.timeoutMs > 0\n ? setTimeout(() => {\n timedOut = true\n controller.abort(new Error(`Headless run timed out after ${opts.timeoutMs}ms`))\n }, opts.timeoutMs)\n : undefined\n\n let stats: AgentStats | undefined\n let error: HeadlessErrorInfo | undefined\n\n try {\n stats = await agent.run({\n prompt: opts.prompt,\n signal: controller.signal,\n ...(opts.model !== undefined ? { model: opts.model } : {}),\n ...(opts.system !== undefined ? { system: opts.system } : {}),\n ...(opts.thinking !== undefined ? { thinking: opts.thinking } : {}),\n })\n }\n catch (err) {\n // The agent loop throws typed errors (AgentAbortedError,\n // AgentContextExceededError, AgentProviderError, …) — their `name` already\n // classifies the failure, so surface it directly.\n const e = err instanceof Error ? err : new Error(String(err))\n error = { message: e.message, type: e.name }\n }\n finally {\n if (timer)\n clearTimeout(timer)\n opts.signal?.removeEventListener('abort', onExternalAbort)\n uninstallEvents()\n uninstallSummary()\n await agent.destroy()\n }\n\n // Status: a thrown error wins, but an abort/timeout can also surface as a\n // clean resolve (the loop returns partial stats), so check the signal too.\n let status: HeadlessStatus\n if (error)\n status = timedOut ? 'timeout' : controller.signal.aborted ? 'aborted' : 'error'\n else if (controller.signal.aborted)\n status = timedOut ? 'timeout' : 'aborted'\n else\n status = 'completed'\n\n const rawTranscript = session.turns.slice(runStartIndex)\n const transcript = opts.includeThinking === false ? stripThinking(rawTranscript) : rawTranscript\n const summary = collector.latest()\n\n const result: HeadlessResult = {\n status,\n finalText: extractFinalText(rawTranscript),\n ...(stats?.output ? { output: stats.output } : {}),\n usage: {\n input: stats?.totalIn ?? 0,\n output: stats?.totalOut ?? 0,\n cacheRead: stats?.totalCacheRead ?? 0,\n cacheCreation: stats?.totalCacheCreation ?? 0,\n ...(typeof stats?.cost === 'number' ? { cost: stats.cost } : {}),\n },\n turns: stats?.turns ?? 0,\n durationMs: stats?.elapsed ?? (Date.now() - startedAt),\n numToolCalls: countToolCalls(rawTranscript),\n // Suppress `finishReason` on abort/timeout: the last persisted turn can\n // carry `finishReason: 'stop'` (e.g. the model finished a turn, a follow-up\n // continued, then the run was cancelled), which would make a consumer\n // keyed on `finishReason === 'stop'` misread a cancel as \"model finished\".\n // `status` is authoritative for those exits; let it speak alone. The\n // `'pause'` exhaustion exit keeps `status: 'completed'` so it still\n // surfaces its `finishReason: 'pause'` here.\n ...(status !== 'aborted' && status !== 'timeout' && lastFinishReason(stats)\n ? { finishReason: lastFinishReason(stats) }\n : {}),\n ...(error ? { error } : {}),\n sessionId: session.id,\n ...(summary ? { summary } : {}),\n transcript,\n }\n\n opts.onEvent?.({ type: 'result', result })\n return result\n}\n\n// ---------------------------------------------------------------------------\n// Transcript → OpenAI chat messages (SFT-ready)\n// ---------------------------------------------------------------------------\n\nexport interface OpenAIChatMessage {\n role: 'system' | 'user' | 'assistant' | 'tool'\n content: string | null | OpenAIChatContentPart[]\n tool_calls?: Array<{ id: string, type: 'function', function: { name: string, arguments: string } }>\n tool_call_id?: string\n}\n\nexport type OpenAIChatContentPart\n = | { type: 'text', text: string }\n | { type: 'image_url', image_url: { url: string } }\n\n/**\n * Convert raw `session.turns` into standard OpenAI chat-completion messages:\n * assistant turns carry `tool_calls`, and each `tool_result` becomes its own\n * `role: 'tool'` message. This is the drop-in shape for an SFT renderer —\n * unlike `toOpenAI` (session/messages.ts), which emits an internal `_tag`\n * envelope meant for re-sending to a provider, not for training data.\n *\n * Fails closed on corrupt raw turns instead of fabricating tool results; silent\n * placeholders would create structurally-valid but semantically-poisoned SFT\n * examples.\n */\nexport function transcriptToOpenAIMessages(\n turns: SessionTurn[],\n options: { strictToolPairing?: boolean } = {},\n): OpenAIChatMessage[] {\n const { strictToolPairing = true } = options\n const messages: OpenAIChatMessage[] = []\n let chunk: Array<{ role: 'user' | 'assistant', content: SessionContentBlock[] }> = []\n const flushChunk = () => {\n if (chunk.length === 0)\n return\n let paired = chunk\n if (strictToolPairing) {\n const repairs: PairingRepair[] = []\n paired = ensureToolResultPairing(chunk, { onRepair: repair => repairs.push(repair) })\n if (repairs.length > 0) {\n throw new Error(\n `Cannot convert transcript to OpenAI messages: tool pairing repair required (${repairs.map(r => r.mode).join(', ')})`,\n )\n }\n }\n for (const msg of paired)\n pushOpenAIChatMessage(messages, msg)\n chunk = []\n }\n\n for (const turn of turns) {\n if (turn.role === 'system') {\n flushChunk()\n messages.push({ role: 'system', content: textOf(turn.content) })\n continue\n }\n chunk.push({ role: turn.role, content: turn.content })\n }\n flushChunk()\n\n return messages\n}\n\nfunction pushOpenAIChatMessage(messages: OpenAIChatMessage[], msg: { role: 'user' | 'assistant', content: SessionContentBlock[] }): void {\n const text = textOf(msg.content)\n\n if (msg.role === 'assistant') {\n const toolCalls = msg.content.filter((b): b is ToolCallBlock => b.type === 'tool_call')\n const out: OpenAIChatMessage = { role: 'assistant', content: text || null }\n if (toolCalls.length > 0) {\n out.tool_calls = toolCalls.map(b => ({\n id: b.id,\n type: 'function' as const,\n function: { name: b.name, arguments: JSON.stringify(b.input) },\n }))\n }\n messages.push(out)\n return\n }\n\n // user turn — may carry prompt text/images and/or tool_result blocks\n const userParts = contentPartsFromBlocks(msg.content)\n if (userParts.length > 0)\n messages.push({ role: 'user', content: simplifyOpenAIContent(userParts) })\n\n for (const tr of msg.content.filter((b): b is ToolResultBlock => b.type === 'tool_result')) {\n const { text, images } = summarizeToolResultForOpenAI(tr.output)\n if (images.length === 0) {\n messages.push({ role: 'tool', tool_call_id: tr.callId, content: text })\n continue\n }\n\n const noun = images.length === 1 ? 'image' : 'images'\n const marker = `[${images.length} ${noun} attached — see next user message]`\n messages.push({\n role: 'tool',\n tool_call_id: tr.callId,\n content: text.length > 0 ? `${text}\\n\\n${marker}` : marker,\n })\n messages.push({\n role: 'user',\n content: [\n ...images.map(toOpenAIImagePart),\n { type: 'text', text: `(${noun} returned by tool call ${tr.callId})` },\n ],\n })\n }\n}\n\n// ---------------------------------------------------------------------------\n// Internals\n// ---------------------------------------------------------------------------\n\ntype TextBlock = Extract<SessionContentBlock, { type: 'text' }>\ntype ToolCallBlock = Extract<SessionContentBlock, { type: 'tool_call' }>\ntype ToolResultBlock = Extract<SessionContentBlock, { type: 'tool_result' }>\n\nfunction noop(): void {}\n\nfunction textOf(content: SessionContentBlock[]): string {\n return content.filter((b): b is TextBlock => b.type === 'text').map(b => b.text).join('')\n}\n\nfunction contentPartsFromBlocks(content: SessionContentBlock[]): OpenAIChatContentPart[] {\n const parts: OpenAIChatContentPart[] = []\n for (const block of content) {\n if (block.type === 'image')\n parts.push(toOpenAIImagePart(block))\n else if (block.type === 'document')\n parts.push({ type: 'text', text: documentMarker(block) })\n else if (block.type === 'text' && block.text.length > 0)\n parts.push({ type: 'text', text: block.text })\n }\n return parts\n}\n\nfunction simplifyOpenAIContent(parts: OpenAIChatContentPart[]): string | OpenAIChatContentPart[] {\n if (parts.length === 1 && parts[0].type === 'text')\n return parts[0].text\n return parts\n}\n\nfunction toOpenAIImagePart(image: { mediaType: string, data: string }): OpenAIChatContentPart {\n return {\n type: 'image_url',\n image_url: { url: `data:${image.mediaType};base64,${image.data}` },\n }\n}\n\nfunction summarizeToolResultForOpenAI(output: string | ToolResultContent[]): {\n text: string\n images: Array<{ mediaType: string, data: string }>\n} {\n if (typeof output === 'string')\n return { text: output, images: [] }\n\n const texts: string[] = []\n const images: Array<{ mediaType: string, data: string }> = []\n for (const block of output) {\n if (block.type === 'text') {\n texts.push(block.text)\n }\n else if (block.type === 'image') {\n images.push({ mediaType: block.mediaType, data: block.data })\n }\n else {\n texts.push(documentMarker(block))\n }\n }\n return { text: texts.join('\\n'), images }\n}\n\nfunction documentMarker(doc: ToolResultDocumentContent): string {\n return documentBlockMarker(doc, 'document omitted')\n}\n\nfunction extractFinalText(turns: SessionTurn[]): string {\n for (let i = turns.length - 1; i >= 0; i--) {\n const turn = turns[i]\n if (turn.role !== 'assistant')\n continue\n const text = textOf(turn.content)\n if (text)\n return text\n }\n return ''\n}\n\nfunction countToolCalls(turns: SessionTurn[]): number {\n let n = 0\n for (const turn of turns) {\n for (const block of turn.content) {\n if (block.type === 'tool_call')\n n++\n }\n }\n return n\n}\n\nfunction lastFinishReason(stats?: AgentStats): TurnFinishReason | undefined {\n const usages = stats?.turnUsage\n if (!usages)\n return undefined\n for (let i = usages.length - 1; i >= 0; i--) {\n if (usages[i].finishReason)\n return usages[i].finishReason\n }\n return undefined\n}\n\nfunction stripThinking(turns: SessionTurn[]): SessionTurn[] {\n return turns.map(turn => ({\n ...turn,\n content: turn.content.filter(b => b.type !== 'thinking' && b.type !== 'redacted_thinking'),\n }))\n}\n\nexport function installHeadlessEventAdapter(\n hooks: Hookable<AgentHooks>,\n onEvent: (event: HeadlessEvent) => void,\n): () => void {\n const unregs: Array<() => void> = []\n const emittedTurnIds = new Set<string>()\n const emit = (event: HeadlessEvent): void => {\n try {\n onEvent(event)\n }\n catch {\n // A consumer's sink throwing is not the adapter's concern.\n }\n }\n\n unregs.push(hooks.hook('agent:start', ctx => emit({\n type: 'start',\n runId: ctx.runId,\n ...(ctx.providerName ? { provider: ctx.providerName } : {}),\n })))\n unregs.push(hooks.hook('stream:text', ctx => emit({ type: 'text', delta: ctx.delta })))\n unregs.push(hooks.hook('stream:thinking', ctx => emit({ type: 'thinking', delta: ctx.delta })))\n unregs.push(hooks.hook('tool:before', ctx => emit({\n type: 'tool_call',\n callId: ctx.callId,\n name: ctx.name,\n input: ctx.input,\n })))\n unregs.push(hooks.hook('tool:after', ctx => emit({\n type: 'tool_result',\n callId: ctx.callId,\n name: ctx.name,\n output: toolResultToText(ctx.result),\n isError: false,\n })))\n unregs.push(hooks.hook('tool:error', ctx => emit({\n type: 'tool_result',\n callId: ctx.callId,\n name: ctx.name,\n output: ctx.error.message,\n isError: true,\n })))\n unregs.push(hooks.hook('session:turns', (ctx) => {\n const startIndex = ctx.count - ctx.turns.length\n ctx.turns.forEach((turn, i) => {\n if (emittedTurnIds.has(turn.id))\n return\n emittedTurnIds.add(turn.id)\n emit({ type: 'turn', index: startIndex + i, turn })\n })\n }))\n unregs.push(hooks.hook('spawn:before', ctx => emit({ type: 'spawn', event: 'before', id: ctx.id })))\n unregs.push(hooks.hook('spawn:complete', ctx => emit({\n type: 'spawn',\n event: 'complete',\n id: ctx.id,\n info: { status: ctx.status ?? 'completed' },\n })))\n unregs.push(hooks.hook('spawn:error', ctx => emit({\n type: 'spawn',\n event: 'error',\n id: ctx.id,\n info: { message: ctx.error.message },\n })))\n unregs.push(hooks.hook('stream:error', ctx => emit({\n type: 'error',\n message: ctx.err instanceof Error ? ctx.err.message : String(ctx.err),\n ...(ctx.err instanceof Error ? { errorType: ctx.err.name } : {}),\n })))\n\n return () => {\n for (const un of unregs) {\n try {\n un()\n }\n catch {\n // ignore\n }\n }\n }\n}\n"],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- import { Bn as OpenAIChatContentPart, Fn as HeadlessOptions, Gn as formatHeadlessTurnEvent, Hn as ProviderTranscriptFormat, In as HeadlessOutputFormat, Jn as installHeadlessEventAdapter, Kn as formattedHeadlessTurnEventToJsonl, Ln as HeadlessResult, Mn as FormattedHeadlessTurnEvent, Nn as HeadlessErrorInfo, Pn as HeadlessEvent, Qn as transcriptToProviderMessages, Rn as HeadlessStatus, Un as exitCodeForHeadlessResult, Vn as OpenAIChatMessage, Wn as formatHeadlessResult, Xn as runHeadless, Yn as providerTranscriptFormatForProvider, Zn as transcriptToOpenAIMessages, jn as FormattedHeadlessResult, qn as headlessEventToJsonl, zn as HeadlessUsage } from "./index-LX8KCBXU.js";
1
+ import { Bn as OpenAIChatContentPart, Fn as HeadlessOptions, Gn as formatHeadlessTurnEvent, Hn as ProviderTranscriptFormat, In as HeadlessOutputFormat, Jn as installHeadlessEventAdapter, Kn as formattedHeadlessTurnEventToJsonl, Ln as HeadlessResult, Mn as FormattedHeadlessTurnEvent, Nn as HeadlessErrorInfo, Pn as HeadlessEvent, Qn as transcriptToProviderMessages, Rn as HeadlessStatus, Un as exitCodeForHeadlessResult, Vn as OpenAIChatMessage, Wn as formatHeadlessResult, Xn as runHeadless, Yn as providerTranscriptFormatForProvider, Zn as transcriptToOpenAIMessages, jn as FormattedHeadlessResult, qn as headlessEventToJsonl, zn as HeadlessUsage } from "./index-C7BvI1Hi.js";
2
2
  export { FormattedHeadlessResult, FormattedHeadlessTurnEvent, HeadlessErrorInfo, HeadlessEvent, HeadlessOptions, HeadlessOutputFormat, HeadlessResult, HeadlessStatus, HeadlessUsage, OpenAIChatContentPart, OpenAIChatMessage, ProviderTranscriptFormat, exitCodeForHeadlessResult, formatHeadlessResult, formatHeadlessTurnEvent, formattedHeadlessTurnEventToJsonl, headlessEventToJsonl, installHeadlessEventAdapter, providerTranscriptFormatForProvider, runHeadless, transcriptToOpenAIMessages, transcriptToProviderMessages };
package/dist/headless.js CHANGED
@@ -1,2 +1,2 @@
1
- import { a as headlessEventToJsonl, c as runHeadless, i as formattedHeadlessTurnEventToJsonl, l as transcriptToOpenAIMessages, n as formatHeadlessResult, o as installHeadlessEventAdapter, r as formatHeadlessTurnEvent, s as providerTranscriptFormatForProvider, t as exitCodeForHeadlessResult, u as transcriptToProviderMessages } from "./headless-D0qfvzG9.js";
1
+ import { a as headlessEventToJsonl, c as runHeadless, i as formattedHeadlessTurnEventToJsonl, l as transcriptToOpenAIMessages, n as formatHeadlessResult, o as installHeadlessEventAdapter, r as formatHeadlessTurnEvent, s as providerTranscriptFormatForProvider, t as exitCodeForHeadlessResult, u as transcriptToProviderMessages } from "./headless-Cn6XXmr3.js";
2
2
  export { exitCodeForHeadlessResult, formatHeadlessResult, formatHeadlessTurnEvent, formattedHeadlessTurnEventToJsonl, headlessEventToJsonl, installHeadlessEventAdapter, providerTranscriptFormatForProvider, runHeadless, transcriptToOpenAIMessages, transcriptToProviderMessages };
@@ -1,4 +1,4 @@
1
- import { a as ExecResult, c as SpawnConfig, o as ExecutionContext } from "./types-CRf_uTpK.js";
1
+ import { l as SpawnConfig, o as ExecResult, s as ExecutionContext } from "./types-BMwiYRIJ.js";
2
2
 
3
3
  //#region src/contexts/process.d.ts
4
4
  declare function createProcessContext(config?: SpawnConfig): ExecutionContext;
@@ -23,4 +23,4 @@ interface SandboxProvider {
23
23
  declare function createSandboxContext(provider: SandboxProvider): ExecutionContext;
24
24
  //#endregion
25
25
  export { createSandboxContext as n, createProcessContext as r, SandboxProvider as t };
26
- //# sourceMappingURL=index-B65HjjKZ.d.ts.map
26
+ //# sourceMappingURL=index-4pQUnr2W.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index-B65HjjKZ.d.ts","names":[],"sources":["../src/contexts/process.ts","../src/contexts/sandbox.ts"],"mappings":";;;iBAyEgB,oBAAA,CAAqB,MAAA,GAAS,WAAA,GAAc,gBAAgB;;;UC5D3D,eAAA;EACf,IAAA;EACA,KAAA,GAAQ,MAAA,EAAQ,WAAA,KAAgB,OAAA;IAAU,EAAA;IAAY,GAAA;EAAA;EACtD,IAAA,GAAO,SAAA,UAAmB,OAAA,UAAiB,OAAA;IAAY,GAAA;IAAc,GAAA,GAAM,MAAA;IAAwB,OAAA;EAAA,MAAuB,OAAA,CAAQ,UAAA;EAClI,QAAA,GAAW,SAAA,UAAmB,IAAA,aAAiB,OAAA;EAC/C,SAAA,GAAY,SAAA,UAAmB,IAAA,UAAc,OAAA,aAAoB,OAAA;EACjE,SAAA,GAAY,SAAA,UAAmB,IAAA,aAAiB,OAAA;EAChD,OAAA,GAAU,SAAA,aAAsB,OAAA;AAAA;AAAA,iBAOlB,oBAAA,CAAqB,QAAA,EAAU,eAAA,GAAkB,gBAAgB"}
1
+ {"version":3,"file":"index-4pQUnr2W.d.ts","names":[],"sources":["../src/contexts/process.ts","../src/contexts/sandbox.ts"],"mappings":";;;iBAyEgB,oBAAA,CAAqB,MAAA,GAAS,WAAA,GAAc,gBAAgB;;;UC5D3D,eAAA;EACf,IAAA;EACA,KAAA,GAAQ,MAAA,EAAQ,WAAA,KAAgB,OAAA;IAAU,EAAA;IAAY,GAAA;EAAA;EACtD,IAAA,GAAO,SAAA,UAAmB,OAAA,UAAiB,OAAA;IAAY,GAAA;IAAc,GAAA,GAAM,MAAA;IAAwB,OAAA;EAAA,MAAuB,OAAA,CAAQ,UAAA;EAClI,QAAA,GAAW,SAAA,UAAmB,IAAA,aAAiB,OAAA;EAC/C,SAAA,GAAY,SAAA,UAAmB,IAAA,UAAc,OAAA,aAAoB,OAAA;EACjE,SAAA,GAAY,SAAA,UAAmB,IAAA,aAAiB,OAAA;EAChD,OAAA,GAAU,SAAA,aAAsB,OAAA;AAAA;AAAA,iBAOlB,oBAAA,CAAqB,QAAA,EAAU,eAAA,GAAkB,gBAAgB"}
@@ -1,5 +1,5 @@
1
- import { M as SkillsConfig, O as SkillConfig, j as SkillSource, k as SkillDiagnostic, l as SkillActivationState, r as AgentHooks } from "./agent-AnumGPWj.js";
2
- import { o as ExecutionContext, s as ExecutionHandle } from "./types-CRf_uTpK.js";
1
+ import { M as SkillsConfig, O as SkillConfig, j as SkillSource, k as SkillDiagnostic, l as SkillActivationState, r as AgentHooks } from "./agent-Bt123Fdy.js";
2
+ import { c as ExecutionHandle, s as ExecutionContext } from "./types-BMwiYRIJ.js";
3
3
  import { Hookable } from "hookable";
4
4
 
5
5
  //#region src/skills/allowed-tools.d.ts
@@ -306,4 +306,4 @@ declare function defineSkill(config: Omit<SkillConfig, 'source'> & {
306
306
  }): SkillConfig;
307
307
  //#endregion
308
308
  export { buildCatalog as C, parseSkillFile as S, installAllowedToolsGate as T, SourcedScanPath as _, SkillValidationResult as a, inferSource as b, parseAllowedToolPattern as c, validateSkillForWrite as d, validateSkillName as f, stripShellInterpolations as g, interpolateShellCommands as h, SkillValidationIssue as i, validateResourcePath as l, InterpolateShellCommandsOptions as m, writeSkillToDisk as n, isToolAllowedByUnion as o, resolveSkills as p, writeSkillsToDisk as r, matchesAllowedTool as s, defineSkill as t, validateResourcePathReal as u, discoverSkills as v, IMPLICITLY_ALLOWED_SKILL_TOOLS as w, parseFrontmatter as x, getDefaultScanPaths as y };
309
- //# sourceMappingURL=index-DsvHiyYU.d.ts.map
309
+ //# sourceMappingURL=index-BodGKXBV.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index-DsvHiyYU.d.ts","names":[],"sources":["../src/skills/allowed-tools.ts","../src/skills/catalog.ts","../src/skills/discovery.ts","../src/skills/interpolate.ts","../src/skills/resolve.ts","../src/skills/validate.ts","../src/skills/writer.ts","../src/skills/index.ts"],"mappings":";;;;;;cAuBa,8BAAA;;;;;;AAkBgB;;;;ACrB7B;iBDmBgB,uBAAA,CACd,KAAA,EAAO,QAAA,CAAS,UAAA,GAChB,KAAA,EAAO,oBAAA;;;UCrBQ,mBAAA;EDoBR;;;;;ECdP,oBAAA;EDe2B;;;;ECV3B,YAAY;AAAA;;;AAAA;iBAME,YAAA,CACd,MAAA,EAAQ,WAAA,IACR,OAAA,GAAS,mBAAwB;;;UC4EzB,eAAA;EACR,WAAA,EAAa,MAAA;EACb,IAAA;EACA,WAAA,EAAa,eAAe;AAAA;;;;;;;;AF7ED;;;;ACrB7B;;;;AAWc;iBC0GE,gBAAA,CAAiB,OAAA,WAAkB,eAAe;AAAA,UAuUxD,iBAAA;;EAER,MAAA,GAAS,WAAW;AAAA;;;;;AD3aa;;;;AC3BoD;;;iBAodjE,cAAA,CACpB,QAAA,UACA,OAAA,GAAS,iBAAA,GACR,OAAA,CAAQ,WAAA;;UAqNM,eAAA;EACf,IAAA;EACA,MAAA,EAAQ,WAAW;AAAA;;AApkBS;AAmB9B;;;iBAyjBgB,mBAAA,CAAA,GAAuB,eAAe;AAzjBY;AAiHjE;;;AAjHiE,iBA0kBlD,WAAA,CAAY,IAAA,WAAe,WAAW;AAjQhC;AActB;;;;;;;;;AAdsB,iBAmRA,cAAA,CACpB,KAAA,EAAO,eAAA,IACP,MAAA,GAAS,WAAA,GACR,OAAA,CAAQ,WAAA;;;;;;;;;;iBC/sBK,wBAAA,CAAyB,YAAoB;AAAA,UAI5C,+BAAA;EHYR;;;AAAoB;;;EGL3B,SAAA,IAAa,OAAA,UAAiB,WAAA;AAAA;;;;AFLlB;AAMd;;;;;;;;;iBEesB,wBAAA,CACpB,YAAA,UACA,SAAA,EAAW,gBAAA,EACX,MAAA,EAAQ,eAAA,EACR,OAAA,GAAS,+BAAA,GACR,OAAA;;;AHlCH;;;;AAIC;AAYD;;;;AAhBA,UIGiB,oBAAA;EACf,MAAA,EAAQ,WAAW;EACnB,OAAA;AAAA;;;;;;;AJa2B;;;;ACrB7B;;;;AAWc;AAMd;;;iBGYsB,aAAA,CAAc,MAAA,EAAQ,YAAA,GAAe,OAAA,CAAQ,oBAAA;;;;;AJtBlE;AAYD;;UKEiB,oBAAA;ELDC;EKGhB,IAAA;ELFO;EKIP,OAAA;ELJ2B;EKM3B,KAAA;AAAA;AAAA,UAGe,qBAAA;EACf,KAAA;EACA,MAAA,EAAQ,oBAAoB;AAAA;ALXD;;;;ACrB7B;;;;AAWc;AAMd;ADI6B,iBK4Bb,iBAAA,CAAkB,IAAY;;;;;;iBAmB9B,qBAAA,CAAsB,KAAA,EAAO,WAAA,GAAc,qBAAqB;;AJjD7C;;;;AC3BoD;;;;;;iBGkLvE,oBAAA,CACd,OAAA,UACA,OAAA;EACG,KAAA;EAAa,YAAA;AAAA;EAA2B,KAAA;EAAc,KAAA;AAAA;;;AHxDO;AAiHjE;;;;AAwNqB;AActB;;;;;;iBG7OsB,wBAAA,CACpB,OAAA,UACA,OAAA,WACC,OAAO;EAAG,KAAA;EAAa,YAAA;AAAA;EAA2B,KAAA;EAAc,KAAA;AAAA;;AH6O7C;AAqNtB;;;;;iBG/ZgB,uBAAA,CAAwB,KAAA;EAAkB,IAAA;EAAc,SAAA;AAAA;AHyaxE;;;;AAAsD;AAiBtD;;;;AAAsD;AAkBtD;;;;AAnCA,iBG5YgB,kBAAA,CACd,WAAA,UACA,KAAA,EAAO,MAAM,mBACb,OAAA;;;;;;iBAyBc,oBAAA,CACd,WAAA,UACA,KAAA,EAAO,MAAM,mBACb,KAAA;;;;;AL1TD;AAYD;;;;;iBM8BgB,gBAAA,CAAiB,KAAA,EAAO,WAAW,EAAE,SAAA;;;;;;iBAoCrC,iBAAA,CAAkB,MAAA,EAAQ,WAAW,IAAI,SAAA;;;;;;;;;iBCjDzC,WAAA,CAAY,MAAA,EAAQ,IAAA,CAAK,WAAA;EAA2B,MAAA,GAAS,WAAA;AAAA,IAA0B,WAAA"}
1
+ {"version":3,"file":"index-BodGKXBV.d.ts","names":[],"sources":["../src/skills/allowed-tools.ts","../src/skills/catalog.ts","../src/skills/discovery.ts","../src/skills/interpolate.ts","../src/skills/resolve.ts","../src/skills/validate.ts","../src/skills/writer.ts","../src/skills/index.ts"],"mappings":"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"}