zidane 5.6.11 → 5.6.13

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 (64) hide show
  1. package/README.md +19 -2
  2. package/dist/{agent-C9AKTU_V.d.ts → agent-ClkpElCZ.d.ts} +540 -55
  3. package/dist/agent-ClkpElCZ.d.ts.map +1 -0
  4. package/dist/chat.d.ts +47 -17
  5. package/dist/chat.d.ts.map +1 -1
  6. package/dist/chat.js +3 -3
  7. package/dist/{index-6f4T7Gc0.d.ts → index-CTDMMdIy.d.ts} +348 -3
  8. package/dist/index-CTDMMdIy.d.ts.map +1 -0
  9. package/dist/{index-DPN7TcXK.d.ts → index-v3Tzobqr.d.ts} +2 -2
  10. package/dist/{index-DPN7TcXK.d.ts.map → index-v3Tzobqr.d.ts.map} +1 -1
  11. package/dist/index.d.ts +4 -4
  12. package/dist/index.js +169 -8
  13. package/dist/index.js.map +1 -1
  14. package/dist/{login-BindcfKi.js → login-DS3sf6b5.js} +4 -4
  15. package/dist/{login-BindcfKi.js.map → login-DS3sf6b5.js.map} +1 -1
  16. package/dist/{mcp-0jRkIV0g.js → mcp-DGeB7-3D.js} +13 -2
  17. package/dist/mcp-DGeB7-3D.js.map +1 -0
  18. package/dist/mcp.d.ts +1 -1
  19. package/dist/mcp.js +1 -1
  20. package/dist/{messages-BfmXLDT4.js → messages-Dym8S_YH.js} +303 -8
  21. package/dist/messages-Dym8S_YH.js.map +1 -0
  22. package/dist/{presets-CmzMeWg2.js → presets-CZXS_87d.js} +2 -2
  23. package/dist/{presets-CmzMeWg2.js.map → presets-CZXS_87d.js.map} +1 -1
  24. package/dist/presets.d.ts +2 -2
  25. package/dist/presets.js +1 -1
  26. package/dist/{providers-C_ahnRBS.js → providers-beXyD9W9.js} +137 -21
  27. package/dist/providers-beXyD9W9.js.map +1 -0
  28. package/dist/providers.d.ts +2 -2
  29. package/dist/providers.js +3 -3
  30. package/dist/restate.d.ts +1 -1
  31. package/dist/session/sqlite.d.ts +1 -1
  32. package/dist/{session-PUzXZlG6.js → session-BRIsmBSY.js} +5 -2
  33. package/dist/session-BRIsmBSY.js.map +1 -0
  34. package/dist/session.d.ts +2 -2
  35. package/dist/session.js +3 -3
  36. package/dist/skills.d.ts +2 -2
  37. package/dist/{tools-CxOfTt3R.js → tools-DE9pR_NG.js} +515 -116
  38. package/dist/tools-DE9pR_NG.js.map +1 -0
  39. package/dist/tools.d.ts +3 -3
  40. package/dist/tools.js +1 -1
  41. package/dist/{transcript-anchors-DDCHSDdX.d.ts → transcript-anchors-D0TR6djV.d.ts} +4 -4
  42. package/dist/transcript-anchors-D0TR6djV.d.ts.map +1 -0
  43. package/dist/tui.d.ts +2 -2
  44. package/dist/tui.d.ts.map +1 -1
  45. package/dist/tui.js +12 -8
  46. package/dist/tui.js.map +1 -1
  47. package/dist/{turn-operations-CxE8BBau.js → turn-operations-6Yls2HuG.js} +907 -42
  48. package/dist/turn-operations-6Yls2HuG.js.map +1 -0
  49. package/dist/types-oKPBdCmL.js.map +1 -1
  50. package/dist/types.d.ts +3 -3
  51. package/docs/ARCHITECTURE.md +101 -20
  52. package/docs/CHAT.md +27 -5
  53. package/docs/RESTATE.md +1 -1
  54. package/docs/SKILL.md +39 -3
  55. package/package.json +5 -2
  56. package/dist/agent-C9AKTU_V.d.ts.map +0 -1
  57. package/dist/index-6f4T7Gc0.d.ts.map +0 -1
  58. package/dist/mcp-0jRkIV0g.js.map +0 -1
  59. package/dist/messages-BfmXLDT4.js.map +0 -1
  60. package/dist/providers-C_ahnRBS.js.map +0 -1
  61. package/dist/session-PUzXZlG6.js.map +0 -1
  62. package/dist/tools-CxOfTt3R.js.map +0 -1
  63. package/dist/transcript-anchors-DDCHSDdX.d.ts.map +0 -1
  64. package/dist/turn-operations-CxE8BBau.js.map +0 -1
@@ -1 +0,0 @@
1
- {"version":3,"file":"tools-CxOfTt3R.js","names":["DEFAULT_LIMIT","builtinShell","pathResolve","readFile","writeFile"],"sources":["../src/aliasing.ts","../src/chat/format.ts","../src/tools/read-state.ts","../src/dedup-tools.ts","../src/loop-persistence.ts","../src/tools/validation.ts","../src/loop.ts","../src/prompt.ts","../src/tool-budgets.ts","../src/tools/shell-semantics.ts","../src/tools/shell.ts","../src/tools/binary-detect.ts","../src/tools/skills-read.ts","../src/tools/shell-quote.ts","../src/tools/skills-run-script.ts","../src/tools/skills-use.ts","../src/tools/tool-search.ts","../src/agent.ts","../src/tools/edit-utils.ts","../src/tools/path-suggest.ts","../src/tools/edit.ts","../src/tools/glob.ts","../src/tools/grep.ts","../src/tools/interaction.ts","../src/tools/list-files.ts","../src/tools/multi-edit.ts","../src/tools/binary-read.ts","../src/tools/read-file.ts","../src/tools/shell-kill.ts","../src/tools/spawn.ts","../src/tools/write-file.ts"],"sourcesContent":["/**\n * Tool-name aliasing helpers.\n *\n * Aliasing is applied at the LLM boundary only: the provider sees the aliased\n * name, but hooks and persisted turns use canonical names. See `AgentOptions.toolAliases`.\n */\n\nimport type { SessionContentBlock, SessionMessage } from './types'\n\n/** Forward + reverse lookup between canonical and aliased tool names */\nexport interface AliasMaps {\n /** canonical → alias (only entries where alias !== canonical) */\n aliasByCanonical: Map<string, string>\n /** alias → canonical (only entries where alias !== canonical) */\n canonicalByAlias: Map<string, string>\n}\n\n/**\n * Build alias lookup maps from a `toolAliases` record.\n *\n * Validates:\n * - No two canonical names map to the same alias (collision).\n * - No alias collides with another canonical tool name (would shadow).\n *\n * Silently ignores alias entries whose canonical name isn't in `canonicalNames` —\n * preset/agent authors can declare aliases for tools that may be added later via MCP.\n *\n * @param aliases - The `toolAliases` map from the agent options.\n * @param canonicalNames - All tool canonical names currently in scope (agent + MCP).\n */\nexport function buildAliasMaps(\n aliases: Record<string, string> | undefined,\n canonicalNames: Iterable<string>,\n): AliasMaps {\n const aliasByCanonical = new Map<string, string>()\n const canonicalByAlias = new Map<string, string>()\n\n if (!aliases) {\n return { aliasByCanonical, canonicalByAlias }\n }\n\n const canonicalSet = new Set(canonicalNames)\n\n // A canonical name is \"genuinely remapped\" when it has an alias entry that is\n // both non-empty and different from itself. Identity aliases or missing keys\n // mean the tool still answers to its canonical name on the wire.\n function isRemappedAway(canonical: string): boolean {\n const mapped = aliases![canonical]\n return typeof mapped === 'string' && mapped.length > 0 && mapped !== canonical\n }\n\n for (const [canonical, alias] of Object.entries(aliases)) {\n if (typeof alias !== 'string' || alias.length === 0)\n throw new Error(`Tool alias for \"${canonical}\" must be a non-empty string`)\n\n if (alias === canonical)\n continue\n\n if (!canonicalSet.has(canonical))\n continue\n\n // When the alias matches an existing canonical tool name, only allow it if\n // that other canonical is itself being remapped away (i.e. a name swap).\n // Otherwise the wire name would shadow a real tool.\n if (canonicalSet.has(alias) && !isRemappedAway(alias)) {\n throw new Error(`Tool alias \"${canonical}\" -> \"${alias}\" collides with an existing canonical tool name`)\n }\n\n const existingCanonical = canonicalByAlias.get(alias)\n if (existingCanonical && existingCanonical !== canonical) {\n throw new Error(\n `Tool alias collision: both \"${existingCanonical}\" and \"${canonical}\" map to alias \"${alias}\"`,\n )\n }\n\n aliasByCanonical.set(canonical, alias)\n canonicalByAlias.set(alias, canonical)\n }\n\n return { aliasByCanonical, canonicalByAlias }\n}\n\n/** Return the alias for a canonical name, falling back to the canonical name itself. */\nexport function toWireName(canonical: string, maps: AliasMaps): string {\n return maps.aliasByCanonical.get(canonical) ?? canonical\n}\n\n/** Return the canonical name for a wire name, falling back to the wire name itself. */\nexport function toCanonicalName(wire: string, maps: AliasMaps): string {\n return maps.canonicalByAlias.get(wire) ?? wire\n}\n\n/**\n * Rewrite `tool_call` block names in a content array from canonical → wire for outbound\n * messages sent to the provider. Mutation is non-destructive (returns a new array).\n */\nexport function rewriteContentToWire(\n content: SessionContentBlock[],\n maps: AliasMaps,\n): SessionContentBlock[] {\n if (maps.aliasByCanonical.size === 0)\n return content\n return content.map((block) => {\n if (block.type !== 'tool_call')\n return block\n const wire = maps.aliasByCanonical.get(block.name)\n if (!wire || wire === block.name)\n return block\n return { ...block, name: wire }\n })\n}\n\n/**\n * Rewrite `tool_call` block names in a content array from wire → canonical for inbound\n * messages received from the provider. Non-destructive.\n */\nexport function rewriteContentToCanonical(\n content: SessionContentBlock[],\n maps: AliasMaps,\n): SessionContentBlock[] {\n if (maps.canonicalByAlias.size === 0)\n return content\n return content.map((block) => {\n if (block.type !== 'tool_call')\n return block\n const canonical = maps.canonicalByAlias.get(block.name)\n if (!canonical || canonical === block.name)\n return block\n return { ...block, name: canonical }\n })\n}\n\n/**\n * Rewrite every `SessionMessage.content` in an array from canonical → wire.\n * Returns a new array of new message objects — input messages are not mutated.\n * When the alias map is empty, returns the input by reference (no allocation).\n */\nexport function rewriteMessagesToWire(\n messages: SessionMessage[],\n maps: AliasMaps,\n): SessionMessage[] {\n if (maps.aliasByCanonical.size === 0)\n return messages\n return messages.map(msg => ({ ...msg, content: rewriteContentToWire(msg.content, maps) }))\n}\n","import { homedir } from 'node:os'\n\n/** Compact token formatter — 12_415 → \"12.4k\", 1_234_567 → \"1.23M\". */\nexport function fmtTokens(n: number): string {\n if (n < 1000)\n return String(n)\n if (n < 1_000_000)\n return `${(n / 1000).toFixed(n < 10_000 ? 2 : 1)}k`\n return `${(n / 1_000_000).toFixed(2)}M`\n}\n\n/** Compact relative-time formatter — \"just now / 5m / 3h / 2d\". */\nexport function ageString(ts: number, now: number = Date.now()): string {\n const m = Math.floor((now - ts) / 60_000)\n if (m < 1)\n return 'just now'\n if (m < 60)\n return `${m}m ago`\n const h = Math.floor(m / 60)\n if (h < 24)\n return `${h}h ago`\n return `${Math.floor(h / 24)}d ago`\n}\n\n/** Six-char short form of a session id for headers and lists. */\nexport function shortId(id: string): string {\n return id.replace(/-/g, '').slice(0, 6)\n}\n\n/**\n * Single-line preview of a multi-line string, capped at `max` chars and\n * ellipsis-terminated when truncated.\n *\n * Whitespace runs (newlines, tabs, multiple spaces) collapse into one\n * space so the rendered output stays on a single visual row no matter\n * how the input was shaped. Used by every transcript \"preview\" surface\n * (spawn-start task, `tool: shell (background): <command>`,\n * `<task-notification>` summary line, etc.) — without the whitespace\n * collapse, a 60-char `slice` on a string with an inline `\\n\\n` paints\n * the second paragraph below the first, producing the visible\n * \"preview text spills onto multiple lines\" bug (and, downstream,\n * misaligned spawn markers when the wrapped lines collide with\n * other events).\n *\n * Reserves one slot for the `…` so the displayed width is exactly\n * `max` when truncation kicks in.\n */\nexport function previewLine(s: string, max: number): string {\n const single = s.replace(/\\s+/g, ' ').trim()\n if (single.length <= max)\n return single\n return `${single.slice(0, max - 1)}…`\n}\n\n/**\n * Compact human-readable duration formatter shared by background-task\n * surfaces (the `<task-notification>` summary, the TUI banner, the\n * `shell_kill` tool result, etc.).\n *\n * Format ladder:\n * - `< 1s` → `\"Nms\"`\n * - `< 10s` → `\"N.Ns\"` (one decimal)\n * - `< 1m` → `\"Ns\"` (whole seconds)\n * - `< 1h` → `\"NmNs\"` / `\"Nm\"` when seconds round to 0\n * - `≥ 1h` → `\"NhNm\"` / `\"Nh\"` when minutes round to 0\n *\n * Single source of truth so a 60s task renders the same across the\n * model-facing XML summary and the user-facing banner. Earlier\n * separate formatters disagreed (XML said `\"60.0s\"`, banner said `\"1m\"`)\n * which was confusing to the user reading both side by side.\n */\nexport function formatDuration(ms: number): string {\n if (ms < 0)\n ms = 0\n if (ms < 1000)\n return `${ms}ms`\n if (ms < 60_000)\n return `${(ms / 1000).toFixed(ms < 10_000 ? 1 : 0)}s`\n const minutes = Math.floor(ms / 60_000)\n const seconds = Math.floor((ms % 60_000) / 1000)\n if (minutes < 60)\n return seconds > 0 ? `${minutes}m${seconds}s` : `${minutes}m`\n const hours = Math.floor(minutes / 60)\n const remMinutes = minutes % 60\n return remMinutes > 0 ? `${hours}h${remMinutes}m` : `${hours}h`\n}\n\n/**\n * Status label for a terminated background task — `\"exited <code>\"`\n * for natural exits, `\"killed\"` (with the signal name when known)\n * for our-issued SIGTERMs.\n *\n * Pulled out as its own function so the `<task-notification>` XML\n * summary, the TUI banner header, the `shell_kill` tool result, and\n * future surfaces all read the same string.\n */\nexport function formatTaskStatus(info: {\n status: 'exited' | 'killed'\n exitCode: number\n signal?: NodeJS.Signals\n}): string {\n return info.status === 'killed'\n ? `killed${info.signal ? ` (${info.signal})` : ''}`\n : `exited ${info.exitCode}`\n}\n\n/**\n * One-line summary of a terminated background task — the shape used by\n * the `<task-notification>` XML's `<summary>` tag AND the TUI banner's\n * `event.text` fallback string. Three dot-separated segments:\n *\n * `<command preview · status · duration>`\n *\n * Centralizes the format so live + replay + wire all agree, and so a\n * future cosmetic tweak (separator glyph, segment ordering) lands in\n * exactly one place.\n */\nexport function formatTaskSummary(info: {\n command: string\n status: 'exited' | 'killed'\n exitCode: number\n signal?: NodeJS.Signals\n durationMs: number\n}, maxCommandChars = 80): string {\n return `${previewLine(info.command, maxCommandChars)} · ${formatTaskStatus(info)} · ${formatDuration(info.durationMs)}`\n}\n\n/**\n * Compact an absolute path for display: replace the user's `$HOME`\n * prefix with `~` (so `/Users/yael/Code/zidane` → `~/Code/zidane`),\n * and optionally left-truncate with an ellipsis when the result\n * still exceeds `maxWidth` (so the path's *tail* — the part the user\n * recognizes — stays visible: `…/zidane` rather than `/Users/yaeluil…`).\n *\n * `maxWidth` is the maximum *display width* in cells. Omit to skip\n * truncation. Paths outside `$HOME` are returned verbatim modulo\n * truncation. The ellipsis (`…`) counts as one cell.\n *\n * `home` overrides `os.homedir()` for tests; production callers leave\n * it undefined and pay the cheap one-syscall lookup per call.\n */\nexport function compactPath(path: string, maxWidth?: number, home?: string): string {\n const h = home ?? homedir()\n let display = path\n if (h) {\n if (path === h)\n display = '~'\n else if (path.startsWith(`${h}/`))\n display = `~${path.slice(h.length)}`\n }\n if (maxWidth !== undefined && maxWidth > 1 && display.length > maxWidth) {\n // Left-truncate: keep the rightmost `maxWidth - 1` chars and\n // prepend `…`. The right side of a path is the distinctive\n // bit (repo name, leaf folder); the left side is the boilerplate\n // (home, common parents) we're happy to drop.\n return `…${display.slice(display.length - maxWidth + 1)}`\n }\n return display\n}\n","/**\n * Per-session file read tracking + tool-dedup state.\n *\n * Two concerns, one module so they can share the `WeakMap<Session, …>`\n * pattern (state lives as long as the session does, GC'd alongside it —\n * no public schema changes, no persistence). Each section below stands\n * on its own; the two maps don't share keys or behavior.\n *\n * ## Read-state\n *\n * Shared between `read_file` (dedup re-reads) and `edit` / `multi_edit`\n * (`requireReadBeforeEdit` guard). The state map records, per canonical\n * absolute file path:\n *\n * - `contentHash` — FNV-1a hash of the bytes the model last saw. Identical\n * re-reads return a stub; edits compare against this hash to detect\n * stale content.\n * - `(offset, limit, maxBytes, lineNumbers)` — slice / shape of the last\n * read, so a wider re-read invalidates the prior stub and a different\n * `lineNumbers` mode doesn't dedup against the previous shape.\n * - `mtimeMs` — wall-clock at last read, diagnostic only.\n *\n * Resolution is split between two helpers so consumers don't have to\n * juggle the `Session` vs. explicit-map cases themselves:\n *\n * - {@link getReadState} returns the map keyed by `Session` instance.\n * - {@link resolveReadStateMap} prefers an explicit `ctx.readState`\n * (the path `spawn`'s `shareReadState` uses to forward the parent's\n * map into a sessionless child) and falls back to the session-keyed\n * lookup. Tools should call this helper, not `getReadState` directly.\n *\n * ## Tool-dedup\n *\n * Backs `behavior.dedupTools`. Records the most recent `(hash, result)`\n * per canonical tool name within a session. Same WeakMap pattern; no\n * cross-talk with the read-state map.\n */\n\nimport type { Session } from '../session'\nimport type { ToolResultContent } from '../types'\nimport { resolve } from 'node:path'\n\n// ---------------------------------------------------------------------------\n// Read-state\n// ---------------------------------------------------------------------------\n\nexport interface ReadStateEntry {\n contentHash: string\n /** Slice parameters for the last read. */\n offset: number\n limit: number\n maxBytes: number\n /**\n * Whether the prior read emitted line-number prefixes. Tracked so a\n * read with `lineNumbers: true` doesn't dedup against one with\n * `lineNumbers: false` (or vice versa) — the dedup stub would mislead\n * the model about the prior output's shape.\n */\n lineNumbers?: boolean\n /** Wall-clock at last read — diagnostic only, not used for invalidation. */\n mtimeMs: number\n}\n\nexport type ReadStateMap = Map<string, ReadStateEntry>\n\nconst STATE = new WeakMap<Session, ReadStateMap>()\n\n/**\n * Get or lazily create the per-session read-state map. Returns `undefined`\n * when no session is provided.\n *\n * Most tool callers should prefer {@link resolveReadStateMap}, which\n * additionally honors an explicit `ctx.readState` (the path\n * `spawn`'s `shareReadState: true` uses to forward the parent's map\n * into a sessionless child). Use this helper directly only when the\n * Session-keyed map is exactly what you want and you don't need to\n * accept an override.\n */\nexport function getReadState(session: Session | undefined): ReadStateMap | undefined {\n if (!session)\n return undefined\n let map = STATE.get(session)\n if (!map) {\n map = new Map()\n STATE.set(session, map)\n }\n return map\n}\n\n/**\n * Resolve the active read-state map from a tool context. An explicit\n * `ctx.readState` wins (used by `spawn`'s `shareReadState` opt-in to\n * forward the parent's map into a sessionless child); otherwise the\n * usual `Session`-keyed lookup applies.\n *\n * Tools should call this helper instead of `getReadState(ctx.session)`\n * directly so the `shareReadState` plumbing is honored uniformly.\n */\nexport function resolveReadStateMap(ctx: {\n session?: Session\n readState?: ReadStateMap\n}): ReadStateMap | undefined {\n return ctx.readState ?? getReadState(ctx.session)\n}\n\n/**\n * Canonical read-state key for a `(cwd, path)` pair.\n *\n * `ctx.execution.readFile(handle, path)` resolves the model-provided\n * path against `handle.cwd` before touching disk — so `src/App.tsx`,\n * `./src/App.tsx`, and `/abs/cwd/src/App.tsx` all read the **same**\n * bytes. The read-state map MUST mirror that resolution: without it,\n * a model that reads `src/App.tsx` and then edits `./src/App.tsx`\n * trips the `requireReadBeforeEdit` gate for a file it demonstrably\n * already saw (the gate's \"has not been read in this session\" message\n * fires on a stale key).\n *\n * `node:path`'s `resolve(cwd, path)` short-circuits when `path` is\n * already absolute, so absolute and relative shapes converge on the\n * same canonical form. We don't `realpath()` symlinks — the file IS\n * the path the model addressed, not the realpath behind it; the host's\n * execution context decides how to dereference.\n */\nexport function readStateKey(cwd: string, path: string): string {\n return resolve(cwd, path)\n}\n\n/**\n * FNV-1a 32-bit hash, hex-encoded. Fast, non-cryptographic — we only need\n * collision resistance against accidental matches between different file\n * contents within one session (~1 in 4 billion). Cheaper than allocating\n * a Buffer and pulling in `crypto`.\n */\nexport function hashContent(text: string): string {\n let h = 0x811C9DC5\n for (let i = 0; i < text.length; i++) {\n h ^= text.charCodeAt(i)\n h = (h + ((h << 1) + (h << 4) + (h << 7) + (h << 8) + (h << 24))) >>> 0\n }\n return h.toString(16).padStart(8, '0')\n}\n\n// ---------------------------------------------------------------------------\n// Tool-dedup\n// ---------------------------------------------------------------------------\n\nexport interface ToolDedupEntry {\n hash: string\n result: string | ToolResultContent[]\n}\n\nexport type ToolDedupMap = Map<string, ToolDedupEntry>\n\nconst TOOL_DEDUP_STATE = new WeakMap<Session, ToolDedupMap>()\n\n/**\n * Get or lazily create the per-session tool-dedup map. Returns `undefined`\n * when no session is provided — middleware should treat that as \"no dedup\".\n */\nexport function getToolDedupState(session: Session | undefined): ToolDedupMap | undefined {\n if (!session)\n return undefined\n let map = TOOL_DEDUP_STATE.get(session)\n if (!map) {\n map = new Map()\n TOOL_DEDUP_STATE.set(session, map)\n }\n return map\n}\n","/**\n * `dedup-tools` middleware — generic per-tool argument deduplication on top\n * of the `tool:gate` writable-`result` slot.\n *\n * Mirrors the WeakMap-keyed-on-Session pattern that backs `read_file` dedup\n * (`src/tools/read-state.ts`), generalized to arbitrary tools whose hasher\n * is supplied by the consumer via `behavior.dedupTools`.\n *\n * Wiring:\n * 1. `tool:gate` — if a hasher is registered for the tool and produces a\n * string equal to the prior recorded hash, set `ctx.result` to the prior\n * payload. The Z20 substitute path takes over from there: skip execute,\n * fire `tool:after` / `tool:transform`, surface the substitute as a\n * successful tool_result.\n * 2. `tool:after` — record `(hash, result)` for the current input so the\n * NEXT identical call dedups. Recorded post-`tool:transform`, i.e. the\n * payload the model actually saw — replaying a pre-transform value\n * would give the model different content on the dedup hit than on the\n * original call.\n *\n * No-session runs are silent no-ops: there's nowhere to record state.\n */\n\nimport type { Hookable } from 'hookable'\nimport type { AgentHooks } from './agent'\nimport type { Session } from './session'\nimport type { AgentBehavior, ToolHookContext, ToolResultContent } from './types'\nimport { getToolDedupState } from './tools/read-state'\n\n/**\n * Install the per-tool argument-dedup middleware on a hook bus.\n * `getDedupTools` returns the resolved per-tool hashers (run override merged\n * with agent defaults). `getSession` does the same for the session-bound\n * state. Both are called lazily so handlers attached after install (e.g. via\n * MCP bootstrap completion) can take effect.\n *\n * Returns an `uninstall` fn — the agent calls this in `finally` so handlers\n * never leak across runs.\n */\nexport function installDedupToolsGate(\n hooks: Hookable<AgentHooks>,\n getDedupTools: () => AgentBehavior['dedupTools'] | undefined,\n getSession: () => Session | undefined,\n): () => void {\n // Track the hash *we* selected per `(callId, name)` so the corresponding\n // `tool:after` records the right entry. Necessary because `ctx.input` on\n // `tool:after` could in principle differ from the gate snapshot if a\n // `tool:gate` mutation rewrote `input` — and re-hashing there would emit a\n // dedup entry against a different fingerprint than the model's input.\n //\n // Cleared in the same `tool:after` handler after recording. Entries leak\n // when `tool:after` never fires for a call this gate saw — three known\n // paths: a downstream `tool:gate` listener sets `block: true`, validation\n // rejects the (post-gate) input, or a `tool:before` handler throws. All\n // bounded by run lifetime: the closure-local map dies with the run when\n // `uninstall()` clears it (called from the agent's run-end `finally`).\n const pending = new Map<string, string>()\n\n function pendingKey(callId: string, name: string): string {\n return `${callId}::${name}`\n }\n\n function gateHandler(ctx: ToolHookContext & {\n block: boolean\n reason: string\n result?: string | ToolResultContent[]\n runToolCounts: Readonly<Record<string, number>>\n }) {\n // Don't override prior gate handlers (block, or another middleware that\n // already substituted a result).\n if (ctx.block || ctx.result !== undefined)\n return\n\n const dedupTools = getDedupTools()\n const hasher = dedupTools?.[ctx.name]\n if (!hasher)\n return\n\n const session = getSession()\n const state = getToolDedupState(session)\n if (!state)\n return\n\n let hash: string | undefined\n try {\n hash = hasher(ctx.input)\n }\n catch {\n // A throwing hasher disables dedup for this call only — never\n // surface it as a tool failure. The model still gets the real\n // execution, telemetry consumers see no surprise.\n return\n }\n if (typeof hash !== 'string' || hash.length === 0)\n return\n\n const prior = state.get(ctx.name)\n if (prior && prior.hash === hash) {\n // Hit — replay the prior result verbatim. The consumer's hasher is the\n // source of truth on \"still valid\"; we don't manufacture a stub message\n // that second-guesses that.\n ctx.result = prior.result\n return\n }\n\n // Miss — remember the hash so the matching `tool:after` records it.\n pending.set(pendingKey(ctx.callId, ctx.name), hash)\n }\n\n function afterHandler(ctx: ToolHookContext & {\n result: string | ToolResultContent[]\n outputBytes: number\n }) {\n const key = pendingKey(ctx.callId, ctx.name)\n const hash = pending.get(key)\n if (hash === undefined)\n return\n pending.delete(key)\n\n const session = getSession()\n const state = getToolDedupState(session)\n if (!state)\n return\n\n state.set(ctx.name, { hash, result: ctx.result })\n }\n\n const unregisterGate = hooks.hook('tool:gate', gateHandler)\n const unregisterAfter = hooks.hook('tool:after', afterHandler)\n\n return function uninstall() {\n unregisterGate()\n unregisterAfter()\n pending.clear()\n }\n}\n","/**\n * Tool-result disk persistence — replace oversize `tool_result` outputs with\n * a `<persisted-output>` stub pointing at the full payload on disk.\n *\n * Why: a single 200 KB shell output sits in the prompt prefix for the rest\n * of the session and gets re-sent every turn. Persisting it shrinks the\n * inline content to a fixed-size stub (preview + path), preserves the\n * original bytes on disk so the model can `read_file` them deliberately,\n * and keeps the prompt-cache prefix byte-stable across turns.\n *\n * Wire contract:\n *\n * - Trigger: `behavior.persistThreshold` bytes (off / undefined disables).\n * - Excluded tools: `behavior.persistExcludeTools` — bypass regardless of\n * size. Built-in chat profiles list `read_file`, `tool_search`,\n * `skills_use`, `skills_read`, `present_plan`, `ask_user`, `spawn`.\n * - Target: `behavior.persistDir`, one file per call id (`<callId>.txt`).\n * Atomic via write-to-`.tmp` then rename.\n * - Stub: byte-stable XML wrapper carrying tool name, byte count, path,\n * and a 2 KiB head preview. The stub itself becomes the `tool_result`\n * output that lands in `session.turns`, so re-emission on subsequent\n * turns reads back the same bytes and rides the prompt cache.\n *\n * No state. The substitution is one-shot at emit time; nothing in this\n * module persists across turns. The disk file is the source of truth for\n * \"what the model originally saw\"; the in-message stub is what gets\n * re-emitted.\n *\n * Execution-context note: persistence writes to the **host** filesystem\n * via Node's `fs/promises`. For agents running in a docker / sandbox\n * context, the model's `read_file` (which routes through\n * `ctx.execution.readFile`) cannot reach the host blob unless the\n * persistence dir is mounted into the container. Process-context agents\n * (the TUI default) have no such constraint.\n */\n\nimport type { ToolResultContent } from './types'\nimport { Buffer } from 'node:buffer'\nimport { mkdir, readdir, rename, rm, stat, unlink, writeFile } from 'node:fs/promises'\nimport { dirname, isAbsolute, join } from 'node:path'\nimport { errorMessage } from './errors'\nimport { toolOutputByteLength } from './types'\n\n/**\n * Bytes of head content included in the inline preview block. 2 KiB matches\n * Claude Code's `PREVIEW_SIZE_BYTES` — enough for the model to identify the\n * content class (error output / structured data / log shape) and decide\n * whether to call `read_file` on the persisted path for the full payload.\n *\n * Tail-priority preview (matching `shell`'s truncation strategy) was\n * considered but rejected: most \"what is this?\" decisions get made from\n * the head, and the path is in the stub for the rare case where the tail\n * matters.\n */\nexport const PERSISTENCE_PREVIEW_BYTES = 2 * 1024\n\n/**\n * Byte-stable prefix every {@link buildPersistedStub} output starts with.\n * Exported so wire-level passes (tail compaction, future stale-output\n * elision) can recognize a persisted stub and preserve its path attribute\n * rather than replacing the stub with their own — losing the pointer to\n * the on-disk blob.\n *\n * Bound to the literal opening of the XML tag; changing the stub format\n * requires updating this constant in lockstep (and shipping a migration\n * for in-flight sessions).\n */\nexport const PERSISTED_STUB_PREFIX = '<persisted-output tool=\"'\n\n/**\n * Characters that are safe to use verbatim as a filesystem segment under\n * `persistDir`. Alphanumeric + `_` + `-` + `.` covers every real-world\n * `tool_use.id` we've seen (Anthropic `toolu_…`, OpenAI `call_…`, Bun\n * `crypto.randomUUID()` style, our own short ids).\n *\n * Anything else — slashes, `..`, control chars, spaces, unicode — is\n * rejected by {@link maybePersistToolResult} so a malformed or hostile\n * `callId` can't escape `persistDir` via path traversal.\n */\nconst SAFE_CALL_ID = /^[\\w.-]+$/\n\n/**\n * Resolve the per-session persistence directory under `<userDir>/tool-results/<sessionId>/`.\n *\n * The chat layer calls this at session activation and forwards the result\n * via `behavior.persistDir`. Exposed as a public helper so SDK consumers\n * pick the same layout — single source of truth for \"where do blobs live\".\n */\nexport function resolvePersistDir(opts: { userDir: string, sessionId: string }): string {\n if (!isAbsolute(opts.userDir))\n throw new Error(`resolvePersistDir: userDir must be absolute, got \"${opts.userDir}\"`)\n if (!opts.sessionId)\n throw new Error('resolvePersistDir: sessionId must be a non-empty string')\n return join(opts.userDir, 'tool-results', opts.sessionId)\n}\n\n/**\n * Resolve the per-session background-tasks directory under\n * `<userDir>/<sessionId>/tasks/`.\n *\n * The chat layer calls this at session activation and forwards the result\n * via `behavior.tasksDir`. Same shape as {@link resolvePersistDir}: hosts\n * get a single source of truth for \"where do task log files live\".\n * Created on first write; cleanup is the session-delete path's job.\n */\nexport function resolveTasksDir(opts: { userDir: string, sessionId: string }): string {\n if (!isAbsolute(opts.userDir))\n throw new Error(`resolveTasksDir: userDir must be absolute, got \"${opts.userDir}\"`)\n if (!opts.sessionId)\n throw new Error('resolveTasksDir: sessionId must be a non-empty string')\n return join(opts.userDir, opts.sessionId, 'tasks')\n}\n\n/**\n * Inputs to {@link maybePersistToolResult}. Kept as a struct so the loop's\n * call site stays readable and additional optional knobs (compression,\n * mime detection, …) land without re-threading every call site.\n */\nexport interface PersistInput {\n /** Canonical tool name — checked against `excludeTools`. */\n toolName: string\n /** `tool_use` id from the assistant turn. Used as the filename. */\n callId: string\n /** Result returned by the tool (post-`tool:transform`). */\n output: string | ToolResultContent[]\n /** Byte threshold; outputs at or below stay inline. */\n threshold: number\n /** Canonical tool names that bypass persistence. */\n excludeTools?: readonly string[]\n /** Persistence root directory. Created on first write. */\n persistDir: string\n /**\n * Optional cap on the total bytes of persisted blobs under `persistDir`.\n * When set (and > 0), after a successful write the helper sweeps the\n * directory and removes the oldest `*.txt` blobs (by mtime) until the\n * sum of remaining sizes is at or below the cap.\n *\n * Bound to the **current session** because `persistDir` is per-session\n * (see {@link resolvePersistDir}); eviction never crosses session\n * boundaries. The new blob is always preserved — its mtime is the\n * latest, so the LRU sort guarantees older blobs go first.\n *\n * Skipped when the value isn't a positive finite number. Eviction\n * failures (permissions, races) are surfaced through `ZIDANE_DEBUG`\n * but never block the calling tool result; an over-cap dir is a\n * housekeeping concern, not a correctness one.\n */\n maxBytes?: number\n}\n\nexport type PersistOutcome\n = | { kind: 'skip', reason: 'disabled' | 'excluded' | 'under-threshold' | 'unsupported-shape' | 'unsafe-call-id' | 'invalid-persist-dir' }\n | { kind: 'persisted', output: string, originalBytes: number, persistedPath: string, evicted?: { files: number, bytes: number } }\n | { kind: 'error', reason: 'write-failed', error: Error }\n\n/**\n * Decide-and-persist for a single tool result. Pure decision + filesystem\n * side-effect; returns the new wire-level `output` string when substitution\n * happened, otherwise tells the caller to leave the result alone.\n *\n * Atomicity: writes go through `<path>.tmp` + `rename` so a concurrent\n * read (or a crash mid-write) never sees a half-written blob.\n *\n * `ToolResultContent[]` results (images, structured blocks) currently bypass\n * persistence — the inline image bytes are the point of the call, and a\n * mixed text/image array isn't representable as a single `.txt` file. We\n * may revisit if a tool starts returning very large text-only arrays.\n */\nexport async function maybePersistToolResult(input: PersistInput): Promise<PersistOutcome> {\n if (!input.threshold || input.threshold <= 0)\n return { kind: 'skip', reason: 'disabled' }\n\n if (input.excludeTools?.includes(input.toolName))\n return { kind: 'skip', reason: 'excluded' }\n\n // Structured content (images, multi-block) — out of scope for v1.\n // Persistence on these would require either flattening to text or\n // multi-file blobs; neither buys us much for the common case (a tool\n // returning a large image is rare, and the image already has its own\n // size cap in the read_file image path).\n if (typeof input.output !== 'string')\n return { kind: 'skip', reason: 'unsupported-shape' }\n\n // Defense in depth — the chat layer always passes the absolute path\n // produced by `resolvePersistDir`, but a direct SDK consumer could pass\n // a relative path that would resolve against cwd in unexpected ways. We\n // refuse rather than create surprise files outside the intended dir.\n if (!isAbsolute(input.persistDir))\n return { kind: 'skip', reason: 'invalid-persist-dir' }\n\n // Path-traversal guard. The `callId` becomes a filename under\n // `persistDir`; an id containing `/`, `\\`, `..`, or control chars could\n // escape the dir (`../../etc/passwd.txt`) or land in subdirectories the\n // operator didn't anticipate. Real providers (Anthropic `toolu_…`,\n // OpenAI `call_…`, UUIDs) all pass the strict regex; rejecting the rest\n // is purely defensive and never bites legitimate traffic.\n if (!SAFE_CALL_ID.test(input.callId) || input.callId.includes('..'))\n return { kind: 'skip', reason: 'unsafe-call-id' }\n\n const originalBytes = toolOutputByteLength(input.output)\n if (originalBytes <= input.threshold)\n return { kind: 'skip', reason: 'under-threshold' }\n\n const persistedPath = join(input.persistDir, `${input.callId}.txt`)\n try {\n await writeAtomic(persistedPath, input.output)\n }\n catch (err) {\n return { kind: 'error', reason: 'write-failed', error: err instanceof Error ? err : new Error(String(err)) }\n }\n\n const stub = buildPersistedStub({\n toolName: input.toolName,\n originalBytes,\n persistedPath,\n output: input.output,\n })\n\n // LRU enforcement runs AFTER the write so the new blob participates in\n // the sweep (and, being newest by mtime, is never evicted itself unless\n // the cap is so tight a single blob can't fit). Best-effort: a failed\n // unlink is logged under `ZIDANE_DEBUG` but never propagates — the\n // caller already has its tool result; an over-cap dir is housekeeping.\n let evicted: { files: number, bytes: number } | undefined\n if (typeof input.maxBytes === 'number' && Number.isFinite(input.maxBytes) && input.maxBytes > 0) {\n evicted = await enforcePersistDirCap(input.persistDir, input.maxBytes)\n }\n\n return { kind: 'persisted', output: stub, originalBytes, persistedPath, ...(evicted && evicted.files > 0 ? { evicted } : {}) }\n}\n\n/**\n * Enforce a byte-cap on a persist directory by removing oldest `*.txt`\n * blobs (by mtime) until the remaining total is at or below `maxBytes`.\n *\n * Exported for tests + for hosts that want to run a periodic sweep\n * decoupled from the per-call hook. {@link maybePersistToolResult} calls\n * this automatically when `maxBytes` is set on its input, so production\n * callers usually don't invoke it directly.\n *\n * Failure modes:\n *\n * - `persistDir` missing → no-op (nothing to evict).\n * - `readdir` / `stat` errors → swallowed (best-effort); returns\n * `{ files: 0, bytes: 0 }`.\n * - Individual `unlink` failures → counted in the result only if they\n * succeeded; the loop continues to the next candidate so a single\n * permission blip doesn't abort the whole sweep.\n *\n * The mtime-asc sort is stable enough for our purposes: two blobs\n * written in the same millisecond are evicted in `readdir` order, which\n * is filesystem-dependent but consistent within a run. We never need\n * strict total ordering — the LRU is a soft cap, not a transactional\n * guarantee.\n */\nexport async function enforcePersistDirCap(\n persistDir: string,\n maxBytes: number,\n): Promise<{ files: number, bytes: number }> {\n if (!isAbsolute(persistDir) || !Number.isFinite(maxBytes) || maxBytes <= 0)\n return { files: 0, bytes: 0 }\n\n let entries: string[]\n try {\n entries = await readdir(persistDir)\n }\n catch {\n // Directory doesn't exist (no blobs persisted yet) or unreadable.\n // Either way: nothing to do.\n return { files: 0, bytes: 0 }\n }\n\n // `.txt` extension is the canonical shape produced by `maybePersistToolResult`.\n // Filter on it so a stray `.tmp` (crash residue) or a manually-dropped\n // sentinel file doesn't get counted toward the budget or accidentally\n // evicted.\n const txtFiles = entries.filter(name => name.endsWith('.txt'))\n\n interface FileMeta { path: string, size: number, mtime: number }\n const metas: FileMeta[] = []\n for (const name of txtFiles) {\n const path = join(persistDir, name)\n try {\n const s = await stat(path)\n if (!s.isFile())\n continue\n metas.push({ path, size: s.size, mtime: s.mtimeMs })\n }\n catch {\n // Race: file vanished between readdir and stat. Skip silently.\n }\n }\n\n const totalBytes = metas.reduce((sum, m) => sum + m.size, 0)\n if (totalBytes <= maxBytes)\n return { files: 0, bytes: 0 }\n\n // Oldest first. Keep evicting until we're under cap OR we're about to\n // touch the newest blob — that one represents the just-written result\n // the caller needs to be able to point the model at, so we preserve it\n // even if its size alone exceeds the cap. That edge case surfaces as a\n // cap exceedance in the logs; the operator's right next move is\n // raising `persistMaxBytes`.\n //\n // The \"preserve newest\" guard runs off the loop INDEX rather than the\n // successful-eviction counter: an unlink failure earlier in the sweep\n // (file vanished mid-readdir, permissions blip) must NOT shift the\n // safety boundary forward. Were we to use `evictedFiles === metas.length - 1`\n // and the very first unlink failed, we'd walk all the way to the\n // newest blob and try to evict it.\n metas.sort((a, b) => a.mtime - b.mtime)\n let running = totalBytes\n let evictedFiles = 0\n let evictedBytes = 0\n for (let i = 0; i < metas.length; i++) {\n if (running <= maxBytes)\n break\n if (i === metas.length - 1)\n break\n const meta = metas[i]!\n try {\n await unlink(meta.path)\n running -= meta.size\n evictedFiles += 1\n evictedBytes += meta.size\n }\n catch (err) {\n if (process.env.ZIDANE_DEBUG)\n process.stderr.write(`[zidane/persistence] evict \"${meta.path}\" failed: ${errorMessage(err)}\\n`)\n }\n }\n\n return { files: evictedFiles, bytes: evictedBytes }\n}\n\ninterface BuildStubInput {\n toolName: string\n originalBytes: number\n persistedPath: string\n output: string\n}\n\n/**\n * Render the byte-stable `<persisted-output>` stub the model sees in place\n * of the original `tool_result`.\n *\n * Format choices:\n * - XML wrapper because models reliably parse it as structural.\n * - Byte count + path in attributes so the model can decide whether to\n * `read_file` the persisted blob without scanning the preview.\n * - Preview always shows the head — `shell`'s tail-priority truncation is\n * irrelevant here because the model has the full path if it needs the\n * tail.\n * - No timestamps, no random UUIDs inside the stub: every byte must be\n * reproducible from the inputs, otherwise re-emission on subsequent\n * turns would bust the prompt cache.\n *\n * Exported for tests (asserting the byte-stable contract) and for SDK\n * consumers wiring their own persistence middleware against the same\n * surface.\n */\nexport function buildPersistedStub(input: BuildStubInput): string {\n const { slice: previewSlice, bytes: previewBytes } = sliceFirstBytes(input.output, PERSISTENCE_PREVIEW_BYTES)\n const truncated = previewSlice.length < input.output.length\n const previewMarker = truncated\n ? `\\n…(${input.originalBytes - previewBytes} more bytes in persisted file)`\n : ''\n // Open tag built from PERSISTED_STUB_PREFIX so the stub-detection constant\n // and the stub emitter share one source of truth — changing the format\n // requires updating one place and downstream recognizers can't drift.\n return [\n `${PERSISTED_STUB_PREFIX}${escapeAttr(input.toolName)}\" bytes=\"${input.originalBytes}\" path=\"${escapeAttr(input.persistedPath)}\">`,\n `${previewSlice}${previewMarker}`,\n '</persisted-output>',\n ].join('\\n')\n}\n\n/**\n * Remove every persisted blob belonging to a session. Called by the chat\n * layer from its session-delete path so closing a session frees the disk\n * footprint alongside the SQLite row.\n *\n * Idempotent — missing directory (session never persisted anything) is a\n * no-op, not an error. Wraps the `rm -rf` so a permissions blip on one\n * blob doesn't propagate to the caller; the chat layer can't usefully\n * recover from \"couldn't unlink a result file\" mid-delete.\n */\nexport async function cleanupPersistedSession(persistRoot: string): Promise<void> {\n if (!isAbsolute(persistRoot))\n throw new Error(`cleanupPersistedSession: persistRoot must be absolute, got \"${persistRoot}\"`)\n try {\n await rm(persistRoot, { recursive: true, force: true })\n }\n catch (err) {\n // Surface under ZIDANE_DEBUG; never throw. A leaked blob dir is\n // harmless (it can be `rm -rf`'d manually) — propagating the error\n // would block the SessionStore.delete() path the user is awaiting.\n if (process.env.ZIDANE_DEBUG)\n process.stderr.write(`[zidane/persistence] cleanup \"${persistRoot}\" failed: ${errorMessage(err)}\\n`)\n }\n}\n\n// ---------------------------------------------------------------------------\n// Internal helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Write-then-rename for atomicity. A concurrent reader (or a crash mid-\n * write) never observes a partial file: the rename is atomic on every\n * POSIX FS, and the destination is either the fully-written file or\n * absent. Creates the parent directory on demand so the caller doesn't\n * have to track which sessions have already initialized their dir.\n *\n * On write failure (disk full, permissions, signal), removes the `.tmp`\n * file so it doesn't accumulate as debris alongside successful blobs.\n * The unlink itself is best-effort — if we can't write to the dir we\n * likely can't unlink from it either, and the original error is what\n * the caller needs to see.\n *\n * Same idiom we use elsewhere (state-store snapshots, session export).\n */\nasync function writeAtomic(path: string, content: string): Promise<void> {\n await mkdir(dirname(path), { recursive: true })\n const tmp = `${path}.tmp`\n try {\n await writeFile(tmp, content, 'utf8')\n await rename(tmp, path)\n }\n catch (err) {\n await rm(tmp, { force: true }).catch(() => {})\n throw err\n }\n}\n\n/**\n * Byte length of a UTF-8 string. `Buffer.byteLength` is the canonical\n * answer; pulling it through a small helper keeps `node:buffer` out of\n * call sites that don't otherwise need it.\n */\nfunction byteLength(text: string): number {\n return Buffer.byteLength(text, 'utf8')\n}\n\n/**\n * Take the first `cap` bytes of `text` without splitting a UTF-8\n * codepoint. Returns the substring AND its exact UTF-8 byte length so\n * the caller doesn't repeat the byte walk (the truncation marker in\n * {@link buildPersistedStub} needs both pieces).\n *\n * Iterates with `for...of` so each step yields a full Unicode codepoint\n * (not a UTF-16 code unit). This matters for content above the BMP —\n * emoji, supplementary-plane characters, anything encoded as a surrogate\n * pair in UTF-16. A naive `text[i]` walk would slice mid-pair, leaving a\n * lone surrogate that re-encodes to the UTF-8 replacement character\n * (garbled preview, off-by-one byte accounting).\n *\n * The `ch.length` advance handles both BMP chars (length 1) and surrogate\n * pairs (length 2) uniformly, since `ch` is whatever `for...of` yielded.\n *\n * Returns the original string when it already fits — no allocation when\n * the cap doesn't bite.\n */\nfunction sliceFirstBytes(text: string, cap: number): { slice: string, bytes: number } {\n if (cap <= 0)\n return { slice: '', bytes: 0 }\n const total = byteLength(text)\n if (total <= cap)\n return { slice: text, bytes: total }\n let bytes = 0\n let charIdx = 0\n for (const ch of text) {\n const chBytes = byteLength(ch)\n if (bytes + chBytes > cap)\n break\n bytes += chBytes\n charIdx += ch.length\n }\n return { slice: text.slice(0, charIdx), bytes }\n}\n\n/**\n * Escape `&`, `\"`, and `<` for safe inclusion in an XML attribute. The\n * stub format embeds tool names and filesystem paths verbatim — both\n * theoretically could contain one of these (Windows paths can't, but a\n * tool alias is user-controlled). Keeping the attribute well-formed\n * prevents a malicious or unusual tool name from breaking the wrapper\n * parse. `&` is escaped first so the subsequent replacements don't\n * double-encode the entities we just emitted.\n */\nfunction escapeAttr(text: string): string {\n return text.replace(/&/g, '&amp;').replace(/\"/g, '&quot;').replace(/</g, '&lt;')\n}\n","/**\n * Tool argument validation against JSON Schema-style inputSchema.\n *\n * Two passes:\n * 1. Required-field presence. Missing or null/undefined required fields fail.\n * 2. Per-property type checks with **best-effort coercion**. Small/OSS models\n * routinely send `\"true\"` for a `boolean` field or `\"42\"` for a `number`,\n * and rejecting outright forces a confusing retry. Instead, we auto-heal\n * coerce when the conversion is unambiguous, fail only when the value\n * cannot be reasonably normalized to any of the declared types.\n *\n * Recursion: when a property declares `type: 'array'` with an `items` schema,\n * each item is validated against `items`. Object items are walked one level\n * deep (their declared `properties` get the same coercion + enum checks the\n * top level does). Items that can't be coerced are dropped rather than\n * rejecting the whole call — the model rarely benefits from an\n * all-or-nothing failure on a 20-item list because one entry was malformed.\n * Dropped items are reported back via `droppedItems` so the tool's `execute`\n * can surface a hint to the model if it wants to.\n */\n\nexport interface ValidationResult {\n valid: boolean\n /** Human-readable reason. Present on failure only. */\n error?: string\n /**\n * Possibly-coerced input. Present iff `valid: true`. Tools should call\n * `execute(coercedInput, ctx)` so auto-healed values reach the tool body.\n * When no coercion was applied, this is reference-equal to the input.\n */\n coercedInput?: Record<string, unknown>\n /**\n * Names of fields whose values were coerced. Empty when nothing changed.\n * Useful for telemetry (`validation:reject` on failure already carries the\n * reason; this is the success-path equivalent).\n */\n coercions?: readonly string[]\n /**\n * Indexes of array items dropped during recursive validation, keyed by\n * the property name. Empty / absent when nothing was dropped. Tools that\n * care about the discrepancy (e.g. `todowrite` wanting to surface\n * \"ignored 2 malformed items\") can inspect this.\n */\n droppedItems?: Readonly<Record<string, readonly number[]>>\n}\n\ninterface PropertySchema {\n type?: string | string[]\n enum?: unknown[]\n /** For `type: 'object'` — nested property schemas (used by array-item recursion). */\n properties?: Record<string, PropertySchema>\n /** For `type: 'object'` — required field names (used by array-item recursion). */\n required?: string[]\n /** For `type: 'array'` — schema applied to each item. */\n items?: PropertySchema\n /** For `type: 'array'` — maximum item count. Excess items are truncated. */\n maxItems?: number\n /** For `type: 'array'` — minimum item count. Falls below → validation error. */\n minItems?: number\n}\n\nconst TRUE_STRINGS = new Set(['true', 'True', 'TRUE', '1', 'yes', 'Yes', 'YES'])\nconst FALSE_STRINGS = new Set(['false', 'False', 'FALSE', '0', 'no', 'No', 'NO'])\n\nexport function validateToolArgs(\n input: Record<string, unknown>,\n schema: Record<string, unknown>,\n): ValidationResult {\n const required = (schema.required ?? []) as string[]\n const properties = (schema.properties ?? {}) as Record<string, PropertySchema>\n\n // Pass 1: required-field presence.\n for (const field of required) {\n if (!(field in input) || input[field] === undefined || input[field] === null) {\n return { valid: false, error: `Missing required field: ${field}` }\n }\n }\n\n // Pass 2: per-property type check with auto-coercion. Walk the input keys\n // (rather than schema properties) so unknown keys flow through unchanged.\n let coerced: Record<string, unknown> | undefined\n const coercions: string[] = []\n let droppedItems: Record<string, readonly number[]> | undefined\n\n for (const [key, value] of Object.entries(input)) {\n const propSchema = properties[key]\n if (!propSchema?.type)\n continue\n if (value === undefined || value === null)\n continue\n\n const outcome = coerceValue(value, propSchema)\n if (outcome.error) {\n return { valid: false, error: `Field \"${key}\": ${outcome.error}` }\n }\n if (outcome.changed) {\n if (!coerced)\n coerced = { ...input }\n coerced[key] = outcome.value\n coercions.push(key)\n }\n\n // Array recursion: validate each item against `propSchema.items` when\n // declared. Item-level failures drop the item rather than failing the\n // whole call — see file-level comment for rationale.\n const arrayValue = outcome.changed ? outcome.value : value\n if (propSchema.type === 'array' && propSchema.items && Array.isArray(arrayValue)) {\n const itemOutcome = validateArrayItems(arrayValue, propSchema)\n if (itemOutcome.error) {\n return { valid: false, error: `Field \"${key}\": ${itemOutcome.error}` }\n }\n if (itemOutcome.changed || itemOutcome.dropped.length > 0 || itemOutcome.truncated) {\n if (!coerced)\n coerced = { ...input }\n coerced[key] = itemOutcome.items\n // Coercion flag covers both per-item coercions and structural changes\n // (drops, truncation) so consumers wiring `validation:coerce` see a\n // single signal: \"the framework rewrote this field.\"\n if (!coercions.includes(key))\n coercions.push(key)\n }\n if (itemOutcome.dropped.length > 0) {\n if (!droppedItems)\n droppedItems = {}\n droppedItems[key] = itemOutcome.dropped\n }\n }\n }\n\n return {\n valid: true,\n coercedInput: coerced ?? input,\n coercions,\n ...(droppedItems ? { droppedItems } : {}),\n }\n}\n\ninterface ArrayItemsOutcome {\n items: unknown[]\n changed: boolean\n truncated: boolean\n dropped: number[]\n error?: string\n}\n\nfunction validateArrayItems(items: unknown[], schema: PropertySchema): ArrayItemsOutcome {\n if (schema.minItems !== undefined && items.length < schema.minItems) {\n return {\n items,\n changed: false,\n truncated: false,\n dropped: [],\n error: `expected at least ${schema.minItems} item${schema.minItems === 1 ? '' : 's'}, got ${items.length}`,\n }\n }\n\n const itemSchema = schema.items\n if (!itemSchema) {\n return { items, changed: false, truncated: false, dropped: [] }\n }\n\n const out: unknown[] = []\n // Parallel array — original input index of each entry in `out`. Used to\n // resolve truncation drops back to original indexes without re-walking.\n const outOriginalIdx: number[] = []\n const dropped: number[] = []\n let changed = false\n\n for (let i = 0; i < items.length; i++) {\n const item = items[i]\n const v = validateOneItem(item, itemSchema)\n if (v.dropped) {\n dropped.push(i)\n changed = true\n continue\n }\n if (v.changed)\n changed = true\n out.push(v.value)\n outOriginalIdx.push(i)\n }\n\n let truncated = false\n if (schema.maxItems !== undefined && out.length > schema.maxItems) {\n for (let i = schema.maxItems; i < out.length; i++)\n dropped.push(outOriginalIdx[i])\n out.length = schema.maxItems\n truncated = true\n changed = true\n }\n\n // Keep dropped sorted ascending — both branches preserve insertion order,\n // but truncation appends original indexes that may be < some validation\n // drops (no, actually drops happen first then truncation indexes are\n // strictly larger). Still cheap to sort defensively.\n dropped.sort((a, b) => a - b)\n\n return { items: out, changed, truncated, dropped }\n}\n\ninterface OneItemOutcome {\n value: unknown\n changed: boolean\n /** True when the item should be omitted from the output array. */\n dropped: boolean\n}\n\nfunction validateOneItem(item: unknown, schema: PropertySchema): OneItemOutcome {\n // Object items: one level of property coercion + required-field check.\n // We do NOT recurse infinitely — two levels of nesting is enough to cover\n // the structured tool inputs that exist today, and stopping here keeps the\n // validator predictable.\n if (schema.type === 'object') {\n if (!item || typeof item !== 'object' || Array.isArray(item))\n return { value: item, changed: false, dropped: true }\n\n const obj = item as Record<string, unknown>\n const required = schema.required ?? []\n for (const field of required) {\n const v = obj[field]\n if (v === undefined || v === null)\n return { value: item, changed: false, dropped: true }\n }\n\n const properties = schema.properties ?? {}\n let coercedItem: Record<string, unknown> | undefined\n for (const [key, value] of Object.entries(obj)) {\n const subSchema = properties[key]\n if (!subSchema?.type)\n continue\n if (value === undefined || value === null)\n continue\n const outcome = coerceValue(value, subSchema)\n if (outcome.error) {\n // Sub-property couldn't be coerced — drop the whole item.\n return { value: item, changed: false, dropped: true }\n }\n if (outcome.changed) {\n if (!coercedItem)\n coercedItem = { ...obj }\n coercedItem[key] = outcome.value\n }\n }\n return coercedItem\n ? { value: coercedItem, changed: true, dropped: false }\n : { value: item, changed: false, dropped: false }\n }\n\n // Scalar items: reuse the top-level coercer; drop on hard failure.\n if (schema.type) {\n const outcome = coerceValue(item, schema)\n if (outcome.error)\n return { value: item, changed: false, dropped: true }\n return { value: outcome.value, changed: outcome.changed, dropped: false }\n }\n\n return { value: item, changed: false, dropped: false }\n}\n\ninterface CoerceOutcome {\n /** Coerced value; equal to input when `changed: false`. */\n value: unknown\n changed: boolean\n error?: string\n}\n\nfunction coerceValue(value: unknown, schema: PropertySchema): CoerceOutcome {\n const declaredTypes = Array.isArray(schema.type)\n ? schema.type as string[]\n : [schema.type as string]\n\n // Already matches one of the declared types? No coercion, just enum-check.\n for (const t of declaredTypes) {\n if (matchesType(value, t)) {\n if (schema.enum && !schema.enum.includes(value)) {\n return {\n value,\n changed: false,\n error: `must be one of ${JSON.stringify(schema.enum)}, got ${formatValue(value)}`,\n }\n }\n return { value, changed: false }\n }\n }\n\n // Try coercion to each declared type in order; first success wins.\n for (const t of declaredTypes) {\n const coerced = tryCoerce(value, t)\n if (coerced.ok) {\n if (schema.enum && !schema.enum.includes(coerced.value)) {\n return {\n value,\n changed: false,\n error: `must be one of ${JSON.stringify(schema.enum)}, got ${formatValue(coerced.value)}`,\n }\n }\n return { value: coerced.value, changed: true }\n }\n }\n\n const expected = declaredTypes.join(' | ')\n return {\n value,\n changed: false,\n error: `expected ${expected}, got ${jsonType(value)} ${formatValue(value)}`,\n }\n}\n\nfunction matchesType(value: unknown, type: string): boolean {\n switch (type) {\n case 'string': return typeof value === 'string'\n case 'number': return typeof value === 'number' && Number.isFinite(value)\n case 'integer': return typeof value === 'number' && Number.isInteger(value)\n case 'boolean': return typeof value === 'boolean'\n case 'array': return Array.isArray(value)\n case 'object': return value !== null && typeof value === 'object' && !Array.isArray(value)\n case 'null': return value === null\n default: return true\n }\n}\n\nfunction tryCoerce(value: unknown, type: string): { ok: true, value: unknown } | { ok: false } {\n // string → other\n if (typeof value === 'string') {\n if (type === 'boolean') {\n const trimmed = value.trim()\n if (TRUE_STRINGS.has(trimmed))\n return { ok: true, value: true }\n if (FALSE_STRINGS.has(trimmed))\n return { ok: true, value: false }\n return { ok: false }\n }\n if (type === 'number') {\n const n = Number(value.trim())\n return Number.isFinite(n) ? { ok: true, value: n } : { ok: false }\n }\n if (type === 'integer') {\n const n = Number(value.trim())\n return Number.isInteger(n) ? { ok: true, value: n } : { ok: false }\n }\n if (type === 'array' || type === 'object') {\n try {\n const parsed = JSON.parse(value)\n if (type === 'array' && Array.isArray(parsed))\n return { ok: true, value: parsed }\n if (type === 'object' && parsed !== null && typeof parsed === 'object' && !Array.isArray(parsed))\n return { ok: true, value: parsed }\n return { ok: false }\n }\n catch {\n return { ok: false }\n }\n }\n if (type === 'null') {\n return value === '' || value === 'null' ? { ok: true, value: null } : { ok: false }\n }\n }\n\n // number → string / integer\n if (typeof value === 'number' && Number.isFinite(value)) {\n if (type === 'string')\n return { ok: true, value: String(value) }\n if (type === 'integer' && Number.isInteger(value))\n return { ok: true, value }\n }\n\n // boolean → string\n if (typeof value === 'boolean' && type === 'string')\n return { ok: true, value: String(value) }\n\n return { ok: false }\n}\n\nfunction jsonType(value: unknown): string {\n if (value === null)\n return 'null'\n if (Array.isArray(value))\n return 'array'\n return typeof value\n}\n\nfunction formatValue(value: unknown): string {\n let s: string\n try {\n s = JSON.stringify(value)\n }\n catch {\n s = String(value)\n }\n if (s === undefined)\n s = String(value)\n return s.length > 80 ? `${s.slice(0, 77)}...` : s\n}\n","/**\n * Agent turn execution loop.\n *\n * Handles streaming, tool execution (per-tool concurrency-safe\n * scheduling via `executeToolBatch`), steering injection, follow-up\n * messages, and abort.\n */\n\nimport type { Hookable } from 'hookable'\nimport type { AgentHooks } from './agent'\nimport type { AliasMaps } from './aliasing'\nimport type { ExecutionContext, ExecutionHandle } from './contexts'\nimport type { Provider, StreamOptions, ToolCall, ToolResult, ToolSpec } from './providers'\nimport type { Session } from './session'\nimport type { PairingRepair } from './session/messages'\nimport type { SkillsConfig } from './skills/types'\nimport type { ReadStateMap } from './tools/read-state'\nimport type { ToolContext, ToolDef } from './tools/types'\nimport type { AgentBehavior, AgentClock, AgentStats, McpServerConfig, SessionMessage, SessionTurn, ThinkingLevel, ToolHookContext, ToolResultContent, TurnUsage } from './types'\nimport { rewriteContentToCanonical, rewriteMessagesToWire, toCanonicalName, toWireName } from './aliasing'\nimport { AgentAbortedError, AgentBudgetExceededError, AgentProviderError, AgentToolPairingError, errorMessage, toTypedError } from './errors'\nimport { maybePersistToolResult, PERSISTED_STUB_PREFIX } from './loop-persistence'\nimport { ensureEndsWithUserMessage, ensureToolResultPairing } from './session/messages'\nimport { readStateKey, resolveReadStateMap } from './tools/read-state'\nimport { validateToolArgs } from './tools/validation'\nimport { toolOutputByteLength } from './types'\n\nexport interface LoopContext {\n provider: Provider\n hooks: Hookable<AgentHooks>\n /** Agent display name — forwarded into `ToolContext.name`. */\n agentName?: string\n /** Agent default system prompt — forwarded into `ToolContext.system`. */\n agentSystem?: string\n /** Source agent tools (pre-MCP-merge) — forwarded into `ToolContext.tools`. */\n agentTools: Record<string, ToolDef>\n /** Agent tool aliases — forwarded into `ToolContext.toolAliases`. */\n agentToolAliases?: Record<string, string>\n /** Agent MCP servers — forwarded into `ToolContext.mcpServers`. */\n agentMcpServers?: McpServerConfig[]\n /** Agent skills config — forwarded into `ToolContext.skills`. */\n agentSkills?: SkillsConfig\n /** Agent behavior defaults — forwarded into `ToolContext.behavior`. */\n agentBehavior?: AgentBehavior\n tools: Record<string, ToolDef>\n formattedTools: unknown[]\n /**\n * Recompute the wire-level tool list from the current state of the agent's\n * `unlocked` set. Set when `behavior.toolDisclosure === 'lazy'` and at least\n * one MCP tool is lazy — every iteration the loop calls this so newly-\n * surfaced tools (via `tool_search`) advertise on the next provider request.\n * When `undefined`, the loop uses {@link LoopContext.formattedTools} verbatim.\n */\n rebuildFormattedTools?: () => unknown[]\n aliasMaps: AliasMaps\n model: string\n system: string\n thinking: ThinkingLevel\n /**\n * Hard cap on the number of tools in flight concurrently within a single\n * assistant turn. The scheduler dispatches concurrency-safe tools\n * (`ToolDef.isConcurrencySafe`) in parallel up to this cap; unsafe tools\n * act as barriers. `undefined` resolves to the framework default (10);\n * `<= 0` is clamped to `1` (effectively force-sequential).\n */\n maxConcurrentTools?: number\n signal: AbortSignal\n execution: ExecutionContext\n handle: ExecutionHandle\n steeringQueue: string[]\n followUpQueue: string[]\n turns: SessionTurn[]\n runId: string\n generateTurnId: () => string | Promise<string>\n /**\n * Time + UUID source for journaled metadata (turn `createdAt`, synthetic\n * ids). Resolved in `agent.run()` from per-run > agent-level > default\n * precedence. Live-only measurements (TTFT deltas, elapsed) keep\n * `Date.now()` directly.\n */\n clock: AgentClock\n /** Max loop iterations (default: 50) */\n maxTurns?: number\n /** Run-level cost ceiling in USD. See `AgentBehavior.maxCostUsd`. */\n maxCostUsd?: number\n /** Run-level token ceiling (input + output). See `AgentBehavior.maxTotalTokens`. */\n maxTotalTokens?: number\n /** Max tokens per LLM turn (default: 16384) */\n maxTokens?: number\n /** Exact thinking token budget (overrides level-based default) */\n thinkingBudget?: number\n /** JSON Schema for structured output enforcement */\n schema?: Record<string, unknown>\n /** Enable provider prompt caching (default: true). See `AgentBehavior.cache`. */\n cache?: boolean\n /** Soft cap on cumulative tool-output bytes per turn. See `AgentBehavior.toolOutputBudget`. */\n toolOutputBudget?: number\n /** Canonical tool names exempt from the budget sum. See `AgentBehavior.toolOutputBudgetExcludeTools`. */\n toolOutputBudgetExcludeTools?: readonly string[]\n /** Client-side compaction strategy. See `AgentBehavior.compactStrategy`. */\n compactStrategy?: 'off' | 'tail'\n /** Bytes threshold that triggers tail compaction. See `AgentBehavior.compactThreshold`. */\n compactThreshold?: number\n /** Trailing turns kept intact during tail compaction. See `AgentBehavior.compactKeepTurns`. */\n compactKeepTurns?: number\n /**\n * Elide `read_file` tool_results whose corresponding read happened before\n * a later successful edit/write of the same path. See\n * `AgentBehavior.elideStaleReads`.\n */\n elideStaleReads?: boolean\n /** Bytes threshold for disk persistence. See `AgentBehavior.persistThreshold`. */\n persistThreshold?: number\n /** Tool names excluded from persistence. See `AgentBehavior.persistExcludeTools`. */\n persistExcludeTools?: readonly string[]\n /** Destination directory for persisted blobs. See `AgentBehavior.persistDir`. */\n persistDir?: string\n /** Soft byte-cap on the persist dir; oldest blobs are evicted. See `AgentBehavior.persistMaxBytes`. */\n persistMaxBytes?: number\n /**\n * Wall-clock start time of the run (`Date.now()` when `runLoop` was invoked).\n * Used to compute `AgentStats.timeTillFirstTokenMs`.\n */\n runStartMs: number\n /** Session bound to this run, if any. Forwarded into `ToolContext.session`. */\n session?: Session\n /**\n * Explicit read-state map. Forwarded into `ToolContext.readState` and\n * preferred over the `Session`-keyed lookup when both are present.\n * Set by `spawn`'s `shareReadState: true` opt-in (via the child agent's\n * `AgentOptions.readState`) so a sessionless child still feeds the\n * parent's `requireReadBeforeEdit` tracking.\n */\n readState?: ReadStateMap\n /**\n * Parent run id when this run is a subagent. Forwarded into\n * `ToolContext.parentRunId` and every `ToolHookContext.parentRunId`\n * / `McpToolHookContext.parentRunId` fired during the run, so\n * observability consumers can stitch sub-trees to their owners\n * without resolving the run row.\n */\n parentRunId?: string\n /** Subagent depth for this run. Forwarded into `ToolContext.depth`. */\n depth?: number\n /**\n * Per-tool call counter for this run. Mutated by the dispatch path; surfaced\n * (frozen snapshot) on `tool:*` and `turn:after` hook contexts. Counts every\n * call the model dispatched — including ones short-circuited by a `tool:gate`\n * `result` substitution. Excludes calls a gate explicitly `block`ed.\n *\n * Scope: per-`runId`. Resumed sessions start a fresh counter.\n */\n runToolCounts: Record<string, number>\n /** Per-run thinking-budget decay schedule. See `AgentBehavior.thinkingDecay`. */\n thinkingDecay?: AgentBehavior['thinkingDecay']\n /** Fail-fast on pre-send pairing repairs. See `AgentBehavior.strictToolPairing`. */\n strictToolPairing?: boolean\n /** Provider name forwarded into `AgentToolPairingError` for strict-mode throws. */\n providerName?: string\n /**\n * Per-callId {@link AbortController} registry shared with the agent so an\n * external `agent.cancelTool(callId)` can flip a single in-flight tool's\n * signal without aborting the whole run. The loop registers a controller\n * for each call right before gate/execute and removes it in a `finally`\n * — so the map only ever holds *currently dispatching* calls.\n *\n * When undefined, per-call cancellation is unavailable; the loop still\n * honors `ctx.signal` (run / sibling abort) the same way.\n */\n pendingToolCancels?: Map<string, AbortController>\n}\n\nconst IMAGE_OMITTED_MARKER = '[image omitted — model does not support vision]'\n\n/**\n * Canonical tool_result text emitted when a tool call is interrupted by the\n * user mid-flight (Esc / Ctrl-C / external `AbortSignal`). Mirrors Claude\n * Code's `INTERRUPT_MESSAGE_FOR_TOOL_USE` so downstream consumers can pattern\n * match a single string across both harnesses. Always paired with\n * `isError: true` on the wire — the model treats it as a failed call rather\n * than a successful tool response.\n */\nexport const INTERRUPT_MESSAGE_FOR_TOOL_USE = '[Request interrupted by user for tool use]'\n\n/**\n * Canonical tool_result text emitted when a tool call is skipped because a\n * steering message arrived between dispatches inside\n * {@link executeToolBatch}. Distinguished from\n * {@link INTERRUPT_MESSAGE_FOR_TOOL_USE} so consumers can split \"user\n * cancelled\" from \"framework superseded\".\n */\nexport const TOOL_USE_SKIPPED_MESSAGE = '[Tool use skipped — superseded by user message]'\n\n/**\n * Canonical tool_result text emitted when a single tool call is cancelled\n * mid-flight via `agent.cancelTool(callId)` (typically the TUI's\n * \"cancel this tool\" affordance). Distinguished from\n * {@link INTERRUPT_MESSAGE_FOR_TOOL_USE} (run-wide user abort) and\n * {@link TOOL_USE_SKIPPED_MESSAGE} (steered) so the model — and downstream\n * consumers — can tell the three apart by string match.\n *\n * Always paired with `isError: true` on the wire so the model treats the\n * call as failed rather than as a successful response. The remaining tool\n * calls in the batch continue running, in contrast with a full-run abort.\n */\nexport const TOOL_USE_CANCELLED_MESSAGE = '[Tool call cancelled by user]'\n\n/**\n * Sentinel message rejected from the per-call cancellation promise inside\n * {@link executeSingleTool}'s race. Plain-string and module-private — only\n * the surrounding code reads it (via the `perCallAbort.signal.aborted`\n * guard, NOT the message itself), so it just needs to be a stable\n * non-empty identifier the rejection can carry.\n */\nconst CANCELLED_BY_USER_SENTINEL = 'zidane:tool:cancelled-by-user'\n\n/**\n * Compute the effective thinking budget for a given run-relative turn, given\n * the configured decay schedule. Pure helper — exported for tests and so\n * downstream tooling can preview decay curves without spinning up the loop.\n *\n * - No `baseBudget` → returns `undefined`. Decay never invents a budget.\n * - No `decay` → identity (`baseBudget`).\n * - Function form → `decay(turn, baseBudget)`. The return is clamped to\n * `[0, baseBudget]` so a buggy curve can't request *more* budget than the\n * caller explicitly opted into.\n * - Struct form `{ afterTurn, factor, floor }` → for `turn <= afterTurn`,\n * returns `baseBudget`. For `turn > afterTurn`, returns\n * `max(floor, baseBudget * factor^(turn - afterTurn))` clamped to\n * `[floor, baseBudget]`.\n *\n * Result is rounded to the nearest integer (token counts are integers).\n */\nexport function applyThinkingDecay(\n baseBudget: number | undefined,\n decay: AgentBehavior['thinkingDecay'] | undefined,\n turn: number,\n): number | undefined {\n if (typeof baseBudget !== 'number' || baseBudget <= 0)\n return baseBudget\n if (!decay)\n return baseBudget\n\n let raw: number\n if (typeof decay === 'function') {\n raw = decay(turn, baseBudget)\n }\n else {\n if (turn <= decay.afterTurn)\n return baseBudget\n const k = turn - decay.afterTurn\n raw = Math.max(decay.floor, baseBudget * decay.factor ** k)\n }\n\n // Clamp into a sane envelope. Decay reduces; never raises above the caller's\n // explicit budget. NaN collapses to 0 (the curve is broken — silent zero is\n // safer than letting NaN propagate into the provider request). Negative also\n // collapses to 0. `+Infinity` is fine — it clamps to baseBudget below.\n if (Number.isNaN(raw) || raw <= 0)\n return 0\n return Math.round(Math.min(baseBudget, raw))\n}\n\n/** Convert turns to the SessionMessage[] format expected by providers. */\nfunction turnsToMessages(turns: SessionTurn[]): SessionMessage[] {\n return turns\n .filter((t): t is SessionTurn & { role: 'user' | 'assistant' } => t.role !== 'system')\n .map(t => ({ role: t.role, content: t.content }))\n}\n\n/**\n * Wire-level cutoff for {@link SessionContentBlock}'s `compact-summary`\n * markers.\n *\n * Walks `turns` to find the latest marker block. When found:\n *\n * 1. Turns whose id appears in the marker's `replacesTurnIds` are\n * dropped from the wire-level list (the model sees the summary\n * instead of those turns).\n * 2. The marker turn itself is replaced with a synthesized plain\n * user-text turn whose text is the summary — providers don't know\n * about the `compact-summary` block type, so we always normalize\n * before they see it.\n * 3. The synthetic marker is positioned at the slot of the **first\n * replaced turn**, so the summary appears CHRONOLOGICALLY where\n * the content it summarizes used to live. This keeps the wire-\n * level conversation in correct narrative order:\n * - older unreplaced turns (PTL head-drops) come first\n * - then the synthesized summary\n * - then later unreplaced turns (preserved tail + new prompts)\n * 4. Every other turn is passed through by reference. This includes:\n * - turns BEFORE the marker that aren't in `replacesTurnIds`\n * (e.g. head turns dropped by `compactConversation`'s PTL retry\n * path — the harness keeps them visible to the model verbatim\n * because the summary doesn't describe them)\n * - turns AFTER the marker (preserved tail + any new prompts the\n * user appended after compaction)\n *\n * If no marker exists, the input array is returned unchanged.\n *\n * Only the latest `compact-summary` block is honored — sessions\n * compacted twice cascade only if the newer marker's `replacesTurnIds`\n * explicitly subsumes the older marker's id (which the runner does\n * when re-compacting because its `turnsToMessages` inlines older\n * markers into the summary scope).\n *\n * Note on role-alternation: when the marker is immediately followed by\n * a new user turn (the common case — user submits a prompt right after\n * compacting), the wire output ends in `[…, synthetic_user, new_user]`.\n * Anthropic folds adjacent same-role messages server-side; other\n * providers tolerate them via their own normalization passes. The\n * harness does NOT fold them here — keeping them separate preserves\n * the per-turn `turnId` tags that the TUI's select-turn mode relies on.\n *\n * Pure: a fresh array is returned only when a marker is found. Input\n * turns are never mutated; the synthetic marker is the only new\n * `SessionTurn` allocated.\n */\nexport function applyCompactSummaryCutoff(turns: SessionTurn[]): SessionTurn[] {\n if (turns.length === 0)\n return turns\n\n // Find the latest marker turn (scan backward — only the most-recent\n // compaction boundary is honored).\n let markerIdx = -1\n let markerSummary = ''\n let markerReplacesIds: readonly string[] = []\n for (let i = turns.length - 1; i >= 0; i--) {\n const block = turns[i].content.find(b => b.type === 'compact-summary')\n if (block && block.type === 'compact-summary') {\n markerIdx = i\n markerSummary = block.summary\n markerReplacesIds = block.replacesTurnIds\n break\n }\n }\n if (markerIdx < 0)\n return turns\n\n const markerTurn = turns[markerIdx]\n const synthetic: SessionTurn = {\n id: markerTurn.id,\n role: 'user',\n content: [{ type: 'text', text: markerSummary }],\n createdAt: markerTurn.createdAt,\n ...(markerTurn.runId ? { runId: markerTurn.runId } : {}),\n }\n\n const replacedSet = new Set(markerReplacesIds)\n const out: SessionTurn[] = []\n let syntheticInserted = false\n\n for (let i = 0; i < turns.length; i++) {\n if (i === markerIdx) {\n // The marker turn never passes through verbatim — its rewritten\n // form (the `synthetic` turn) is inserted at the first-replaced\n // slot below. Skipping here ensures we never emit it twice.\n continue\n }\n if (replacedSet.has(turns[i].id)) {\n // First replaced turn becomes the synthetic's anchor — that's\n // where the summary lands chronologically.\n if (!syntheticInserted) {\n out.push(synthetic)\n syntheticInserted = true\n }\n continue\n }\n out.push(turns[i])\n }\n\n // Defensive fallback: marker has empty `replacesTurnIds` (or every id\n // pointed at turns that don't exist). The summary is a no-op boundary\n // but we still want it to appear at the marker's original chronological\n // slot. Count how many non-elided turns came before the marker and\n // splice the synthetic in at that offset so the narrative order holds.\n if (!syntheticInserted) {\n let insertAt = 0\n for (let i = 0; i < markerIdx; i++) {\n if (!replacedSet.has(turns[i].id))\n insertAt++\n }\n out.splice(insertAt, 0, synthetic)\n }\n\n return out\n}\n\n/**\n * Flatten image blocks inside previously-stored `tool_result` outputs to text\n * markers when the provider reports no vision capability. This handles the\n * session-resume / mid-session-provider-switch case:\n *\n * - A session was produced with a vision-capable provider (e.g. Claude) and\n * contains structured tool_result content with images.\n * - The agent resumes with a text-only provider (e.g. Cerebras).\n * - Without this pass, the persisted image blocks would re-encode via the\n * provider's wire format and leak base64 payloads into a non-vision model's\n * context.\n *\n * Scope: stored tool_result outputs only. User-authored image blocks are left\n * alone — those are consumer-controlled and the consumer is responsible for\n * matching prompt parts to the provider's capabilities.\n */\nconst COMPACTION_STUB = '[…elided by client-side tail compaction; ask the user or re-run the tool to retrieve.]'\n\n/**\n * Tail-compaction for non-Anthropic providers: when the cumulative byte size\n * of `tool_result` content across the wire-level message list exceeds\n * `threshold`, replace older `tool_result` outputs with a short stub. The\n * newest `keepTurns` messages (user/assistant alike) are left untouched so\n * the model retains the freshest tool context.\n *\n * Only `tool_result` blocks are touched — text and image blocks pass through\n * unchanged. Mutates a shallow-cloned message array; original `messages` is\n * not modified.\n *\n * For Anthropic users, prefer the server-side `context-management-2025-06-27`\n * beta (token-accurate, no client-side approximation). This function is the\n * client-side fallback for OpenAI-compatible / OpenRouter / Cerebras runs\n * against OSS models that lack a server-side equivalent.\n */\nexport function applyTailCompaction(\n messages: SessionMessage[],\n threshold: number,\n keepTurns: number,\n): SessionMessage[] {\n if (messages.length === 0)\n return messages\n\n let totalBytes = 0\n for (const msg of messages) {\n for (const block of msg.content) {\n if (block.type === 'tool_result')\n totalBytes += toolOutputByteLength(block.output)\n }\n }\n if (totalBytes <= threshold)\n return messages\n\n // Index range eligible for elision: everything before the trailing\n // `keepTurns` messages. Negative result means there's nothing old enough\n // to elide — bail out.\n const keep = Math.max(0, keepTurns)\n const cutoff = messages.length - keep\n if (cutoff <= 0)\n return messages\n\n let changed = false\n const out = messages.slice()\n for (let i = 0; i < cutoff; i++) {\n const msg = out[i]\n let msgChanged = false\n const newContent = msg.content.map((block) => {\n if (block.type !== 'tool_result')\n return block\n // Already a short string — nothing to compact.\n const existingBytes = toolOutputByteLength(block.output)\n if (existingBytes <= COMPACTION_STUB.length)\n return block\n // Already a disk-persisted stub (~2 KiB carrying preview + path).\n // Replacing it with COMPACTION_STUB would drop the path attribute,\n // stranding the on-disk blob: still on disk until session delete,\n // but no longer reachable from the conversation. Saving ~1.5 KiB\n // by overwriting isn't worth losing the reference.\n if (typeof block.output === 'string' && block.output.startsWith(PERSISTED_STUB_PREFIX))\n return block\n msgChanged = true\n changed = true\n return { ...block, output: COMPACTION_STUB }\n })\n if (msgChanged)\n out[i] = { ...msg, content: newContent }\n }\n return changed ? out : messages\n}\n\n/**\n * Replace `read_file` `tool_result` blocks with a short stub when a later\n * successful `edit` / `multi_edit` / `write_file` modified the same path.\n *\n * Eliminates the common waste pattern where the model carries the pre-edit\n * file body forward across many turns. Operates on the wire-level message\n * list only — the persisted session keeps the original content.\n *\n * Detection is conservative: success is gated on the corresponding\n * tool_result starting with `Edited ` (edit / multi_edit) or `Created ` /\n * `Updated ` (write_file). Failed edits and `No change needed` writes do\n * NOT invalidate prior reads — the file content is still what the read\n * returned.\n *\n * Pure function, exported for tests and so downstream tooling can preview\n * elision without spinning up the loop.\n */\nexport const STALE_READ_STUB = '[…elided: file edited later in this run; re-read if still needed.]'\n\nexport interface StaleReadElisionResult {\n messages: SessionMessage[]\n /**\n * Paths whose reads were stubbed this pass. The caller invalidates the\n * matching read-state entries so the next `read_file` misses its own\n * dedup (which would otherwise return the \"unchanged\" replay stub — see\n * `src/tools/read-file.ts`) and emits fresh content; the next edit's\n * read-before-edit guard fails so the model re-reads before re-editing.\n * Without this, both stubs combine to leave the model with zero visible\n * content for a path it has both read and edited.\n */\n elidedPaths: string[]\n}\n\nexport function applyStaleReadElision(messages: SessionMessage[]): StaleReadElisionResult {\n if (messages.length === 0)\n return { messages, elidedPaths: [] }\n\n // Map call_id → tool_result string (for success-marker checks).\n const resultByCallId = new Map<string, string>()\n for (const msg of messages) {\n for (const block of msg.content) {\n if (block.type === 'tool_result' && typeof block.output === 'string')\n resultByCallId.set(block.callId, block.output)\n }\n }\n\n // For each path, the highest message index where a successful mutation\n // landed. `read_file` tool_results whose corresponding read happened\n // BEFORE that index (for the same path) are stale.\n const maxMutationIdxByPath = new Map<string, number>()\n // call_id → { path, msgIdx } for read_file calls.\n const readCallInfo = new Map<string, { path: string, msgIdx: number }>()\n\n for (let i = 0; i < messages.length; i++) {\n for (const block of messages[i].content) {\n if (block.type !== 'tool_call')\n continue\n const path = (block.input as { path?: unknown }).path\n if (typeof path !== 'string')\n continue\n\n if (block.name === 'read_file') {\n readCallInfo.set(block.id, { path, msgIdx: i })\n continue\n }\n\n const isEdit = block.name === 'edit' || block.name === 'multi_edit'\n const isWrite = block.name === 'write_file'\n if (!isEdit && !isWrite)\n continue\n\n const result = resultByCallId.get(block.id)\n if (typeof result !== 'string')\n continue\n\n // Success markers — see edit.ts (`Edited ${target}: replaced …`),\n // multi-edit.ts (`Edited ${target}: applied …`), write-file.ts\n // (`Created … (N bytes)` / `Updated … (N bytes)`). The TUI's\n // `tool:transform` hook may append an `<edit-outcomes>` annotation\n // block to multi_edit results, but the prefix is preserved.\n const succeeded = isEdit\n ? result.startsWith('Edited ')\n : (result.startsWith('Created ') || result.startsWith('Updated '))\n if (!succeeded)\n continue\n\n const prior = maxMutationIdxByPath.get(path)\n if (prior === undefined || i > prior)\n maxMutationIdxByPath.set(path, i)\n }\n }\n\n if (maxMutationIdxByPath.size === 0)\n return { messages, elidedPaths: [] }\n\n // call_ids whose read happened before that path's last successful mutation.\n const staleCallIds = new Set<string>()\n const elidedPathSet = new Set<string>()\n for (const [callId, info] of readCallInfo) {\n const lastMutationIdx = maxMutationIdxByPath.get(info.path)\n if (typeof lastMutationIdx === 'number' && info.msgIdx < lastMutationIdx) {\n staleCallIds.add(callId)\n elidedPathSet.add(info.path)\n }\n }\n\n if (staleCallIds.size === 0)\n return { messages, elidedPaths: [] }\n\n // Paths that ALSO have a non-stale read (i.e. a read whose msgIdx\n // is at or after the path's last mutation). These shouldn't be\n // invalidated by the caller: the fresh read already populated the\n // read-state entry with the post-mutation hash, and invalidating\n // would nuke it — making the next edit's `requireReadBeforeEdit`\n // gate fire \"has not been read\" on a file the model demonstrably\n // re-read after the previous edit. The wire-level stub for the\n // stale read still rides; only the in-memory tracking is kept.\n const pathsWithFreshRead = new Set<string>()\n for (const [callId, info] of readCallInfo) {\n if (!staleCallIds.has(callId))\n pathsWithFreshRead.add(info.path)\n }\n\n let changed = false\n const out = messages.slice()\n for (let i = 0; i < out.length; i++) {\n const msg = out[i]\n let msgChanged = false\n const newContent = msg.content.map((block) => {\n if (block.type !== 'tool_result' || !staleCallIds.has(block.callId))\n return block\n // Idempotent: a second pass over an already-elided message must not\n // bump `changed`, otherwise the function would always allocate a new\n // array (no-op-but-not-stable). Match the exact stub string.\n if (block.output === STALE_READ_STUB)\n return block\n // Mirror of the `applyTailCompaction` guard at the top of this\n // file: a `<persisted-output …>` stub carries the path to the\n // on-disk blob. Replacing it with `STALE_READ_STUB` would drop\n // the path attribute, stranding the blob (still on disk until\n // session delete, but no longer reachable from the conversation).\n // The model already gets the staleness signal via the next\n // `read_file` call missing the read-state dedup; preserving the\n // stub keeps the persistence pointer intact.\n if (typeof block.output === 'string' && block.output.startsWith(PERSISTED_STUB_PREFIX))\n return block\n // Structured outputs (image reads via `read_file`) are intentionally\n // also collapsed to the string stub: the bytes ARE stale once the\n // path was overwritten, the marker explicitly tells the model to\n // re-read, and `output: string | ToolResultContent[]` accepts a\n // string. Downstream image-flattening for non-vision providers is\n // a no-op on strings.\n msgChanged = true\n changed = true\n return { ...block, output: STALE_READ_STUB }\n })\n if (msgChanged)\n out[i] = { ...msg, content: newContent }\n }\n return {\n messages: changed ? out : messages,\n elidedPaths: [...elidedPathSet].filter(p => !pathsWithFreshRead.has(p)),\n }\n}\n\n/**\n * Drop read-state entries for paths whose reads got elided. Keys are\n * canonical absolute paths produced by `readStateKey(cwd, path)` (see\n * `src/tools/read-state.ts`); the loop has `ctx.handle.cwd` in scope,\n * so we re-derive each elided path's canonical key and delete by\n * direct lookup. No suffix matching, no cross-cwd ambiguity.\n *\n * Resolves the map via `resolveReadStateMap` so a child agent running\n * with a parent's shared map (via `shareReadState`) invalidates the\n * shared entries too — otherwise a child's `read → edit → re-read`\n * would dedup-hit on the now-stale parent entry.\n */\nfunction invalidateReadStateForElidedPaths(\n ctx: { session?: Session, readState?: ReadStateMap },\n cwd: string,\n elidedPaths: readonly string[],\n): void {\n if (elidedPaths.length === 0)\n return\n const readState = resolveReadStateMap(ctx)\n if (!readState || readState.size === 0)\n return\n for (const p of elidedPaths)\n readState.delete(readStateKey(cwd, p))\n}\n\n/**\n * Run {@link ensureToolResultPairing} with the loop's hook + strict-mode\n * context plugged in. Centralized so the pre-send path and the schema-\n * enforcement path share identical telemetry + throw semantics.\n *\n * The captured `repairs` array is what `AgentToolPairingError` carries on\n * strict-mode throws, and it's what `pairing:repair` fires from. Hook\n * notifications run AFTER the pass completes so they don't interfere with\n * the synchronous walk — and we drop them on the floor in strict mode (the\n * throw is more informative than a fire-and-forget log).\n */\nfunction applyPairingRepair(\n ctx: LoopContext,\n messages: SessionMessage[],\n turnId: string,\n): SessionMessage[] {\n const repairs: PairingRepair[] = []\n const repaired = ensureToolResultPairing(messages, {\n onRepair: repair => repairs.push(repair),\n })\n\n if (repairs.length === 0)\n return repaired\n\n if (ctx.strictToolPairing) {\n throw new AgentToolPairingError({\n message:\n `Tool pairing corruption detected (${repairs.length} repair${repairs.length === 1 ? '' : 's'}); `\n + `strict mode is on so the request was not sent.`,\n ...(ctx.providerName ? { provider: ctx.providerName } : {}),\n repairs,\n })\n }\n\n for (const repair of repairs) {\n void ctx.hooks.callHook('pairing:repair', { ...repair, turnId })\n }\n\n return repaired\n}\n\nfunction sanitizeStoredToolResults(\n provider: Provider,\n messages: SessionMessage[],\n): SessionMessage[] {\n if (provider.meta.capabilities?.vision !== false)\n return messages\n\n return messages.map((msg) => {\n let changed = false\n const newContent = msg.content.map((block) => {\n if (block.type !== 'tool_result' || typeof block.output === 'string')\n return block\n changed = true\n const flattened = block.output\n .map(b => b.type === 'image' ? IMAGE_OMITTED_MARKER : b.text)\n .join('\\n')\n return { ...block, output: flattened }\n })\n return changed ? { ...msg, content: newContent } : msg\n })\n}\n\nexport async function runLoop(ctx: LoopContext): Promise<AgentStats> {\n let totalIn = 0\n let totalOut = 0\n let totalCacheRead = 0\n let totalCacheCreation = 0\n const turnUsages: TurnUsage[] = []\n const startTime = Date.now()\n // Default to no cap — runs are bounded by `result.ended` and the abort\n // signal. Callers wanting a runaway-loop safety net set `behavior.maxTurns`.\n const maxTurns = ctx.maxTurns ?? Number.POSITIVE_INFINITY\n let turnsCompleted = 0\n\n // Track time-to-first-token across the entire run. Earliest of the first\n // stream:text, stream:thinking, or tool:before event latches the value.\n const ttft = { mark: undefined as number | undefined }\n const markTtft = () => {\n if (ttft.mark === undefined)\n ttft.mark = Date.now() - ctx.runStartMs\n }\n const unregisterTtftText = ctx.hooks.hook('stream:text', markTtft)\n const unregisterTtftThinking = ctx.hooks.hook('stream:thinking', markTtft)\n const unregisterTtftTool = ctx.hooks.hook('tool:before', markTtft)\n\n try {\n for (let turn = 0; turn < maxTurns; turn++) {\n if (ctx.signal.aborted) {\n await ctx.hooks.callHook('agent:abort', {})\n break\n }\n\n // Pass the run-cumulative usage state into the turn so its\n // `turn:after` hook can carry `cumulativeUsage` (priorUsage + this\n // turn's usage) without consumers re-accumulating. Includes the\n // current turn count (1-indexed including this one) so dashboards\n // can plot per-turn deltas without an external counter.\n const result = await executeTurn(ctx, turn, {\n input: totalIn,\n output: totalOut,\n cacheRead: totalCacheRead,\n cacheCreation: totalCacheCreation,\n priorCost: turnUsages.reduce((s, t) => s + (t.cost ?? 0), 0),\n priorTurns: turnsCompleted,\n })\n turnsCompleted = turn + 1\n\n totalIn += result.usage.input\n totalOut += result.usage.output\n totalCacheRead += result.usage.cacheRead ?? 0\n totalCacheCreation += result.usage.cacheCreation ?? 0\n turnUsages.push(result.usage)\n\n await ctx.hooks.callHook('usage', { turn, turnId: result.turnId, usage: result.usage, totalIn, totalOut })\n\n // Check abort after turn completes\n if (ctx.signal.aborted) {\n await ctx.hooks.callHook('agent:abort', {})\n break\n }\n\n // Run-level budget circuit breaker. Post-turn so the breaching\n // turn's usage is visible to consumers via `turn:after` + `usage`\n // hooks before the throw lands — a host can log the spend that\n // tripped the cap and then handle the typed error without scraping\n // a stats blob from a half-built `AgentStats`.\n //\n // Token ledger here sums `input + output` only. Cache reads/creates\n // are billed at a discount; including them at par would shift the\n // semantic away from \"what does this run cost in tokens\" toward\n // \"how much context did we shovel through\", which is rarely the\n // operator's actual concern. Cost path uses provider-reported\n // numbers directly so the discount is already baked in.\n const breach = checkRunBudget({\n maxCostUsd: ctx.maxCostUsd,\n maxTotalTokens: ctx.maxTotalTokens,\n cost: turnUsages.reduce((s, t) => s + (t.cost ?? 0), 0),\n tokens: totalIn + totalOut,\n })\n if (breach) {\n throw new AgentBudgetExceededError(breach)\n }\n\n // Check steering queue after tool execution\n if (ctx.steeringQueue.length > 0) {\n const steerMsg = ctx.steeringQueue.shift()!\n await ctx.hooks.callHook('steer:inject', { message: steerMsg })\n const steerUserMsg = ctx.provider.userMessage(steerMsg)\n ctx.turns.push({\n id: await ctx.generateTurnId(),\n runId: ctx.runId,\n role: steerUserMsg.role,\n content: steerUserMsg.content,\n createdAt: await ctx.clock.now(),\n })\n continue\n }\n\n if (result.ended) {\n // Check follow-up queue before finishing\n if (ctx.followUpQueue.length > 0) {\n const followUp = ctx.followUpQueue.shift()!\n await ctx.hooks.callHook('steer:inject', { message: followUp })\n const followUpMsg = ctx.provider.userMessage(followUp)\n ctx.turns.push({\n id: await ctx.generateTurnId(),\n runId: ctx.runId,\n role: followUpMsg.role,\n content: followUpMsg.content,\n createdAt: await ctx.clock.now(),\n })\n continue\n }\n\n return {\n totalIn,\n totalOut,\n totalCacheRead,\n totalCacheCreation,\n turns: turn + 1,\n elapsed: Date.now() - startTime,\n turnUsage: turnUsages,\n output: result.output,\n ...(ttft.mark !== undefined ? { timeTillFirstTokenMs: ttft.mark } : {}),\n }\n }\n }\n\n return {\n totalIn,\n totalOut,\n totalCacheRead,\n totalCacheCreation,\n turns: turnsCompleted,\n elapsed: Date.now() - startTime,\n turnUsage: turnUsages,\n ...(ttft.mark !== undefined ? { timeTillFirstTokenMs: ttft.mark } : {}),\n }\n }\n finally {\n unregisterTtftText()\n unregisterTtftThinking()\n unregisterTtftTool()\n }\n}\n\n// ---------------------------------------------------------------------------\n// Single turn\n// ---------------------------------------------------------------------------\n\ninterface TurnResult {\n ended: boolean\n turnId: string\n usage: TurnUsage\n output?: Record<string, unknown>\n}\n\n/**\n * Wrap a caught provider error in the matching typed error class.\n *\n * Uses the provider's `classifyError` seam when implemented; otherwise falls back\n * to wrapping in `AgentProviderError`. Abort signals always produce `AgentAbortedError`\n * regardless of the provider classification.\n */\n/**\n * Pure predicate for the run-level budget circuit breaker. Returns a\n * structured breach descriptor when EITHER the cost or token ledger has\n * crossed its configured ceiling, otherwise `null`.\n *\n * Order of checks: cost first when both are set, because cost is the\n * direct dollar accounting operators set ceilings against — emitting a\n * `'tokens'` breach when both knobs would have tripped on the same turn\n * would hide the more user-actionable signal.\n *\n * `undefined` / `0` / non-positive / non-finite ceilings disable the\n * corresponding axis without protest, matching the established\n * `behavior.toolOutputBudget` semantic. Exported only for tests; the\n * loop is the sole production caller.\n */\nexport function checkRunBudget(input: {\n maxCostUsd?: number\n maxTotalTokens?: number\n cost: number\n tokens: number\n}): { limit: 'cost' | 'tokens', limitValue: number, actualValue: number } | null {\n const { maxCostUsd, maxTotalTokens, cost, tokens } = input\n if (typeof maxCostUsd === 'number' && Number.isFinite(maxCostUsd) && maxCostUsd > 0 && cost >= maxCostUsd) {\n return { limit: 'cost', limitValue: maxCostUsd, actualValue: cost }\n }\n if (typeof maxTotalTokens === 'number' && Number.isFinite(maxTotalTokens) && maxTotalTokens > 0 && tokens >= maxTotalTokens) {\n return { limit: 'tokens', limitValue: maxTotalTokens, actualValue: tokens }\n }\n return null\n}\n\nfunction wrapProviderError(err: unknown, ctx: LoopContext): Error {\n if (ctx.signal.aborted || (err instanceof Error && err.name === 'AbortError'))\n return new AgentAbortedError('Agent run aborted', { cause: err })\n\n const classification = ctx.provider.classifyError?.(err)\n if (classification)\n return toTypedError(classification, ctx.provider.name, err)\n\n return new AgentProviderError(errorMessage(err), { provider: ctx.provider.name, cause: err })\n}\n\n/** Max bytes of provider error text inlined into the assistant turn placeholder. */\nconst ERROR_PLACEHOLDER_MAX = 280\n\n/**\n * Build the assistant-turn placeholder text when the provider throws before\n * streaming any output. Inlines the underlying error message (truncated and\n * stripped of stack-like newlines) so the persisted turn carries a useful\n * diagnostic — the human reading the transcript sees the failure mode\n * without having to attach a debugger, and `tool_search` schema rejections\n * become self-explanatory.\n *\n * The bracketed `[✗ Streaming failed: ...]` shape preserves the prior\n * format that hosts may pattern-match on while adding the new payload\n * suffix. Falls back to the original generic placeholder when no message\n * can be extracted.\n */\nfunction buildStreamErrorPlaceholder(err: unknown): string {\n const raw = errorMessage(err).trim()\n if (raw.length === 0)\n return '[✗ Streaming failed before any output.]'\n // Collapse newlines so the placeholder stays on one transcript line; a\n // multi-line provider stack trace would otherwise wreck the model's\n // continuation context.\n const oneLine = raw.replace(/\\s+/g, ' ')\n const trimmed = oneLine.length > ERROR_PLACEHOLDER_MAX\n ? `${oneLine.slice(0, ERROR_PLACEHOLDER_MAX - 1).trimEnd()}…`\n : oneLine\n return `[✗ Streaming failed before any output: ${trimmed}]`\n}\n\ninterface RunUsageSnapshot {\n input: number\n output: number\n cacheRead: number\n cacheCreation: number\n priorCost: number\n priorTurns: number\n}\n\nfunction buildCumulativeUsage(prior: RunUsageSnapshot, turnUsage: TurnUsage): Readonly<{\n input: number\n output: number\n cacheRead: number\n cacheCreation: number\n cost?: number\n turns: number\n}> {\n const cost = prior.priorCost + (turnUsage.cost ?? 0)\n return Object.freeze({\n input: prior.input + turnUsage.input,\n output: prior.output + turnUsage.output,\n cacheRead: prior.cacheRead + (turnUsage.cacheRead ?? 0),\n cacheCreation: prior.cacheCreation + (turnUsage.cacheCreation ?? 0),\n ...(cost > 0 ? { cost } : {}),\n turns: prior.priorTurns + 1,\n })\n}\n\n/**\n * Best-effort extraction of `statusCode` / `requestId` from native provider\n * SDK exceptions, attached to `stream:error` ctx so observability handlers\n * don't have to walk `cause` chains. Recognized shapes:\n *\n * - Anthropic SDK `APIError` (`status`, `headers['request-id']`)\n * - OpenAI SDK error (`status`, `headers['x-request-id']`)\n * - OpenRouter / OpenAI-compat HTTP errors (`status`)\n * - Cerebras (`status`)\n *\n * Silent no-op for arbitrary `Error`s and primitives — the fields stay\n * undefined and the raw `err` is still forwarded for handlers that need\n * full context.\n */\nfunction extractStreamErrorMeta(err: unknown): { statusCode?: number, requestId?: string } {\n if (!err || typeof err !== 'object')\n return {}\n const e = err as Record<string, unknown>\n const out: { statusCode?: number, requestId?: string } = {}\n\n const statusCandidate = typeof e.status === 'number'\n ? e.status\n : typeof e.statusCode === 'number'\n ? e.statusCode\n : undefined\n if (typeof statusCandidate === 'number' && Number.isFinite(statusCandidate))\n out.statusCode = statusCandidate\n\n if (typeof e.requestId === 'string') {\n out.requestId = e.requestId\n }\n else {\n const headers = e.headers\n if (headers && typeof headers === 'object') {\n const h = headers as Record<string, unknown>\n const candidate = h['request-id'] ?? h['x-request-id'] ?? h.requestId\n if (typeof candidate === 'string')\n out.requestId = candidate\n }\n }\n return out\n}\n\nasync function executeTurn(\n ctx: LoopContext,\n turn: number,\n priorUsage: RunUsageSnapshot,\n): Promise<TurnResult> {\n // Generate turn ID early so streaming hooks have access to it\n const turnId = await ctx.generateTurnId()\n\n // Build provider-bound messages: apply outbound alias rewrite so the LLM sees\n // tool_call blocks with their wire (aliased) names. Canonical names stay in ctx.turns.\n //\n // Order of operations is load-bearing:\n // 1. `applyCompactSummaryCutoff` first — turns before the latest\n // `compact-summary` marker never reach the provider, so every\n // transform below sees a smaller, marker-free slice. Operating on\n // `SessionTurn[]` lets the cutoff identify the marker block type\n // without leaking the type into the wire-level path.\n // 2. `turnsToMessages` second — converts the post-cutoff turns into\n // the `SessionMessage[]` shape providers expect.\n // 3. `applyStaleReadElision` walks tool_calls + tool_results to\n // replace pre-edit `read_file` outputs. Runs BEFORE alias rewrite\n // so the function never has to learn about the alias map.\n // 4. Alias rewrite, image sanitization, tail compaction follow.\n const turnsAfterCutoff = applyCompactSummaryCutoff(ctx.turns)\n let canonicalMessages = turnsToMessages(turnsAfterCutoff)\n\n if (ctx.elideStaleReads === true) {\n const elision = applyStaleReadElision(canonicalMessages)\n canonicalMessages = elision.messages\n // Force the next read_file / edit on the elided paths to start from\n // fresh content — the model can no longer see the original read in\n // the message history, so the cheap dedup stub would mislead it.\n invalidateReadStateForElidedPaths(ctx, ctx.handle.cwd, elision.elidedPaths)\n }\n\n const wireMessages = rewriteMessagesToWire(canonicalMessages, ctx.aliasMaps)\n // Flatten any stored structured tool_result images to text markers when the\n // provider is non-vision. No-op on vision-capable providers and on sessions\n // without structured outputs. See `sanitizeStoredToolResults` for scope.\n let sanitizedMessages = sanitizeStoredToolResults(ctx.provider, wireMessages)\n\n // Client-side tail compaction. Non-Anthropic OSS-model fallback for what the\n // `context-management-2025-06-27` beta gives Anthropic users server-side.\n // Skipped when off, when no threshold-bearing context, or when the total is\n // under the threshold.\n if (ctx.compactStrategy === 'tail') {\n const threshold = typeof ctx.compactThreshold === 'number' && ctx.compactThreshold > 0\n ? ctx.compactThreshold\n : 131_072\n const keep = typeof ctx.compactKeepTurns === 'number' && ctx.compactKeepTurns >= 0\n ? ctx.compactKeepTurns\n : 4\n sanitizedMessages = applyTailCompaction(sanitizedMessages, threshold, keep)\n }\n\n const effectiveThinkingBudget = applyThinkingDecay(ctx.thinkingBudget, ctx.thinkingDecay, turn)\n\n // Lazy tool disclosure — when active, rebuild the tool list each turn so\n // tools surfaced through `tool_search` since the last provider call land\n // in this request. The set only grows within a run (entries are appended,\n // never reordered or removed), so the prefix-cache breakpoint advances\n // monotonically rather than thrashing.\n const formattedTools = ctx.rebuildFormattedTools\n ? ctx.rebuildFormattedTools()\n : ctx.formattedTools\n\n const streamOptions: StreamOptions = {\n model: ctx.model,\n system: ctx.system,\n tools: formattedTools,\n messages: sanitizedMessages,\n maxTokens: ctx.maxTokens ?? 16384,\n thinking: ctx.thinking,\n thinkingBudget: effectiveThinkingBudget,\n cache: ctx.cache ?? true,\n signal: ctx.signal,\n }\n\n // Context transform hook — lets consumers prune/modify messages before LLM call\n // ctx.messages here is the wire-level view (aliases applied) so transformations are\n // consistent with what the provider actually sees.\n const transformCtx = { messages: streamOptions.messages }\n await ctx.hooks.callHook('context:transform', transformCtx)\n streamOptions.messages = transformCtx.messages\n\n // Last line of defense before the wire: repair any `tool_use` ↔\n // `tool_result` adjacency violations. Anthropic 400s on orphans loudly\n // (`'tool_use' ids were found without 'tool_result' blocks immediately\n // after`, `tool_result must be preceded by a tool_call with the same\n // toolCallId`); OpenAI is more forgiving but still rejects most\n // mismatches. Common sources: interrupted runs persisted mid-pair, the\n // schema-enforcement `__output__` tail, `context:transform` hooks that\n // prune messages without preserving pairing, duplicate tool_use ids\n // surfaced by buggy providers, compaction passes that strand assistant\n // half of a pair.\n //\n // Strict mode (`behavior.strictToolPairing`) collects every repair and\n // throws `AgentToolPairingError` instead of patching the wire — for\n // training-data collectors that need to reject corrupted transcripts\n // rather than ship synthetic placeholders.\n streamOptions.messages = applyPairingRepair(ctx, streamOptions.messages, turnId)\n\n // Final defense: guarantee the wire ends with a user message. Pair-repair\n // mode 2 already appends a synthetic `tool_result` user turn for orphan\n // `tool_use`s, but a *plain* assistant text tail (no tool_use blocks)\n // slips through — typically when a `context:transform` hook trims the\n // last user turn. Models that don't support assistant prefill (opus 4.7,\n // o-series translation layers) 400 with \"This model does not support\n // assistant message prefill. The conversation must end with a user\n // message.\" Idempotent on a healthy tail.\n streamOptions.messages = ensureEndsWithUserMessage(streamOptions.messages, ctx.provider)\n\n // System transform hook — runtime-derived system-prompt sections. Fires\n // after `context:transform` so handlers can branch on the wire-level\n // message list. Cache breakpoints are applied inside the provider after\n // this point, so mutations land in the cache key naturally.\n const systemCtx: {\n system: string\n messages: readonly SessionMessage[]\n turn: number\n turnId: string\n session?: Session\n } = {\n system: streamOptions.system,\n messages: streamOptions.messages,\n turn,\n turnId,\n ...(ctx.session ? { session: ctx.session } : {}),\n }\n await ctx.hooks.callHook('system:transform', systemCtx)\n streamOptions.system = systemCtx.system\n\n await ctx.hooks.callHook('turn:before', { turn, turnId, options: streamOptions })\n\n let currentText = ''\n let currentThinking = ''\n // Per-turn TTFT — earliest of first text/thinking delta. Tool-first turns\n // (no surface stream content) inherit `undefined` and let the metric\n // histogram skip them rather than reporting `0` (provider returned before\n // anything observable streamed; a no-op turn shouldn't pollute the bucket).\n const streamStartedAt = Date.now()\n let turnTtftMs: number | undefined\n const markTurnTtft = (): void => {\n if (turnTtftMs === undefined)\n turnTtftMs = Date.now() - streamStartedAt\n }\n await ctx.hooks.callHook('stream:start', { turnId, startedAt: streamStartedAt })\n\n let result\n try {\n result = await ctx.provider.stream(\n streamOptions,\n {\n onText(delta) {\n markTurnTtft()\n currentText += delta\n ctx.hooks.callHook('stream:text', { delta, text: currentText, turnId })\n },\n onThinking(delta) {\n markTurnTtft()\n currentThinking += delta\n ctx.hooks.callHook('stream:thinking', { delta, thinking: currentThinking, turnId })\n },\n onOAuthRefresh(refreshCtx) {\n return ctx.hooks.callHook('oauth:refresh', refreshCtx)\n },\n },\n )\n }\n catch (err) {\n // Ensure turn:after fires even when the provider throws.\n //\n // Persisted turns must always carry at least one content block — both\n // Anthropic and OpenAI reject `content: []` on resume. When the\n // provider blew up before any text streamed, drop in a placeholder\n // text block so the turn round-trips cleanly through any session\n // store and a subsequent `agent.run()` doesn't reject the history.\n //\n // Two distinct placeholders so the transcript reads correctly to\n // both the human and the model:\n // - Abort: user cancelled mid-stream. Friendly + clearly a system\n // marker, so the model reads it as \"the user interrupted\n // me\" rather than \"I produced this content\".\n // - Error: provider threw before any output. The bracketed marker\n // carries the underlying provider message (truncated) so\n // both the transcript and any host UI watching `turns[]`\n // can diagnose without walking the thrown `.cause` chain.\n // Without this, the catch block was effectively swallowing\n // the only evidence of the failure (`AgentProviderError`\n // with the cause is rethrown — but consumers reading the\n // persisted turn never saw the message).\n const wasAborted = ctx.signal.aborted || (err instanceof Error && err.name === 'AbortError')\n const errorUsage: TurnUsage = { input: 0, output: 0 }\n const placeholderText = wasAborted\n ? '[⏹ Streaming was aborted.]'\n : buildStreamErrorPlaceholder(err)\n const errorContent = currentText\n ? [{ type: 'text' as const, text: currentText }]\n : [{ type: 'text' as const, text: placeholderText }]\n const errorTurn: SessionTurn = {\n id: turnId,\n runId: ctx.runId,\n role: 'assistant',\n content: errorContent,\n usage: errorUsage,\n createdAt: await ctx.clock.now(),\n }\n ctx.turns.push(errorTurn)\n // Fire `stream:error` BEFORE the synthetic `turn:after` so observers\n // see the raw provider error first. Aborts are routed through\n // `agent:abort` elsewhere — don't double-fire here.\n if (!wasAborted) {\n const meta = extractStreamErrorMeta(err)\n await ctx.hooks.callHook('stream:error', { err, turnId, ...meta })\n }\n await ctx.hooks.callHook('turn:after', {\n turn,\n turnId,\n usage: errorUsage,\n message: errorTurn,\n toolCounts: { turn: Object.freeze({}), run: Object.freeze({ ...ctx.runToolCounts }) },\n cumulativeUsage: buildCumulativeUsage(priorUsage, errorUsage),\n })\n throw wrapProviderError(err, ctx)\n }\n\n if (currentText) {\n await ctx.hooks.callHook('stream:end', { text: currentText, turnId })\n }\n\n // Tool-only turns (no text / no thinking deltas) still need a TTFT\n // number — the stream returned silently with just tool_use blocks. Use\n // the wall-clock duration of `provider.stream()` as the proxy. Tool\n // dispatch fires `tool:before` later, but that's post-stream and would\n // double-count provider latency.\n if (turnTtftMs === undefined && result.toolCalls.length > 0)\n turnTtftMs = Date.now() - streamStartedAt\n\n // Inbound alias → canonical rewrite. After this, tool calls and assistant\n // content blocks use canonical names everywhere downstream (turns, dispatch, hooks).\n const canonicalToolCalls = result.toolCalls.map(tc => ({\n ...tc,\n name: toCanonicalName(tc.name, ctx.aliasMaps),\n }))\n const canonicalContent = rewriteContentToCanonical(\n result.assistantMessage?.content ?? [],\n ctx.aliasMaps,\n )\n\n // Stamp per-turn TTFT onto the usage record so the assistant turn\n // carries it through session persistence (downstream consumers reading\n // `SessionTurn.usage` for postmortem analysis get it without re-running\n // the loop) and the hook ctx exposes it for metrics histograms.\n if (turnTtftMs !== undefined && result.usage.timeToFirstTokenMs === undefined)\n result.usage.timeToFirstTokenMs = turnTtftMs\n\n // Build the assistant turn and push BEFORE firing turn:after\n const assistantTurn: SessionTurn = {\n id: turnId,\n runId: ctx.runId,\n role: 'assistant',\n content: result.done\n ? (canonicalContent.length > 0 ? canonicalContent : [{ type: 'text', text: currentText }])\n : canonicalContent,\n usage: result.usage,\n createdAt: await ctx.clock.now(),\n }\n ctx.turns.push(assistantTurn)\n\n // Per-turn tool counts: how many of each tool the model emitted in this\n // assistant turn. Counts emissions, not dispatches — `turn:after` fires\n // before tool dispatch, so blocked calls are counted here too. For \"actually\n // dispatched\" cumulative numbers, use `toolCounts.run`.\n const turnCounts: Record<string, number> = {}\n for (const tc of canonicalToolCalls)\n turnCounts[tc.name] = (turnCounts[tc.name] ?? 0) + 1\n\n await ctx.hooks.callHook('turn:after', {\n turn,\n turnId,\n usage: result.usage,\n message: assistantTurn,\n toolCounts: { turn: Object.freeze(turnCounts), run: Object.freeze({ ...ctx.runToolCounts }) },\n cumulativeUsage: buildCumulativeUsage(priorUsage, result.usage),\n })\n\n if (result.done) {\n // Schema enforcement: force one more call with a synthetic output tool\n if (ctx.schema && !ctx.signal.aborted) {\n const outputSpec: ToolSpec = {\n name: '__output__',\n description: 'Return the final structured output matching the required schema.',\n inputSchema: ctx.schema,\n }\n // Apply the same compact-summary cutoff as the main path — without\n // this, a session compacted before schema enforcement would leak\n // the `compact-summary` block type to the provider. Same pairing\n // repair too, so the synthetic `__output__` tool_call doesn't 400\n // on its own (the schema turn legitimately has no matching\n // tool_result; mode 2 inserts one).\n // Same two-pass defense the main wire-send uses (see above): pair-\n // repair fills orphan `tool_use` adjacency; ensureEndsWithUserMessage\n // covers the plain assistant-text-tail case (`result.done` after a\n // text-only assistant turn lands here with an assistant-terminated\n // history — pairing repair can't fix it because pairing is already\n // valid).\n const schemaMessages = ensureEndsWithUserMessage(\n applyPairingRepair(\n ctx,\n rewriteMessagesToWire(\n turnsToMessages(applyCompactSummaryCutoff(ctx.turns)),\n ctx.aliasMaps,\n ),\n turnId,\n ),\n ctx.provider,\n )\n let schemaResult\n try {\n schemaResult = await ctx.provider.stream(\n {\n model: ctx.model,\n system: ctx.system,\n tools: ctx.provider.formatTools([outputSpec]),\n messages: schemaMessages,\n maxTokens: ctx.maxTokens ?? 16384,\n signal: ctx.signal,\n toolChoice: { type: 'tool', name: '__output__' },\n },\n {\n onText: () => {},\n onOAuthRefresh(refreshCtx) {\n return ctx.hooks.callHook('oauth:refresh', refreshCtx)\n },\n },\n )\n }\n catch (err) {\n throw wrapProviderError(err, ctx)\n }\n\n const output = schemaResult.toolCalls.find(tc => tc.name === '__output__')?.input\n\n if (output) {\n await ctx.hooks.callHook('output', { output, schema: ctx.schema })\n }\n\n const schemaTurn: SessionTurn = {\n id: await ctx.generateTurnId(),\n runId: ctx.runId,\n role: 'assistant',\n content: schemaResult.assistantMessage.content,\n usage: schemaResult.usage,\n createdAt: await ctx.clock.now(),\n }\n ctx.turns.push(schemaTurn)\n\n return {\n ended: true,\n turnId,\n usage: {\n input: result.usage.input + schemaResult.usage.input,\n output: result.usage.output + schemaResult.usage.output,\n },\n output,\n }\n }\n\n return { ended: true, turnId, usage: result.usage }\n }\n\n // Pause-turn recovery (Anthropic 4.6+ `pause_turn`): the model stopped\n // mid-turn for a server-side pause but no tools were requested. Push a\n // synthetic continue prompt as a user message so the next turn resumes\n // with valid (non-empty) input — Anthropic rejects empty user content.\n // Gated on `finishReason === 'pause'` to avoid masking provider bugs that\n // legitimately produce zero tool calls and zero text.\n if (canonicalToolCalls.length === 0 && result.usage.finishReason === 'pause') {\n const continueMsg = ctx.provider.userMessage('Please continue.')\n ctx.turns.push({\n id: await ctx.generateTurnId(),\n runId: ctx.runId,\n role: continueMsg.role,\n content: continueMsg.content,\n createdAt: await ctx.clock.now(),\n })\n return { ended: false, turnId, usage: result.usage }\n }\n\n // Execute tool calls (canonical names after inbound rewrite). One\n // unified dispatcher: concurrency-safe tools fan out, unsafe tools\n // act as barriers. See `executeToolBatch` for the contract.\n const toolResults = await executeToolBatch(ctx, canonicalToolCalls, turnId)\n\n // Tool results as a user turn\n const toolResultMsg = ctx.provider.toolResultsMessage(toolResults)\n const toolResultsTurn: SessionTurn = {\n id: await ctx.generateTurnId(),\n runId: ctx.runId,\n role: toolResultMsg.role,\n content: toolResultMsg.content,\n createdAt: await ctx.clock.now(),\n }\n ctx.turns.push(toolResultsTurn)\n\n // Fire `tool-results:after` so persistence layers can durably store the\n // tool_use ↔ tool_result pair together — closes the crash window between\n // `turn:after` (assistant turn persisted) and the next loop iteration's\n // `turn:after` (where these tool results would otherwise piggyback). A\n // process death in that window left the DB with an orphan tool_use that\n // Anthropic rejects on resume.\n await ctx.hooks.callHook('tool-results:after', {\n turn,\n turnId,\n message: toolResultsTurn,\n results: toolResults,\n })\n\n // Enforce per-turn tool-output budget. Sum the post-transform bytes of every\n // tool result; on overshoot, push a synthetic user message that nudges the\n // model toward summarization, and surface the event via `budget:exceeded`.\n //\n // Tools listed in `toolOutputBudgetExcludeTools` are skipped entirely:\n // `tool_search` / `skills_use` exist to LOAD context into the conversation,\n // so counting their bytes against the budget would steer the model away\n // from the very call it needs to make progress (the \"summarize before\n // calling more tools\" message is nonsensical for schema-loading tools).\n // Lookup is canonical-name keyed because `canonicalToolCalls` was already\n // alias-rewritten upstream; map result id → canonical name so the filter\n // works for parallel batches where order in `toolResults` is dispatch\n // order, not call order.\n if (typeof ctx.toolOutputBudget === 'number' && ctx.toolOutputBudget > 0) {\n const excludeSet = ctx.toolOutputBudgetExcludeTools && ctx.toolOutputBudgetExcludeTools.length > 0\n ? new Set(ctx.toolOutputBudgetExcludeTools)\n : undefined\n const nameById = excludeSet\n ? new Map(canonicalToolCalls.map(c => [c.id, c.name]))\n : undefined\n const totalBytes = toolResults.reduce(\n (sum, r) => {\n if (excludeSet && nameById) {\n const name = nameById.get(r.id)\n if (name !== undefined && excludeSet.has(name))\n return sum\n }\n return sum + toolOutputByteLength(r.content)\n },\n 0,\n )\n if (totalBytes > ctx.toolOutputBudget) {\n const warning = `[Tool output budget exceeded: ${totalBytes} bytes returned in this turn (cap: ${ctx.toolOutputBudget}). Summarize the salient findings before calling more tools.]`\n const userMsg = ctx.provider.userMessage(warning)\n ctx.turns.push({\n id: await ctx.generateTurnId(),\n runId: ctx.runId,\n role: userMsg.role,\n content: userMsg.content,\n createdAt: await ctx.clock.now(),\n })\n await ctx.hooks.callHook('budget:exceeded', {\n turn,\n turnId,\n bytes: totalBytes,\n budget: ctx.toolOutputBudget,\n })\n }\n }\n\n return { ended: false, turnId, usage: result.usage }\n}\n\n// ---------------------------------------------------------------------------\n// Tool execution\n// ---------------------------------------------------------------------------\n\n/**\n * Strip image blocks from a tool output when the provider is not vision-capable.\n * Each image is replaced with a short text marker so the model sees an honest\n * \"no image\" signal instead of JSON-stringified base64 it might confabulate over.\n *\n * Returns the output unchanged when:\n * - The provider reports `capabilities.vision: true` (native routing handles it),\n * - The provider omits `capabilities` entirely (we default to vision-capable\n * so third-party providers without the field aren't penalized),\n * - The output is a plain string (no image blocks to strip).\n *\n * With the current `text | image` union, replacing every image with a text marker\n * leaves an all-text array — collapse to a plain string to keep the downstream wire\n * shape as narrow as possible.\n */\nfunction stripImagesForNonVision(\n provider: Provider,\n output: string | ToolResultContent[],\n): string | ToolResultContent[] {\n if (typeof output === 'string')\n return output\n if (provider.meta.capabilities?.vision !== false)\n return output\n\n return output\n .map(b => b.type === 'image' ? IMAGE_OMITTED_MARKER : b.text)\n .join('\\n')\n}\n\n/**\n * Build the per-call base for every `tool:*` hook ctx (and the\n * matching shape for `mcp:tool:*`). Centralized so the `runId` /\n * `parentRunId` / `depth` identity fields land uniformly on every\n * event the loop fires — without one helper they drift across ~14\n * inline construction sites. The returned object IS the\n * {@link ToolHookContext} canonical shape; specialized hook payloads\n * (`gateCtx`, `transformCtx`, etc.) spread it and append their own\n * fields.\n */\nfunction buildToolHookBase(\n ctx: LoopContext,\n turnId: string,\n callId: string,\n name: string,\n displayName: string,\n input: Record<string, unknown>,\n): ToolHookContext {\n return {\n turnId,\n callId,\n name,\n displayName,\n input,\n ...(ctx.runId !== undefined ? { runId: ctx.runId } : {}),\n ...(ctx.parentRunId !== undefined ? { parentRunId: ctx.parentRunId } : {}),\n ...(typeof ctx.depth === 'number' ? { depth: ctx.depth } : {}),\n }\n}\n\nasync function executeSingleTool(\n ctx: LoopContext,\n call: ToolCall,\n turnId: string,\n): Promise<{ result: ToolResult }> {\n const toolDef = ctx.tools[call.name]\n const callId = call.id\n const displayName = toWireName(call.name, ctx.aliasMaps)\n\n // Frozen pre-call snapshot of run-cumulative tool counts. Shared across the\n // gate, tool:before, and tool:after hooks so consumers see a consistent view\n // for the lifecycle of this call. A consumer that wants the post-increment\n // count can add 1 to the entry for `ctx.name`.\n const runToolCounts: Readonly<Record<string, number>> = Object.freeze({ ...ctx.runToolCounts })\n\n // Per-call cancellation slot. Registered ASAP (before the gate) so a user\n // pressing \"cancel this tool\" between `tool:before` and `tool:after` finds\n // a live controller to flip. The `finally` below always removes the entry,\n // so the map only ever holds *currently dispatching* calls — never grows\n // unbounded across a run.\n //\n // The race against this controller's signal lives down in the execute\n // section; gate-blocked / substituted / unknown-tool / invalid-input paths\n // return early without ever reaching it, which is fine — those exits are\n // immediate and the user has nothing to cancel.\n const perCallAbort = new AbortController()\n ctx.pendingToolCancels?.set(callId, perCallAbort)\n try {\n return await runSingleToolDispatch(ctx, call, turnId, {\n toolDef,\n callId,\n displayName,\n runToolCounts,\n perCallAbort,\n })\n }\n finally {\n // Best-effort remove: a concurrent unregister (the agent dropping its\n // map on destroy) is a no-op.\n ctx.pendingToolCancels?.delete(callId)\n }\n}\n\n/**\n * Body of {@link executeSingleTool}. Hoisted into its own function purely so\n * the per-call cancel registration (which spans every exit path) can sit in\n * a tight `try / finally` at the call site. Behavior is unchanged from the\n * pre-cancel implementation aside from the user-cancel branch in the\n * execute body — see {@link TOOL_USE_CANCELLED_MESSAGE}.\n */\nasync function runSingleToolDispatch(\n ctx: LoopContext,\n call: ToolCall,\n turnId: string,\n fixed: {\n toolDef: ToolDef | undefined\n callId: string\n displayName: string\n runToolCounts: Readonly<Record<string, number>>\n perCallAbort: AbortController\n },\n): Promise<{ result: ToolResult }> {\n const { toolDef, callId, displayName, runToolCounts, perCallAbort } = fixed\n\n // Gate hook — handlers can `block` (refuse the call), substitute a `result`\n // (skip execute, send the substitute back as a normal tool_result), or do\n // nothing (tool runs as usual).\n const gateCtx: ToolHookContext & {\n block: boolean\n reason: string\n result?: string | ToolResultContent[]\n runToolCounts: Readonly<Record<string, number>>\n } = {\n ...buildToolHookBase(ctx, turnId, callId, call.name, displayName, call.input),\n block: false,\n reason: 'Tool execution was blocked',\n runToolCounts,\n }\n await ctx.hooks.callHook('tool:gate', gateCtx)\n\n // Conflict resolution: block wins over result. This handles two cases\n // cleanly without forcing every middleware to defensively coordinate:\n //\n // - A consumer hook substitutes a `result` (e.g. dedup cache), then a\n // policy gate (skills allowed-tools, custom security) refuses the call\n // via `block`. The refusal must take precedence — security beats\n // convenience.\n // - A buggy single handler sets both. The call gets blocked; the spurious\n // `result` is dropped silently. The buggy code path can be caught with a\n // tool:gate observability hook downstream of the framework gates.\n if (gateCtx.block) {\n // Blocked calls do not count — the model \"asked\" but the framework refused\n // before any side effect could happen. Treating them as charged would make\n // budget guards self-defeating (block on N triggers Nth call to count).\n //\n // `tool:dispatched` fires with `outcome: 'gate-block'` so consumers\n // wanting symmetric per-call notification (chat-layer transcript\n // rebuilders, downstream message-history consumers) see every refused\n // call. `tool:after` / `tool:transform` deliberately do NOT fire here:\n // preserving their pre-existing \"tool body produced a result\" contract\n // keeps the TUI, tracing-span lifecycle, and budget-byte counters from\n // having to special-case blocked calls.\n //\n // `isError: true` on the returned tool_result so the model treats the\n // refusal as a failed attempt rather than a successful `Blocked` string.\n await fireDispatched(ctx, {\n turnId,\n callId,\n name: call.name,\n displayName,\n input: gateCtx.input,\n outcome: 'gate-block',\n reason: gateCtx.reason,\n runToolCounts,\n })\n return { result: { id: callId, content: `Blocked: ${gateCtx.reason}`, isError: true } }\n }\n\n // The call passed gate; record it in the run counter. Counted once here so\n // `result`-substituted calls still count — the model emitted the call.\n ctx.runToolCounts[call.name] = (ctx.runToolCounts[call.name] ?? 0) + 1\n\n // Z20 substitute path. Skip validate / `tool:before` / execute and emit a\n // synthetic successful `tool_result`. `tool:after` and `tool:transform`\n // still fire so byte-budgeting, telemetry, and post-mutation hooks see\n // the substitute consistently with executed calls. `tool:transform` may\n // still flip the `isError` flag on its way through (e.g. a hook that\n // demotes a cached error reply to a graceful retry hint).\n if (gateCtx.result !== undefined) {\n await fireDispatched(ctx, {\n turnId,\n callId,\n name: call.name,\n displayName,\n input: gateCtx.input,\n outcome: 'gate-substitute',\n runToolCounts,\n })\n const emitted = await emitToolResult(ctx, {\n turnId,\n callId,\n name: call.name,\n displayName,\n input: gateCtx.input,\n output: gateCtx.result,\n isError: false,\n runToolCounts,\n })\n return {\n result: {\n id: callId,\n content: emitted.output,\n ...(emitted.isError ? { isError: true } : {}),\n },\n }\n }\n\n // Input that downstream hooks + execute see, after any tool:gate mutation.\n let effectiveInput = gateCtx.input\n\n if (!toolDef) {\n // Hallucinated tool name (model invented it) or dangling reference (MCP\n // server dropped, alias removed). Give consumers a chance to substitute a\n // friendly response or suppress the companion `tool:error` so the trace\n // doesn't carry a noisy \"Unknown tool\" message back to the model.\n const unknownCtx: ToolHookContext & {\n result?: string | ToolResultContent[]\n suppressError: boolean\n } = {\n ...buildToolHookBase(ctx, turnId, callId, call.name, displayName, effectiveInput),\n suppressError: false,\n }\n await ctx.hooks.callHook('tool:unknown', unknownCtx)\n\n const content = unknownCtx.result ?? `Tool error: Unknown tool: ${call.name}`\n // Treat unknown-tool as an error iff the consumer didn't substitute a\n // successful result via `tool:unknown` (which is the substitute-and-\n // recover path). A non-substituted unknown tool always carries\n // `Tool error: …` text and should ride the `is_error` channel so the\n // model sees the wire-level error flag.\n const isError = unknownCtx.result === undefined\n\n if (!unknownCtx.suppressError) {\n const err = new Error(`Unknown tool: ${call.name}`)\n await ctx.hooks.callHook('tool:error', {\n ...buildToolHookBase(ctx, turnId, callId, call.name, displayName, effectiveInput),\n error: err,\n })\n }\n // `tool:dispatched` fires for symmetry; `tool:after` / `tool:transform`\n // do NOT — see the comment in the gate-block branch above for the\n // backward-compat rationale.\n await fireDispatched(ctx, {\n turnId,\n callId,\n name: call.name,\n displayName,\n input: effectiveInput,\n outcome: 'unknown',\n runToolCounts,\n })\n return { result: { id: callId, content, ...(isError ? { isError: true } : {}) } }\n }\n\n // Validate arguments (respect mutations from tool:gate hook). Auto-coerces\n // string→boolean/number/etc. for OSS models that don't always honor types.\n const validation = validateToolArgs(effectiveInput, toolDef.spec.inputSchema)\n if (!validation.valid) {\n await ctx.hooks.callHook('validation:reject', {\n ...buildToolHookBase(ctx, turnId, callId, call.name, displayName, effectiveInput),\n reason: validation.error ?? 'invalid input',\n schema: toolDef.spec.inputSchema,\n })\n // Symmetric `tool:dispatched`; narrow `tool:after` semantics preserved.\n await fireDispatched(ctx, {\n turnId,\n callId,\n name: call.name,\n displayName,\n input: effectiveInput,\n outcome: 'invalid-input',\n runToolCounts,\n })\n return { result: { id: callId, content: `Validation error: ${validation.error}`, isError: true } }\n }\n // Pass the coerced input to the tool — `\"true\"` is now `true`, etc.\n effectiveInput = validation.coercedInput ?? effectiveInput\n\n // Surface successful coercions so consumers can count \"model wrongness rate\".\n // Only fires when at least one field was coerced; perfectly-typed inputs stay\n // silent so the hook bus isn't noisy on the happy path.\n const coercions = validation.coercions && validation.coercions.length > 0\n ? validation.coercions\n : undefined\n if (coercions) {\n await ctx.hooks.callHook('validation:coerce', {\n ...buildToolHookBase(ctx, turnId, callId, call.name, displayName, effectiveInput),\n coercions,\n schema: toolDef.spec.inputSchema,\n })\n }\n\n await fireDispatched(ctx, {\n turnId,\n callId,\n name: call.name,\n displayName,\n input: effectiveInput,\n outcome: 'execute',\n runToolCounts,\n })\n await ctx.hooks.callHook('tool:before', {\n ...buildToolHookBase(ctx, turnId, callId, call.name, displayName, effectiveInput),\n runToolCounts,\n ...(coercions ? { coercions } : {}),\n })\n\n // `output` is set in the success branch and in the regular-error branch\n // of the catch; the user-cancel branch returns early *above* the\n // post-execute emit so it never reaches the `output` read. Initialising\n // to a never-used sentinel keeps TS happy without disabling strict mode.\n let output: string | ToolResultContent[] = ''\n let isError = false\n let cancelledByUser = false\n\n // Per-call abort composes with the run-scoped / sibling-scoped `ctx.signal`\n // so the tool body sees one combined signal. The body still gets aborted\n // when the run / sibling fleet aborts; the new case the union enables is\n // a user-issued cancel of *this specific call* without unwinding the rest\n // of the batch. Falls back to `ctx.signal` if `AbortSignal.any` is\n // unavailable (Node < 20.3 / Bun < 0.6) — we lose per-call cancellation\n // but the body's existing observation of `ctx.signal` keeps working.\n const childSignal: AbortSignal = typeof AbortSignal.any === 'function'\n ? AbortSignal.any([ctx.signal, perCallAbort.signal])\n : ctx.signal\n\n try {\n const toolCtx: ToolContext = {\n provider: ctx.provider,\n signal: childSignal,\n execution: ctx.execution,\n handle: ctx.handle,\n hooks: ctx.hooks,\n tools: ctx.agentTools,\n ...(ctx.agentName !== undefined ? { name: ctx.agentName } : {}),\n ...(ctx.agentSystem !== undefined ? { system: ctx.agentSystem } : {}),\n ...(ctx.agentToolAliases !== undefined ? { toolAliases: ctx.agentToolAliases } : {}),\n ...(ctx.agentMcpServers !== undefined ? { mcpServers: ctx.agentMcpServers } : {}),\n ...(ctx.agentSkills !== undefined ? { skills: ctx.agentSkills } : {}),\n ...(ctx.agentBehavior !== undefined ? { behavior: ctx.agentBehavior } : {}),\n turnId,\n callId,\n runId: ctx.runId,\n ...(ctx.parentRunId !== undefined ? { parentRunId: ctx.parentRunId } : {}),\n ...(ctx.session ? { session: ctx.session } : {}),\n ...(ctx.readState ? { readState: ctx.readState } : {}),\n ...(typeof ctx.depth === 'number' ? { depth: ctx.depth } : {}),\n clock: ctx.clock,\n }\n\n // Race the tool body against a per-call cancellation. Two design points:\n //\n // 1. The body promise is kicked off synchronously, then we attach a\n // no-op `.catch` to it so a late settle (the user cancels mid-\n // flight, we win the race with cancellation, then the body\n // eventually throws into the void) never surfaces as an unhandled\n // rejection. Misbehaving tool bodies that ignore `ctx.signal`\n // continue running in the background until they naturally finish;\n // the loop has already moved on with the cancellation marker.\n // 2. The rejection-side promise listens on `perCallAbort.signal` only.\n // Sibling / run aborts flow through `ctx.signal` into `childSignal`\n // and into the body — the existing AbortError path catches them\n // and the `cancelledByUser` guard below correctly leaves them in\n // the regular error branch (preserving the existing\n // `INTERRUPT_MESSAGE_FOR_TOOL_USE` semantics at the batch layer).\n const bodyPromise = toolDef.execute(effectiveInput, toolCtx)\n bodyPromise.catch(() => {})\n\n let removeAbortListener: (() => void) | undefined\n const cancellationPromise = new Promise<never>((_, reject) => {\n if (perCallAbort.signal.aborted) {\n reject(new Error(CANCELLED_BY_USER_SENTINEL))\n return\n }\n const onAbort = () => reject(new Error(CANCELLED_BY_USER_SENTINEL))\n perCallAbort.signal.addEventListener('abort', onAbort, { once: true })\n removeAbortListener = () => perCallAbort.signal.removeEventListener('abort', onAbort)\n })\n\n try {\n output = await Promise.race([bodyPromise, cancellationPromise])\n }\n finally {\n // Detach the abort listener on the happy path so a subsequent cancel\n // (race-condition-ish: body resolved, then user clicks cancel before\n // we've returned) doesn't reject a promise nobody awaits.\n removeAbortListener?.()\n }\n }\n catch (err) {\n // Classify the failure as user-cancellation vs regular error.\n //\n // Two signals say \"this was the user's per-call cancel\":\n // 1. Our cancellation promise won the race — it always rejects with\n // the sentinel-message Error we built above. This is the\n // definitive path because it pins the cause to OUR promise; no\n // other code path produces this message.\n // 2. The body observed `ctx.signal` and threw an `AbortError`-shaped\n // rejection, AND `perCallAbort` is the one that aborted (regardless\n // of whether the sibling/run signal also aborted, which can happen\n // when shell-cascade fires after our first cancelled shell returns\n // — see `dispatch`'s onresolved branch below for the cascade-skip\n // that prevents this in practice).\n //\n // The previous check `(perCallAbort.signal.aborted && !ctx.signal.aborted)`\n // was a too-narrow proxy: it false-rejected when the cascade had already\n // flipped `ctx.signal` between our perCallAbort and the catch, which\n // leaked the raw sentinel string to the model on parallel-cancel-all.\n const isOurSentinel = err instanceof Error && err.message === CANCELLED_BY_USER_SENTINEL\n const isAbortError = err instanceof Error && err.name === 'AbortError'\n if (isOurSentinel || (isAbortError && perCallAbort.signal.aborted)) {\n cancelledByUser = true\n }\n else {\n const error = err instanceof Error ? err : new Error(String(err))\n // Hook can mutate `result` to substitute a custom payload for the model —\n // e.g. OSS-model error rewriting, collapsing stack traces. Default\n // `Tool error: <msg>` is used when no handler sets it.\n const errorCtx: ToolHookContext & {\n error: Error\n result?: string | ToolResultContent[]\n } = {\n ...buildToolHookBase(ctx, turnId, callId, call.name, displayName, effectiveInput),\n error,\n }\n await ctx.hooks.callHook('tool:error', errorCtx)\n output = errorCtx.result ?? `Tool error: ${error.message}`\n isError = true\n }\n }\n\n if (cancelledByUser) {\n const reason = typeof perCallAbort.signal.reason === 'string'\n ? perCallAbort.signal.reason\n : 'cancelled-by-user'\n await ctx.hooks.callHook('tool:cancelled', {\n ...buildToolHookBase(ctx, turnId, callId, call.name, displayName, effectiveInput),\n reason,\n runToolCounts,\n })\n return { result: { id: callId, content: TOOL_USE_CANCELLED_MESSAGE, isError: true } }\n }\n\n const emitted = await emitToolResult(ctx, {\n turnId,\n callId,\n name: call.name,\n displayName,\n input: effectiveInput,\n output,\n isError,\n runToolCounts,\n ...(coercions ? { coercions } : {}),\n })\n\n return {\n result: {\n id: callId,\n content: emitted.output,\n ...(emitted.isError ? { isError: true } : {}),\n },\n }\n}\n\n/**\n * Fire `tool:dispatched` with the resolved path discriminator. Every code\n * path in {@link executeSingleTool} that produces a tool_result must call\n * this exactly once — that contract is what makes `tool:dispatched` ↔\n * `tool:after` a guaranteed symmetric pairing for live-event consumers\n * (the chat layer, SDK consumers reconstructing wire history from events,\n * tracing spans that want to record refused calls separately from\n * executed ones).\n *\n * Helper exists to centralize the optional-field plumbing for `reason`\n * (only set on `gate-block`) without sprinkling spread-conditional logic\n * across five call sites.\n */\nasync function fireDispatched(\n ctx: LoopContext,\n params: {\n turnId: string\n callId: string\n name: string\n displayName: string\n input: Record<string, unknown>\n outcome: 'execute' | 'gate-substitute' | 'gate-block' | 'unknown' | 'invalid-input'\n reason?: string\n runToolCounts: Readonly<Record<string, number>>\n },\n): Promise<void> {\n const { turnId, callId, name, displayName, input, outcome, reason, runToolCounts } = params\n await ctx.hooks.callHook('tool:dispatched', {\n ...buildToolHookBase(ctx, turnId, callId, name, displayName, input),\n outcome,\n runToolCounts,\n ...(reason !== undefined ? { reason } : {}),\n })\n}\n\n/**\n * Shared post-output emission: fire `tool:transform` (mutate-allowed), strip\n * images for non-vision providers, fire `tool:after`. Used by both the\n * gate-substitute (Z20) and post-execute paths so they stay byte-for-byte\n * identical from the consumer's perspective.\n *\n * Returns both the (possibly transformed) output and the final `isError`\n * flag — `tool:transform` listeners can flip the flag in either direction\n * (e.g. rewrite a structured error response to a graceful retry hint), and\n * the caller needs the post-transform value to populate `ToolResult.isError`\n * on the wire.\n */\nasync function emitToolResult(\n ctx: LoopContext,\n params: {\n turnId: string\n callId: string\n name: string\n displayName: string\n input: Record<string, unknown>\n output: string | ToolResultContent[]\n isError: boolean\n runToolCounts: Readonly<Record<string, number>>\n coercions?: readonly string[]\n },\n): Promise<{ output: string | ToolResultContent[], isError: boolean }> {\n const { turnId, callId, name, displayName, input, runToolCounts, coercions } = params\n let output = params.output\n let isError = params.isError\n\n // Transform hook — mutate ctx.result / ctx.isError to modify output. The\n // `outputBytes` field is the byte-length of the result *before* any\n // consumer mutation, so a truncation hook can decide whether to act.\n const transformCtx = {\n ...buildToolHookBase(ctx, turnId, callId, name, displayName, input),\n result: output,\n isError,\n outputBytes: toolOutputByteLength(output),\n ...(coercions ? { coercions } : {}),\n }\n await ctx.hooks.callHook('tool:transform', transformCtx)\n output = transformCtx.result\n isError = transformCtx.isError\n\n // Disk persistence — runs AFTER `tool:transform` so consumer transforms\n // get first crack at the result (compress / redact / annotate) before\n // we decide whether the post-transform bytes blow past the threshold.\n // Substitution mutates `output` in place; `tool:after` below then sees\n // the stub's byte size, and the stub is what lands in `session.turns`,\n // so subsequent turns re-emit the same bytes and ride the prompt cache.\n //\n // The truthy-check on `threshold` + `persistDir` short-circuits when\n // persistence is fully off (saves an async hop + struct allocation per\n // tool result) AND narrows both locals to non-nullable inside the block,\n // so the helper call site stays clean. The helper still validates its\n // own inputs (isAbsolute, threshold > 0, callId shape) — single source\n // of truth for \"is this call eligible\".\n const threshold = ctx.persistThreshold\n const persistDir = ctx.persistDir\n if (threshold && threshold > 0 && persistDir) {\n const outcome = await maybePersistToolResult({\n toolName: name,\n callId,\n output,\n threshold,\n persistDir,\n ...(ctx.persistExcludeTools ? { excludeTools: ctx.persistExcludeTools } : {}),\n ...(typeof ctx.persistMaxBytes === 'number' ? { maxBytes: ctx.persistMaxBytes } : {}),\n })\n if (outcome.kind === 'persisted') {\n output = outcome.output\n }\n // `error` and `skip` outcomes leave `output` untouched. Write failures\n // are intentionally swallowed at this layer — a permissions blip on\n // the persistence dir shouldn't take down a tool call whose result is\n // otherwise valid. The loop continues with the full inline payload.\n else if (outcome.kind === 'error' && process.env.ZIDANE_DEBUG) {\n process.stderr.write(`[zidane/loop] persistence write failed for ${name}/${callId}: ${outcome.error.message}\\n`)\n }\n }\n\n // Strip images for non-vision providers before they hit the wire. Done\n // after the transform hook so consumers can still observe the raw\n // structured output.\n output = stripImagesForNonVision(ctx.provider, output)\n\n // `tool:after` carries the post-mutation byte size — what actually goes\n // back to the model. Telemetry consumers should prefer this over\n // recomputing.\n await ctx.hooks.callHook('tool:after', {\n ...buildToolHookBase(ctx, turnId, callId, name, displayName, input),\n result: output,\n outputBytes: toolOutputByteLength(output),\n runToolCounts,\n ...(coercions ? { coercions } : {}),\n })\n\n return { output, isError }\n}\n\n/** Default cap on in-flight tools per turn. Mirrors Claude Code's `CLAUDE_CODE_MAX_TOOL_USE_CONCURRENCY`. */\nconst DEFAULT_MAX_CONCURRENT_TOOLS = 10\n\n/** Canonical name of the shell tool — referenced for cascade-cancel semantics. */\nconst SHELL_TOOL_NAME = 'shell'\n\n/** Reason surfaced on `siblingAbort.signal` when a shell error cancels its fleet. */\nconst SHELL_CASCADE_REASON = 'sibling-shell-error'\n\n/**\n * Canonical `tool_result.content` text emitted to siblings that were\n * cancelled by a `shell` error in the same batch. Distinct from\n * {@link INTERRUPT_MESSAGE_FOR_TOOL_USE} (user-issued abort) and\n * {@link TOOL_USE_SKIPPED_MESSAGE} (steered) so consumers can split\n * the three causes by string-match.\n */\nexport const SHELL_CASCADE_CANCEL_MESSAGE = 'Cancelled: a sibling `shell` call in the same batch errored; re-run independently if still needed.'\n\n/**\n * Resolve a tool's concurrency-safety verdict for a specific call.\n *\n * - Missing toolDef (unknown tool) → `false`. `executeSingleTool` handles\n * the unknown-tool path itself; barriering it keeps the unknown-tool\n * error from racing with siblings.\n * - Static `true` / `false` → use as-is.\n * - Function → invoke; any throw is treated as `false` (fail-closed) so a\n * buggy predicate can't accidentally widen the safety window.\n *\n * Pure / sync — pre-computed once per call before dispatch begins, so the\n * scheduler's hot path stays branch-light.\n */\nfunction resolveConcurrencySafe(\n def: ToolDef | undefined,\n input: Record<string, unknown>,\n): boolean {\n if (!def)\n return false\n const flag = def.isConcurrencySafe\n if (flag === undefined)\n return false\n if (typeof flag === 'boolean')\n return flag\n try {\n return flag(input) === true\n }\n catch {\n return false\n }\n}\n\n/**\n * Unified per-turn tool dispatcher.\n *\n * Walks `toolCalls` in submission order. For each call:\n *\n * - **Concurrency-safe + fleet is all safe + room under the cap** → fires\n * asynchronously and the loop advances to the next call. The fleet runs\n * in parallel up to `behavior.maxConcurrentTools` (default {@link\n * DEFAULT_MAX_CONCURRENT_TOOLS}).\n * - **Unsafe** (or the in-flight fleet contains anything unsafe) → acts\n * as a barrier: waits for the fleet to drain, then runs alone, then\n * unblocks the queue.\n *\n * Results are written into a fixed `results[index]` array on completion\n * and yielded back in submission order, so the model sees deterministic\n * adjacency regardless of which call finished first.\n *\n * **Failure modes:**\n *\n * - **Hook throws / tool body throws** — captured per-call into a\n * `tool_result` so the assistant turn's `tool_use` IDs always have\n * matching `tool_result` IDs (providers reject orphan IDs).\n * - **Parent abort** mid-batch — drains the in-flight fleet (their\n * `AbortError` becomes `INTERRUPT_MESSAGE_FOR_TOOL_USE`), then synthesizes\n * interrupt results for any unstarted calls so the turn closes cleanly.\n * - **Steering queue populated** between dispatches — same drain + a\n * `TOOL_USE_SKIPPED_MESSAGE` result for unstarted calls. The outer loop\n * picks up the steer at the next checkpoint.\n * - **Shell error in a fleet** — `siblingAbort.abort('sibling-shell-error')`\n * tears down concurrently-running siblings. Mirrors the convention that\n * shell commands often chain (`mkdir foo && cd foo`); one failing\n * sibling commonly invalidates the rest. Non-shell errors are isolated.\n *\n * A child `AbortController` (`siblingAbort`) forwards the parent abort\n * AND carries the shell-cascade signal — siblings see one signal source.\n */\nasync function executeToolBatch(\n ctx: LoopContext,\n toolCalls: ToolCall[],\n turnId: string,\n): Promise<ToolResult[]> {\n if (toolCalls.length === 0)\n return []\n\n const N = toolCalls.length\n const maxConcurrent = Math.max(1, ctx.maxConcurrentTools ?? DEFAULT_MAX_CONCURRENT_TOOLS)\n const results: (ToolResult | undefined)[] = Array.from({ length: N })\n\n // Pre-resolve every call's safety verdict so the scheduler's main loop\n // doesn't re-invoke (potentially throwing) predicates inside its hot\n // path. Indices line up with `toolCalls` and `results`.\n const safe: boolean[] = Array.from({ length: N })\n for (let i = 0; i < N; i++)\n safe[i] = resolveConcurrencySafe(ctx.tools[toolCalls[i].name], toolCalls[i].input)\n\n // One controller for the whole batch — siblings share it. The parent\n // abort is forwarded via a single listener that we MUST clean up\n // when the batch returns: `ctx.signal` is the run-scoped\n // `agent.run()` signal that outlives this batch, so a never-removed\n // listener would accumulate one entry per batch over a multi-turn\n // run. Captured in `parentAbortListener` so the `finally` below\n // can call `removeEventListener`. `{ once: true }` short-circuits\n // the listener if the abort fires, but we still need to remove it\n // on the (typical) non-abort exit.\n const siblingAbort = new AbortController()\n let parentAbortListener: (() => void) | undefined\n if (ctx.signal.aborted) {\n siblingAbort.abort(ctx.signal.reason ?? 'parent-aborted')\n }\n else {\n parentAbortListener = () => siblingAbort.abort(ctx.signal.reason ?? 'parent-aborted')\n ctx.signal.addEventListener('abort', parentAbortListener, { once: true })\n }\n\n // Tools see the sibling-scoped signal — a shell cascade cancels them\n // without unwinding the parent. Spreading `ctx` keeps every other\n // field (hooks, behavior, readState, parentRunId, …) reaching tools.\n const childCtx: LoopContext = { ...ctx, signal: siblingAbort.signal }\n\n /** Indices currently in flight. Tracked for fleet-safety + cap checks. */\n const inFlight = new Map<number, Promise<void>>()\n\n /**\n * Distinguish a shell-cascade kill from a user-issued abort so the\n * model sees actionable text. When BOTH the parent signal and the\n * sibling signal are aborted, the parent wins — user-issued aborts\n * take precedence (the model is being interrupted by the human, not\n * by a sibling's failure).\n */\n const cancelMessage = (): string => {\n if (ctx.signal.aborted)\n return INTERRUPT_MESSAGE_FOR_TOOL_USE\n if (siblingAbort.signal.reason === SHELL_CASCADE_REASON)\n return SHELL_CASCADE_CANCEL_MESSAGE\n return INTERRUPT_MESSAGE_FOR_TOOL_USE\n }\n\n const dispatch = (index: number): Promise<void> => {\n const call = toolCalls[index]\n return executeSingleTool(childCtx, call, turnId).then(\n ({ result }) => {\n results[index] = result\n // Cascade-cancel only for shell, and only on the first error\n // that lands in a still-live fleet — keeps the abort reason\n // pinned to the first culprit. We deliberately EXCLUDE\n // user-cancelled results: a `agent.cancelTool('A')` on a shell\n // means \"I'm done with A\", not \"A failed in a way that\n // invalidates B and C\". Without this exclusion, cancelling one\n // shell would cascade-kill its siblings (surprising and wrong\n // — the user only cancelled one), and `cancel all` on a fleet\n // of shells would mis-classify B and C as cascade-aborts\n // instead of user-cancellations, leaking the internal sentinel\n // string to the wire.\n const contentIsString = typeof result.content === 'string'\n const isUserCancel = contentIsString && result.content === TOOL_USE_CANCELLED_MESSAGE\n if (\n result.isError\n && !isUserCancel\n && call.name === SHELL_TOOL_NAME\n && !siblingAbort.signal.aborted\n ) {\n siblingAbort.abort(SHELL_CASCADE_REASON)\n }\n },\n (err: unknown) => {\n const isAbort = siblingAbort.signal.aborted\n || ctx.signal.aborted\n || (err instanceof Error && err.name === 'AbortError')\n results[index] = {\n id: call.id,\n content: isAbort ? cancelMessage() : `Error: ${errorMessage(err)}`,\n isError: true,\n }\n },\n ).finally(() => {\n inFlight.delete(index)\n })\n }\n\n const drain = async (): Promise<void> => {\n if (inFlight.size > 0)\n await Promise.all([...inFlight.values()])\n }\n\n /** Whether every in-flight call is concurrency-safe. */\n const fleetAllSafe = (): boolean => {\n for (const idx of inFlight.keys()) {\n if (!safe[idx])\n return false\n }\n return true\n }\n\n /**\n * Fill all unstarted slots (`results[j]` still undefined) with the\n * canonical text + `isError: true`. Used at every short-circuit\n * branch (abort / steer) so the assistant turn's `tool_use` IDs\n * always have matching `tool_result` IDs — providers reject orphan\n * IDs loudly.\n */\n const fillUnstarted = (from: number, content: string): void => {\n for (let j = from; j < N; j++) {\n if (!results[j])\n results[j] = { id: toolCalls[j].id, content, isError: true }\n }\n }\n\n try {\n for (let i = 0; i < N; i++) {\n // Barrier semantics: an unsafe call (or an unsafe-poisoned fleet,\n // or a full fleet) flushes everything in-flight before dispatching.\n // After the drain, the fleet is empty and the unsafe call runs\n // alone; subsequent safe calls re-form a new fleet behind it.\n if (!safe[i] || !fleetAllSafe() || inFlight.size >= maxConcurrent)\n await drain()\n\n // Re-check abort + steer AFTER any drain — tools that resolved\n // during the drain may have flipped either signal (e.g. a tool\n // calls `agent.abort()` from its body, or `tool:after` enqueues\n // a steer). Checking only at the top of the iteration would\n // miss these and dispatch one extra tool. Cheap re-check; no I/O.\n //\n // On either early-return path we MUST drain the live fleet\n // first: dispatched-but-unresolved promises still own slots in\n // `results[]`, so returning before they finish would leave\n // those slots `undefined` and the caller would see orphan\n // `tool_use` IDs without paired `tool_result`s. Aborted tools\n // propagate through `siblingAbort.signal` and resolve with an\n // error result; steered tools complete normally (the steer is\n // a post-batch concern, not an in-flight kill).\n if (ctx.signal.aborted) {\n await drain()\n fillUnstarted(i, INTERRUPT_MESSAGE_FOR_TOOL_USE)\n return results as ToolResult[]\n }\n if (ctx.steeringQueue.length > 0) {\n await drain()\n fillUnstarted(i, TOOL_USE_SKIPPED_MESSAGE)\n return results as ToolResult[]\n }\n\n inFlight.set(i, dispatch(i))\n }\n\n // Drain the trailing fleet — last call may have been safe + non-\n // blocking. After the drain, results may still contain interrupt\n // messages from dispatch's onrejected branch for tools that saw a\n // mid-flight abort.\n await drain()\n return results as ToolResult[]\n }\n finally {\n // Detach the parent-abort listener so we don't leak one listener\n // per batch on `ctx.signal` (run-scoped, outlives many batches).\n // No-op if the listener already fired (`{ once: true }`) or was\n // never registered (parent was already aborted at batch start).\n if (parentAbortListener)\n ctx.signal.removeEventListener('abort', parentAbortListener)\n }\n}\n","/**\n * Prompt canonicalization + default multimodal message builder.\n *\n * `agent.run({ prompt })` accepts either `string` or `PromptPart[]`. The\n * agent normalizes both to `PromptPart[]` before handing to the provider.\n *\n * Providers may implement `Provider.promptMessage` for native-format support\n * (e.g. Anthropic's document blocks). Providers that don't fall back to\n * `defaultPromptMessage`, which inlines text-encoded documents and throws on\n * base64-encoded documents.\n */\n\nimport type { Provider } from './providers'\nimport type { PromptPart, SessionContentBlock, SessionMessage } from './types'\n\n/**\n * Coerce the run-level prompt into a `PromptPart[]`.\n *\n * - `string` prompt → a single `text` part. Empty string returns `undefined`\n * so callers skip pushing an empty user turn.\n * - `PromptPart[]` prompt → validated and returned as-is. An empty array, or\n * an array whose text parts are all empty with no image/document parts,\n * returns `undefined`.\n * - `undefined` → `undefined` (promptless resume path).\n */\nexport function canonicalizePrompt(\n prompt: string | PromptPart[] | undefined,\n): PromptPart[] | undefined {\n if (prompt === undefined)\n return undefined\n\n if (typeof prompt === 'string') {\n if (prompt.length === 0)\n return undefined\n return [{ type: 'text', text: prompt }]\n }\n\n // Array path — drop if empty, or if every text part is empty and there are no\n // image/document parts (nothing meaningful to send).\n if (prompt.length === 0)\n return undefined\n\n // Reject malformed parts up front rather than letting them slip through to\n // `defaultPromptMessage` / `provider.promptMessage`, where the failure\n // mode is a confusing \"Cannot read property 'text' of undefined\" or a\n // base64-document throw far from the call-site. The check is cheap and\n // catches the common typo (wrong `type` field) loudly.\n for (const part of prompt) {\n if (!part || typeof part !== 'object' || typeof (part as { type?: unknown }).type !== 'string') {\n throw new Error('Invalid PromptPart: each part must be an object with a `type` field.')\n }\n const type = (part as { type: string }).type\n if (type !== 'text' && type !== 'image' && type !== 'document') {\n throw new Error(`Invalid PromptPart type \"${type}\". Expected \"text\" | \"image\" | \"document\".`)\n }\n }\n\n const hasMeaningfulPart = prompt.some(part => (\n (part.type === 'text' && part.text.length > 0)\n || part.type === 'image'\n || part.type === 'document'\n ))\n if (!hasMeaningfulPart)\n return undefined\n\n return prompt\n}\n\n/**\n * Build a user `SessionMessage` from prompt parts without provider-specific handling.\n *\n * - `text` parts map to `{ type: 'text', text }` blocks.\n * - `image` parts map to `{ type: 'image', mediaType, data }` blocks.\n * - `document` parts with `encoding: 'text'` are inlined as an attachment-tagged\n * text block so every provider can read them.\n * - `document` parts with `encoding: 'base64'` throw — the caller should switch\n * to a provider that implements `promptMessage` (e.g. Anthropic for PDFs).\n */\nexport function defaultPromptMessage(parts: PromptPart[]): SessionMessage {\n const content: SessionContentBlock[] = []\n\n for (const part of parts) {\n if (part.type === 'text') {\n if (part.text.length > 0)\n content.push({ type: 'text', text: part.text })\n continue\n }\n\n if (part.type === 'image') {\n content.push({ type: 'image', mediaType: part.mediaType, data: part.data, ...(part.name ? { name: part.name } : {}) })\n continue\n }\n\n // document\n if (part.encoding === 'text') {\n const header = part.name\n ? `<attachment name=\"${part.name}\" media_type=\"${part.mediaType}\">`\n : `<attachment media_type=\"${part.mediaType}\">`\n content.push({ type: 'text', text: `${header}\\n${part.data}\\n</attachment>` })\n continue\n }\n\n throw new Error(\n `Provider does not support base64 document parts (mediaType: ${part.mediaType}). `\n + `Use a text-encoded document or a provider that implements promptMessage (e.g. Anthropic).`,\n )\n }\n\n return { role: 'user', content }\n}\n\n/**\n * Build the prompt `SessionMessage` for a given provider.\n *\n * Prefers `provider.promptMessage` when defined, falling back to `defaultPromptMessage`.\n */\nexport function buildPromptMessage(provider: Provider, parts: PromptPart[]): SessionMessage {\n if (provider.promptMessage)\n return provider.promptMessage(parts)\n return defaultPromptMessage(parts)\n}\n","/**\n * `tool-budgets` middleware — per-tool soft call caps on top of the\n * `tool:gate` writable-`block`/`result` slots and the run-cumulative\n * `runToolCounts` (Z24).\n *\n * Two modes:\n *\n * - `'block'` — refuse the over-budget call via `ctx.block = true`. The model\n * sees a `Blocked: <message>` tool result.\n * - `'steer'` — let the call run, but enqueue a synthetic user message for\n * the NEXT turn so the model sees the budget warning before deciding to\n * call again. Reuses the existing `steeringQueue` plumbing.\n *\n * Either mode fires `tool-budget:exceeded` so observability layers can react.\n */\n\nimport type { Hookable } from 'hookable'\nimport type { AgentHooks } from './agent'\nimport type { AgentBehavior, ToolHookContext, ToolResultContent } from './types'\n\n/**\n * Install the per-tool soft-budget middleware on a hook bus.\n * `getToolBudgets` returns the resolved per-tool budgets (run override merged\n * with agent defaults). `enqueueSteer` pushes a synthetic user message into\n * the run's steeringQueue so the loop drains it between turns.\n *\n * The middleware maintains its OWN approval counter (`approvedCounts`),\n * incremented at gate-time — independent of the loop's `runToolCounts`,\n * which is incremented after gate completes. This gives atomic per-call\n * reservation in parallel batches: when a batch of N calls all fire\n * `tool:gate` before any increments propagate, each gate handler still\n * sees the prior approvals and refuses past `max`.\n *\n * Returns an `uninstall` fn.\n */\nexport function installToolBudgetsGate(\n hooks: Hookable<AgentHooks>,\n getToolBudgets: () => AgentBehavior['toolBudgets'] | undefined,\n enqueueSteer: (message: string) => void,\n): () => void {\n // Per-run set of tools that have ALREADY had a steer fired. Without this,\n // every call past `max` queues another nudge and the conversation drowns\n // in identical reminders. Cleared on uninstall (run-end).\n const steeredOnce = new Set<string>()\n\n // Per-tool approval counter the middleware owns. Incremented synchronously\n // when a call passes gate, so within-batch ordering is preserved even when\n // the loop's `runToolCounts` lags (handlers across a parallel batch can\n // fire before any of the batch's calls have been incremented loop-side).\n const approvedCounts: Record<string, number> = {}\n\n async function gateHandler(ctx: ToolHookContext & {\n block: boolean\n reason: string\n result?: string | ToolResultContent[]\n runToolCounts: Readonly<Record<string, number>>\n }) {\n // Don't override prior gate handlers.\n if (ctx.block || ctx.result !== undefined)\n return\n\n const toolBudgets = getToolBudgets()\n const budget = toolBudgets?.[ctx.name]\n if (!budget)\n return\n\n const max = budget.max\n if (typeof max !== 'number' || max <= 0)\n return\n\n // Read our internal approval counter — independent of the loop's\n // `runToolCounts` so within-batch ordering is correct even in parallel\n // mode. Each gate that approves a call increments this synchronously.\n const count = approvedCounts[ctx.name] ?? 0\n if (count < max) {\n // Approve atomically: bump the counter now, before yielding to the\n // next handler in the chain. Subsequent calls in the same parallel\n // batch see this approval and may refuse if the budget is depleted.\n approvedCounts[ctx.name] = count + 1\n return\n }\n\n // Resolve mode + message. Function form runs untrusted consumer code —\n // a throw would crash the hook bus, so we fall back to the default\n // 'steer' shape on error rather than poisoning the run.\n const onExceed = budget.onExceed ?? 'steer'\n let mode: 'steer' | 'block'\n let message: string\n if (typeof onExceed === 'function') {\n try {\n const out = onExceed({ tool: ctx.name, count, max })\n mode = out.mode\n message = out.message\n }\n catch {\n mode = 'steer'\n message = defaultSteerMessage(ctx.name, count, max)\n }\n }\n else if (onExceed === 'block') {\n mode = 'block'\n message = defaultBlockMessage(ctx.name, max)\n }\n else {\n mode = 'steer'\n message = defaultSteerMessage(ctx.name, count, max)\n }\n\n if (mode === 'block') {\n ctx.block = true\n ctx.reason = message\n await hooks.callHook('tool-budget:exceeded', {\n tool: ctx.name,\n count,\n max,\n turnId: ctx.turnId,\n mode: 'block',\n })\n return\n }\n\n // Steer mode — let the call go through, but queue a single nudge per\n // tool so the model sees the budget warning before its next turn.\n if (!steeredOnce.has(ctx.name)) {\n steeredOnce.add(ctx.name)\n enqueueSteer(message)\n await hooks.callHook('tool-budget:exceeded', {\n tool: ctx.name,\n count,\n max,\n turnId: ctx.turnId,\n mode: 'steer',\n })\n }\n }\n\n const unregister = hooks.hook('tool:gate', gateHandler)\n\n return function uninstall() {\n unregister()\n steeredOnce.clear()\n }\n}\n\nfunction defaultSteerMessage(tool: string, count: number, max: number): string {\n return `[Tool budget reached: '${tool}' has been called ${count} times this run (cap: ${max}). Avoid calling it again unless strictly necessary; commit to a result and move on.]`\n}\n\nfunction defaultBlockMessage(tool: string, max: number): string {\n return `Tool '${tool}' has reached its per-run budget of ${max} calls; further invocations are refused.`\n}\n","/**\n * Per-command exit-code interpretation for the `shell` tool.\n *\n * Many command-line tools use non-zero exit codes to signal information that\n * is *not* an error — `grep` returns 1 for \"no matches found\", `diff` returns\n * 1 for \"files differ\", `find` returns 1 when some directories were\n * inaccessible, and `test`/`[` return 1 for \"condition false\". Treating\n * those uniformly as failures wastes turns: models retry or pivot when there\n * is nothing wrong.\n *\n * Mirrors `claude-code/tools/BashTool/commandSemantics.ts`.\n */\n\nexport interface CommandSemanticResult {\n /** Whether to surface this as an error to the model (`Exit code N` prefix). */\n isError: boolean\n /** Optional human-readable footer appended after the body, e.g. \"No matches found\". */\n message?: string\n}\n\ntype CommandSemantic = (exitCode: number) => CommandSemanticResult\n\nconst DEFAULT_SEMANTIC: CommandSemantic = exitCode => ({\n isError: exitCode !== 0,\n message: exitCode !== 0 ? `Command failed with exit code ${exitCode}` : undefined,\n})\n\nconst COMMAND_SEMANTICS: ReadonlyMap<string, CommandSemantic> = new Map<string, CommandSemantic>([\n // grep / ripgrep: 0 = matches, 1 = no matches, ≥2 = error.\n ['grep', exit => ({ isError: exit >= 2, message: exit === 1 ? 'No matches found' : undefined })],\n ['rg', exit => ({ isError: exit >= 2, message: exit === 1 ? 'No matches found' : undefined })],\n // diff: 0 = identical, 1 = differ, ≥2 = error.\n ['diff', exit => ({ isError: exit >= 2, message: exit === 1 ? 'Files differ' : undefined })],\n // find: 0 = ok, 1 = some dirs inaccessible (warning), ≥2 = error.\n ['find', exit => ({ isError: exit >= 2, message: exit === 1 ? 'Some directories were inaccessible' : undefined })],\n // test / [: 0 = condition true, 1 = condition false, ≥2 = error.\n ['test', exit => ({ isError: exit >= 2, message: exit === 1 ? 'Condition is false' : undefined })],\n ['[', exit => ({ isError: exit >= 2, message: exit === 1 ? 'Condition is false' : undefined })],\n])\n\n/**\n * Pick the semantic for a command line. Best-effort: walks the command from\n * right to left, taking the last segment after `|` / `&&` / `||` / `;` —\n * that's the segment whose exit code propagates. Don't depend on this for\n * security; it's a heuristic, not a parser.\n */\nexport function interpretShellResult(\n command: string,\n exitCode: number,\n): CommandSemanticResult {\n const base = extractTrailingCommand(command)\n const semantic = COMMAND_SEMANTICS.get(base) ?? DEFAULT_SEMANTIC\n return semantic(exitCode)\n}\n\nfunction extractTrailingCommand(command: string): string {\n // Split on the common chain operators. The exit code we see is the trailing\n // segment's. Quoted operators escape the split, but we don't try to be\n // perfect — false positives just fall back to default semantics.\n const segments = command.split(/\\|\\||&&|[;|\\n]/)\n const last = segments[segments.length - 1]?.trim() ?? command\n // First whitespace-delimited token of the trailing segment, sans leading\n // env assignments (`FOO=bar baz` → `baz`).\n const tokens = last.split(/\\s+/).filter(t => !/^[A-Z_]\\w*=/i.test(t))\n return tokens[0] ?? ''\n}\n","import type { ToolContext, ToolDef } from './types'\nimport { Buffer } from 'node:buffer'\nimport { previewLine } from '../chat/format'\nimport { interpretShellResult } from './shell-semantics'\n\n/**\n * Execute a shell command in the agent's execution context.\n *\n * Truncation is **tail-priority**: when stdout+stderr combined exceeds\n * `maxOutputBytes`, the head is dropped and a marker `…(N bytes truncated\n * from head)…` is inserted before the tail. Errors and exit summaries\n * usually live at the end of output, so keeping the tail preserves the\n * model's most useful signal.\n *\n * Defaults are tuned for typical commands (build output, test runs): the\n * combined cap is 32 KiB and the per-call timeout follows the execution\n * context's own default (30 s for in-process).\n */\n\nconst DEFAULT_MAX_OUTPUT_BYTES = 32_768\n\n/**\n * Best-effort read-only allow-list for the leading command token. Members\n * are commands whose stock behavior cannot mutate the workspace under any\n * argument combination — `ls`, `cat`, `pwd`, etc. Commands that *can*\n * mutate depending on flags (`find -delete`, `git tag <name>`, `tar -x`)\n * are intentionally excluded; the input-aware {@link isReadOnlyShellCommand}\n * predicate falls back to the conservative \"not safe\" answer for them, so\n * the scheduler barriers them.\n */\nconst SHELL_READ_ONLY_COMMANDS: ReadonlySet<string> = new Set([\n 'ls',\n 'cat',\n 'head',\n 'tail',\n 'wc',\n 'pwd',\n 'whoami',\n 'id',\n 'date',\n 'uname',\n 'hostname',\n 'tty',\n 'echo',\n 'printf',\n 'env',\n 'printenv',\n 'which',\n 'type',\n 'command',\n 'file',\n 'stat',\n 'grep',\n 'rg',\n 'ag',\n 'true',\n 'false',\n 'test',\n // Intentionally NOT included: `sed`, `awk`, `find`, `tar`, `xargs`,\n // `tee` — all can write to disk depending on flags / script body\n // (`sed -i`, `awk 'BEGIN { print > \"f\" }'`, `find -delete`, `tar\n // -x`). Conservative omission keeps the predicate fail-closed.\n])\n\n/**\n * `git` subcommands that are pure reads regardless of arguments. Excludes\n * `branch`/`tag`/`remote` (which can mutate when given a name) and\n * `config` (which writes when given a value).\n */\nconst GIT_READ_ONLY_SUBCOMMANDS: ReadonlySet<string> = new Set([\n 'status',\n 'log',\n 'diff',\n 'show',\n 'blame',\n 'rev-parse',\n 'ls-files',\n 'ls-tree',\n 'cat-file',\n 'reflog',\n 'shortlog',\n 'describe',\n 'rev-list',\n 'name-rev',\n 'whatchanged',\n 'merge-base',\n 'symbolic-ref',\n])\n\n/**\n * Conservative read-only verdict for a shell command — used to opt a\n * `shell` invocation into the scheduler's concurrent fleet. Returns\n * `false` (fail-closed) on anything ambiguous so the scheduler barriers\n * it. Specifically:\n *\n * - Rejects compound commands (`;`, `&&`, `||`, `|`) and redirects (`>`,\n * `>>`, `<`) — even a pipe to a read-only sink is treated as too\n * complex to analyze.\n * - Rejects subshell / process substitution (`$(...)`, `` `...` ``,\n * `<(...)`, `>(...)`).\n * - Skips leading `VAR=value` env assignments to find the real\n * command token.\n * - Strips a possible absolute path on the command (`/usr/bin/ls` → `ls`).\n * - Allows the command iff its base name is in\n * {@link SHELL_READ_ONLY_COMMANDS} OR it's `git <subcmd>` where\n * `<subcmd>` is in {@link GIT_READ_ONLY_SUBCOMMANDS}.\n *\n * Cheap (no spawned process; regex + token scan). Safe to call from the\n * hot scheduler path.\n */\nexport function isReadOnlyShellCommand(command: unknown): boolean {\n if (typeof command !== 'string')\n return false\n const trimmed = command.trim()\n if (trimmed === '')\n return false\n\n // Shell metacharacters that could enable mutation, command chaining,\n // or backgrounding — any of these takes the command out of the\n // \"single read invocation\" shape we can analyze safely. Pipes (`|`)\n // are rejected even when piping to a read-only sink because parsing\n // both sides is more complexity than the win.\n //\n // >, <, >> — redirects\n // ;, &, \\n — command separators (and `&` covers backgrounding)\n // |, ||, && — pipes + boolean chains\n // `…`, $(…) — command substitution\n // <(…), >(…) — process substitution\n if (/[<>;&|`\\n]/.test(trimmed))\n return false\n if (trimmed.includes('$(') || trimmed.includes('<(') || trimmed.includes('>('))\n return false\n\n const tokens = trimmed.split(/\\s+/)\n let i = 0\n // Skip leading VAR=value env assignments (e.g. `FOO=bar ls`).\n while (i < tokens.length && /^[A-Z_]\\w*=/i.test(tokens[i]))\n i++\n const head = tokens[i]\n if (!head)\n return false\n\n // Strip a leading path: `/usr/bin/ls` → `ls`.\n const base = head.split('/').pop() ?? head\n if (SHELL_READ_ONLY_COMMANDS.has(base))\n return true\n if (base === 'git') {\n const sub = tokens[i + 1]\n return typeof sub === 'string' && GIT_READ_ONLY_SUBCOMMANDS.has(sub)\n }\n return false\n}\n\n/**\n * Canonical sibling tools that, when registered alongside `shell`, are worth\n * nudging the model toward — re-running `ls`/`cat` to \"verify\" a path the\n * model already inspected through a dedicated tool is a measurable Kimi/Sonnet\n * failure mode. Each entry maps the canonical tool name to the line rendered\n * in the description; the rendered tool name itself is alias-resolved at\n * render time so deployments that wire-rename (e.g. `read_file` → `Read`)\n * still point at a name the model recognizes from the tool spec.\n */\nconst SHELL_SWAP_HINTS: ReadonlyArray<{ canonical: string, label: string, instead: string }> = [\n { canonical: 'read_file', label: 'Read files', instead: 'cat/head/tail' },\n { canonical: 'glob', label: 'File search', instead: 'find/ls' },\n { canonical: 'grep', label: 'Content search', instead: 'grep/rg' },\n { canonical: 'list_files', label: 'Directory listings', instead: 'ls' },\n { canonical: 'edit', label: 'Edit files', instead: 'sed/awk' },\n { canonical: 'write_file', label: 'Write files', instead: 'echo>/heredoc' },\n]\n\n/**\n * Build the `shell` tool's description text.\n *\n * - The background-mode paragraphs are appended only when `allowBackground`\n * is true so the model isn't pointed at a feature the agent has disabled.\n * - The \"prefer the dedicated tool\" swap block is appended only for siblings\n * actually present in `registeredCanonicals`. Hosts that ship `shell`\n * without `read_file`/`glob`/etc. don't see misleading nudges. Aliased\n * names are rendered via `toolAliases` so the printed name matches what\n * the model sees in the tool spec.\n */\nfunction buildShellDescription({\n allowBackground,\n registeredCanonicals,\n toolAliases,\n}: {\n allowBackground: boolean\n registeredCanonicals?: ReadonlySet<string>\n toolAliases?: Record<string, string>\n}): string {\n const lines = [\n 'Execute a shell command in the project root and return its combined stdout/stderr.',\n 'Output is tail-priority truncated at 32 KiB by default; errors and exit-code summaries live in the tail.',\n 'By default each call appends a `(exit N, Nms)` footer and surfaces non-empty stderr in a separate section even on success — set `metadata: false` to return only stdout. Set maxOutputBytes=0 to disable truncation.',\n ]\n\n if (registeredCanonicals && registeredCanonicals.size > 0) {\n const swaps: string[] = []\n for (const { canonical, label, instead } of SHELL_SWAP_HINTS) {\n if (!registeredCanonicals.has(canonical))\n continue\n const wireName = toolAliases?.[canonical] ?? canonical\n swaps.push(`- ${label}: use \\`${wireName}\\` (not ${instead})`)\n }\n if (swaps.length > 0) {\n lines.push(\n '',\n 'When a dedicated tool fits, prefer it over shell:',\n ...swaps,\n '',\n 'Re-running `ls`/`cat` on the same path is not useful — the prior result is still current unless you wrote to that path since.',\n )\n }\n }\n\n if (allowBackground) {\n lines.push(\n '',\n 'Long-running commands (`npm run dev`, `python train.py`, anything that would otherwise block your turn for minutes) → `run_in_background: true`. The call returns immediately with `{ task_id, output_path, pid }`; stdout + stderr stream to the log file at `output_path`.',\n '',\n 'After spawning a background task: end your current turn (do NOT keep iterating). A `<task-notification>` arrives on the agent\\'s NEXT user-turn with the final status. Polling the log file in a loop wastes tokens and blocks your turn — the notification IS the wake-up. If you NEED to check progress immediately (rare), call `read_file({ path: output_path, ... })` exactly once and decide. To terminate, use `shell_kill({ task_id })`.',\n '',\n 'When called from inside a `spawn`\\'d subagent: you have NO next user-turn — your `agent.run` ends as soon as you finish responding. Start the background task, return a brief summary including the `task_id`, and end your turn. Ownership of the task is transferred to the parent agent when your run finishes; the parent will see the notification on ITS next user-turn.',\n )\n }\n return lines.join('\\n')\n}\n\n/**\n * Build the `shell` tool's JSON-schema. The `run_in_background` field\n * is included only when `allowBackground` is true; the `timeout` /\n * `maxOutputBytes` / `metadata` field descriptions also drop their\n * \"Ignored in background mode\" qualifier when there's no background\n * mode to ignore.\n */\nfunction buildShellInputSchema({ allowBackground }: { allowBackground: boolean }): Record<string, unknown> {\n const bgQualifier = allowBackground ? ' Ignored in background mode.' : ''\n const bgQualifierOutput = allowBackground ? ' Ignored in background mode (output streams to disk).' : ''\n const properties: Record<string, unknown> = {\n command: { type: 'string', description: 'Shell command to run.' },\n timeout: { type: 'integer', description: `Per-call timeout in milliseconds.${bgQualifier}` },\n maxOutputBytes: { type: 'integer', description: `Truncate combined stdout+stderr beyond this many bytes. Default: 32768. Set 0 for unlimited.${bgQualifierOutput}` },\n metadata: { type: 'boolean', description: `Append \\`(exit N, Nms)\\` footer and surface non-empty stderr on success. Default: true.${bgQualifier}` },\n }\n if (allowBackground) {\n properties.run_in_background = { type: 'boolean', description: 'Start the command in the background, returning a task handle. See the tool description for the full flow.' }\n }\n return {\n type: 'object',\n properties,\n required: ['command'],\n }\n}\n\nexport interface CreateShellToolOptions {\n /**\n * Whether to expose the `run_in_background` flag in the input schema +\n * the background-mode paragraphs in the description. When `false`, the\n * model never sees the flag and won't try to use it. The execute path\n * still has a defensive fallback: an explicit `run_in_background: true`\n * call (e.g. from a hand-crafted message) returns a clean error rather\n * than silently running foreground.\n *\n * Default: `true`.\n */\n allowBackground?: boolean\n\n /**\n * Canonical names of tools registered alongside `shell` on the same\n * agent. When non-empty, the description gains a \"prefer the dedicated\n * tool\" block for each known sibling (`read_file`, `glob`, `grep`,\n * `list_files`, `edit`, `write_file`) — useful against the\n * `ls`/`cat`-to-re-verify loop some models fall into when both a\n * dedicated tool AND `shell` are visible. Unknown / unrecognized names\n * are ignored.\n *\n * Set by `createAgent` per-run from the tool registry; hosts that\n * construct a `shell` directly can pass it explicitly. Omit to suppress\n * the block entirely (no nudge for shell-only agents, no nudge for\n * hosts that prefer to author their own anti-loop prose).\n */\n registeredCanonicals?: ReadonlySet<string>\n\n /**\n * The agent's `toolAliases` map, used to render the wire-level name of\n * each sibling in the swap block. Without this, the block always prints\n * canonical names — fine for the default preset, wrong for hosts that\n * alias-rename (the model would be told to call a name it doesn't see\n * in the tool spec).\n */\n toolAliases?: Record<string, string>\n}\n\n/**\n * Factory for the `shell` tool. The default exported `shell` is\n * equivalent to `createShellTool({ allowBackground: true })`. The\n * factory is the entry point hosts use when they want to override the\n * default — e.g. to ship a preset that always disables background mode\n * regardless of `behavior.tasksDir`.\n *\n * Hosts that use the framework's `createAgent` typically don't need to\n * call this directly: when `behavior.tasksDir` is unset or\n * `behavior.disableBackgroundTasks: true` is set, the agent\n * automatically rewrites the registered `shell` (if it's the\n * framework's built-in) using this factory.\n */\nexport function createShellTool(opts: CreateShellToolOptions = {}): ToolDef {\n const allowBackground = opts.allowBackground !== false\n const { registeredCanonicals, toolAliases } = opts\n return {\n // Conditional concurrency safety. Read-only commands (`ls`, `cat`,\n // `git status`, `rg`, …) fan out with other safe siblings; anything\n // mutating or ambiguous barriers. See {@link isReadOnlyShellCommand}\n // for the exact allow-list.\n isConcurrencySafe: input => isReadOnlyShellCommand(input.command),\n spec: {\n name: 'shell',\n description: buildShellDescription({ allowBackground, registeredCanonicals, toolAliases }),\n inputSchema: buildShellInputSchema({ allowBackground }),\n },\n async execute({ command, timeout, maxOutputBytes, metadata, run_in_background }, ctx: ToolContext) {\n const cmd = command as string\n\n // Background mode — dispatches via `execBackground` and returns\n // immediately. The model gets a structured one-liner naming the\n // task id + output path; the actual completion notification arrives\n // on the next turn via the agent's `<task-notification>` injection\n // (see `pendingTaskNotifications` in `src/agent.ts`).\n //\n // Defense-in-depth: when this tool variant has `allowBackground:\n // false` the field isn't in the schema, but a forged input could\n // still set it — surface a clean error instead of silently\n // running foreground (which would surprise the caller).\n if (run_in_background === true) {\n if (!allowBackground)\n return 'shell error: background mode is disabled for this agent (no `behavior.tasksDir` set, or `behavior.disableBackgroundTasks: true`). Fall back to foreground (drop `run_in_background`).'\n return runBackground(cmd, ctx)\n }\n\n // `ExecutionContext.exec` accepts a timeout in seconds; the tool surface\n // takes it in milliseconds so the value lines up with the rest of zidane's\n // ms-based timing. Round up so a 500ms request doesn't collapse to 0.\n //\n // `signal` is what makes `agent.cancelTool(callId)` actually reach\n // the underlying OS process. The loop hands us a unioned signal\n // (`AbortSignal.any([ctx.signal_run, perCallAbort.signal])`) via\n // `ctx.signal`; forwarding it into `exec` lets `child_process.exec`\n // send SIGTERM the moment any of the three layers (run abort,\n // per-call cancel, sibling-cascade) aborts. Without this, a\n // `sleep 60` survived its own cancellation message — orphaned\n // process, observable via `ps` after the run had supposedly\n // finished.\n const execOpts: { timeout?: number, signal?: AbortSignal } = { signal: ctx.signal }\n if (typeof timeout === 'number' && Number.isFinite(timeout) && timeout > 0)\n execOpts.timeout = Math.max(1, Math.ceil(timeout / 1000))\n\n const wantMetadata = metadata !== false\n const startedAt = Date.now()\n const result = await ctx.execution.exec(ctx.handle, cmd, execOpts)\n const durationMs = Date.now() - startedAt\n\n const cap = normalizeCap(maxOutputBytes)\n const semantic = interpretShellResult(cmd, result.exitCode)\n\n // True success (exit 0): stdout body, tail-truncated. With metadata,\n // surface non-empty stderr in a separate section (warnings, deprecation\n // notices) and append an `(exit 0, Nms)` footer.\n if (result.exitCode === 0) {\n const stdoutTail = truncateTail(result.stdout || '(no output)', cap)\n if (!wantMetadata)\n return stdoutTail\n const stderrTrimmed = result.stderr.trim()\n const stderrSection = stderrTrimmed\n ? `\\n[stderr]\\n${truncateTail(stderrTrimmed, Math.min(cap, 2048))}`\n : ''\n return `${stdoutTail}${stderrSection}\\n(exit 0, ${durationMs}ms)`\n }\n\n // Per-command semantic override: non-zero exit codes that are NOT errors\n // (grep no-match, diff differ, find partial, test false). Don't surface\n // `Exit code N` — that misleads the model into retrying. Append a short\n // footer with the semantic interpretation so the model sees the why.\n if (!semantic.isError) {\n const body = (result.stdout || result.stderr || '').trim()\n const tail = truncateTail(body, cap)\n const semanticFooter = semantic.message ? `\\n(${semantic.message})` : ''\n const timingFooter = wantMetadata ? `\\n(exit ${result.exitCode}, ${durationMs}ms)` : ''\n const head = tail.length > 0 ? tail : (semantic.message ?? '(no output)')\n return `${head}${semanticFooter}${timingFooter}`\n }\n\n // Real failure → keep the `Exit code N` line outside the truncation budget.\n // Truncating the joined body otherwise drops the exit-code prefix (it sits at\n // the head) the moment stdout overflows, leaving the model unable to tell\n // success from failure on large-output commands.\n const combined = `${result.stdout}\\n${result.stderr}`.trim()\n const header = wantMetadata\n ? `Exit code ${result.exitCode} (${durationMs}ms)`\n : `Exit code ${result.exitCode}`\n return `${header}\\n${truncateTail(combined, cap)}`\n },\n }\n}\n\n/**\n * Default `shell` tool with background mode enabled.\n *\n * Most hosts use this directly via `basicTools`. When the agent's\n * `behavior.tasksDir` is unset OR `behavior.disableBackgroundTasks:\n * true` is set, `createAgent` auto-rewrites this identity to a\n * `createShellTool({ allowBackground: false })` variant so the model\n * never sees a flag it can't use. Hosts who want to bypass that\n * auto-rewrite can register a `createShellTool({ allowBackground })`\n * directly — the rewrite only fires on identity-equal references to\n * this constant.\n */\nexport const shell: ToolDef = createShellTool({ allowBackground: true })\n\n/**\n * Background-mode entry point for the `shell` tool. Settles fast,\n * registers an `onExit` callback that fires `background:exit` on the\n * agent's hook bus, and returns a structured one-liner to the model.\n *\n * Reachable only via the `allowBackground: true` variant; the\n * `allowBackground: false` variant short-circuits before this is\n * called and `createAgent` auto-rewrites the built-in to the\n * `false` variant when `behavior.tasksDir` is unset or\n * `behavior.disableBackgroundTasks: true` is set. The runtime checks\n * below are defense-in-depth for hosts who skip the auto-rewrite\n * (custom shell tool, run-level tools override, etc.):\n *\n * - `behavior.tasksDir` unset → host opted into the schema but\n * forgot to wire the log dir. Clean error, model can fall back to\n * foreground.\n * - `ctx.execution.execBackground` undefined → the context doesn't\n * support it (some remote sandboxes).\n * - `mkdir` on the output dir fails → the underlying filesystem\n * can't accommodate. Surface the error verbatim.\n */\nasync function runBackground(command: string, ctx: ToolContext): Promise<string> {\n const tasksDir = ctx.behavior?.tasksDir\n if (typeof tasksDir !== 'string' || tasksDir.length === 0) {\n return 'shell error: background mode requires `behavior.tasksDir` to be set on the agent. The host has not opted into background tasks — either fall back to foreground (drop `run_in_background`) or ask the user to enable it.'\n }\n if (!ctx.execution.execBackground) {\n return `shell error: the active execution context (${ctx.execution.type}) does not support background tasks. Fall back to foreground (drop \\`run_in_background\\`).`\n }\n\n try {\n const handle = await ctx.execution.execBackground(ctx.handle, command, {\n outputDir: tasksDir,\n onExit: (info) => {\n // Fire-and-forget. `callHook` returns a Promise (it may run\n // async listeners); we don't await — the agent's listener is\n // sync map.set, and the lifecycle is \"task already exited;\n // notify whoever cares\". Surface unhandled rejections under\n // ZIDANE_DEBUG only.\n Promise.resolve(ctx.hooks.callHook('background:exit', info))\n .catch((err: unknown) => {\n if (process.env.ZIDANE_DEBUG)\n process.stderr.write(`[zidane/shell] background:exit hook rejected: ${err instanceof Error ? err.message : String(err)}\\n`)\n })\n },\n })\n\n // Fire `background:start` AFTER the handle is returned so listeners\n // see a usable id + pid + path. Observational only — no caller\n // depends on this hook for behavior, so we don't await it on the\n // critical path.\n Promise.resolve(ctx.hooks.callHook('background:start', {\n taskId: handle.taskId,\n pid: handle.pid,\n command,\n cwd: ctx.handle.cwd,\n outputPath: handle.outputPath,\n startedAt: Date.now(),\n })).catch((err: unknown) => {\n if (process.env.ZIDANE_DEBUG)\n process.stderr.write(`[zidane/shell] background:start hook rejected: ${err instanceof Error ? err.message : String(err)}\\n`)\n })\n\n const cmdPreview = previewLine(command, 60)\n // Depth-aware wake-up instruction. Subagents (depth > 0) get a\n // run-once `agent.run()` so there's no \"next turn\" for them to\n // observe the notification on; the notification ends up on the\n // PARENT's next user-turn after `spawn.ts`'s `reassignBackgroundTasks`\n // transfers ownership at child-destroy time. Telling a subagent to\n // \"wait for your next turn\" misleads it into polling, which blocks\n // the parent's spawn call until maxTurns hits.\n const inSubagent = (ctx.depth ?? 0) > 0\n const wakeupHint = inSubagent\n ? 'You are inside a `spawn`\\'d subagent — you have NO next user-turn to receive the notification on. Return a brief summary INCLUDING this task_id and end your turn now. Ownership of the task transfers to the parent agent when your run completes; the parent will see the `<task-notification>` on its next user-turn.'\n : 'The task is running in the background. You\\'ll receive a <task-notification> on your NEXT user-turn with the final status. End your current turn now — do NOT poll the output file in a loop, the notification IS the wake-up. To inspect progress, call `read_file({ path: <output> })` once. To terminate, use `shell_kill({ task_id })`.'\n return [\n `Started ${handle.taskId} (pid ${handle.pid}).`,\n ` command: ${cmdPreview}`,\n ` output: ${handle.outputPath}`,\n '',\n wakeupHint,\n ].join('\\n')\n }\n catch (err) {\n const msg = err instanceof Error ? err.message : String(err)\n return `shell error: failed to start background task: ${msg}`\n }\n}\n\nfunction normalizeCap(value: unknown): number {\n if (typeof value !== 'number' || !Number.isFinite(value))\n return DEFAULT_MAX_OUTPUT_BYTES\n if (value < 0)\n return DEFAULT_MAX_OUTPUT_BYTES\n return Math.floor(value)\n}\n\n/**\n * Tail-priority byte truncation. When `text` exceeds `cap` bytes, the head is\n * dropped and replaced with a marker. Always cuts on character boundaries (no\n * mid-codepoint splits) by walking from the end with `Buffer.byteLength`.\n *\n * `cap === 0` disables truncation. `cap` is interpreted as a UTF-8 byte budget\n * for the tail itself — the marker is added on top and may push the visible\n * length slightly past `cap`. That tradeoff is intentional: a marker that\n * always fits inside the budget would shrink the actual content displayed.\n */\nfunction truncateTail(text: string, cap: number): string {\n if (cap === 0)\n return text\n\n const totalBytes = Buffer.byteLength(text)\n if (totalBytes <= cap)\n return text\n\n // Walk back from the end, accumulating UTF-8 bytes per character, until\n // we've gathered up to `cap` bytes. Slicing on char index ensures we never\n // split a multi-byte codepoint mid-stream.\n let bytes = 0\n let charIdx = text.length\n while (charIdx > 0) {\n const ch = text[charIdx - 1]\n const chBytes = Buffer.byteLength(ch)\n if (bytes + chBytes > cap)\n break\n bytes += chBytes\n charIdx--\n }\n\n const tail = text.slice(charIdx)\n const droppedBytes = totalBytes - Buffer.byteLength(tail)\n return `…(${droppedBytes} bytes truncated from head)…\\n${tail}`\n}\n","/**\n * Heuristics for detecting binary content in UTF-8-decoded strings.\n *\n * `ExecutionContext.readFile` always returns text. Invalid bytes survive\n * decoding as U+FFFD (replacement char), and genuinely binary files often\n * contain NUL bytes — UTF-8 text legitimately should not. These helpers\n * give tools a cheap way to bail before drowning the model in mojibake.\n */\n\nconst SNIFF_BYTES = 8192\nconst REPLACEMENT_RATIO_THRESHOLD = 0.01\nconst REPLACEMENT_MIN_COUNT = 5\n\n/**\n * True if a NUL (`\\x00`) appears in the leading sample. Cheap, no false\n * positives on real text — UTF-8 never embeds NUL, so any NUL means the\n * source bytes were binary.\n */\nexport function containsNullByte(text: string, sniffBytes = SNIFF_BYTES): boolean {\n const sample = text.length > sniffBytes ? text.slice(0, sniffBytes) : text\n for (let i = 0; i < sample.length; i++) {\n if (sample.charCodeAt(i) === 0)\n return true\n }\n return false\n}\n\n/**\n * Heavier check used by `read_file`:\n * - NUL byte ⇒ binary,\n * - or U+FFFD count ≥ minimum AND ratio over threshold ⇒ binary.\n *\n * The replacement-char ratio + minimum guards against tripping on a\n * one-or-two-stray-replacement-char text file (config dumps, editor logs).\n */\nexport function looksBinary(text: string, sniffBytes = SNIFF_BYTES): boolean {\n const sample = text.length > sniffBytes ? text.slice(0, sniffBytes) : text\n if (sample.length === 0)\n return false\n\n let replacementCount = 0\n for (let i = 0; i < sample.length; i++) {\n const code = sample.charCodeAt(i)\n if (code === 0)\n return true\n if (code === 0xFFFD)\n replacementCount++\n }\n return replacementCount >= REPLACEMENT_MIN_COUNT\n && replacementCount / sample.length > REPLACEMENT_RATIO_THRESHOLD\n}\n","/**\n * `skills_read` tool — reads a bundled resource file from an active skill.\n *\n * Requires the skill to be active (model must have called `skills_use` first).\n * Paths are validated against the skill's `baseDir` to prevent directory\n * traversal. File I/O goes through `ctx.execution.readFile` so docker/sandbox\n * execution contexts work identically to in-process.\n */\n\nimport type { SkillActivationState } from '../skills/activation'\nimport type { SkillConfig } from '../skills/types'\nimport type { ToolContext, ToolDef } from './types'\nimport { errorMessage } from '../errors'\nimport { validateResourcePath } from '../skills/validate'\nimport { containsNullByte } from './binary-detect'\n\nexport interface SkillsReadToolOptions {\n catalog: readonly SkillConfig[]\n state: SkillActivationState\n}\n\nexport function createSkillsReadTool(options: SkillsReadToolOptions): ToolDef {\n const byName = new Map(options.catalog.map(s => [s.name, s]))\n\n return {\n // Read-only over the skill catalog — concurrency-safe.\n isConcurrencySafe: true,\n spec: {\n name: 'skills_read',\n description:\n 'Read a bundled resource file from an active skill. '\n + 'The skill must have been activated via skills_use first. '\n + 'Path is relative to the skill\\'s directory (e.g. \"references/REFERENCE.md\", \"assets/template.txt\").',\n inputSchema: {\n type: 'object',\n properties: {\n name: {\n type: 'string',\n enum: options.catalog.map(s => s.name),\n description: 'The name of the active skill.',\n },\n path: {\n type: 'string',\n description: 'Path to the resource, relative to the skill root. Cannot escape the skill directory.',\n },\n },\n required: ['name', 'path'],\n additionalProperties: false,\n },\n },\n\n async execute(input, ctx: ToolContext): Promise<string> {\n const skillName = input.name as string\n const relPath = input.path as string\n\n const skill = byName.get(skillName)\n if (!skill)\n return `Error: unknown skill \"${skillName}\".`\n\n if (!options.state.isActive(skillName))\n return `Error: skill \"${skillName}\" is not active. Call skills_use with name: \"${skillName}\" first.`\n\n if (!skill.baseDir) {\n return (\n `Error: skill \"${skillName}\" has no base directory `\n + '(likely an inline skill without bundled resources); cannot read files.'\n )\n }\n\n const validated = validateResourcePath(relPath, skill.baseDir)\n if (!validated.valid)\n return `Error: ${validated.error}`\n\n let content: string\n try {\n content = await ctx.execution.readFile(ctx.handle, validated.absolutePath)\n }\n catch (err) {\n return `Error reading \"${relPath}\" in skill \"${skillName}\": ${errorMessage(err)}`\n }\n\n if (containsNullByte(content)) {\n // `ExecutionContext.readFile` returns a UTF-8 string — invalid UTF-8\n // bytes get replacement chars, so a round-trip through base64 would\n // corrupt the payload. Instead of pretending we can deliver binary\n // bytes, return a descriptor that tells the agent where the file is.\n // A future `readFileBytes(handle, path)` API on ExecutionContext\n // would unlock proper inline delivery; until then, host adapters\n // needing binaries should stream them out-of-band.\n return JSON.stringify({\n kind: 'binary-unsupported',\n path: validated.absolutePath,\n note:\n 'This file appears to be binary. The skills_read tool returns text only; '\n + 'binary files are not delivered through the execution context\\'s text-based readFile API.',\n })\n }\n\n return content\n },\n }\n}\n","/**\n * Shared shell-argument quoter for tool implementations.\n *\n * Single source of truth so `grep`, `binary-read`, and `skills-run-script`\n * don't drift on the POSIX `'\\''` escape pattern.\n */\n\nconst SAFE_TOKEN_RE = /^[\\w@%+=:,./-]+$/\nconst SINGLE_QUOTE_RE = /'/g\n\n/**\n * Wrap an argument in single quotes, escaping embedded single quotes via the\n * standard POSIX `'\\''` close-escape-reopen trick. Tokens that are already\n * shell-safe pass through unchanged so command lines stay readable in logs.\n *\n * NOT a sandbox — only safe when the caller controls the surrounding shell\n * context. Use it for arguments only, never for the verb / subcommand.\n */\nexport function shellQuote(arg: string): string {\n if (SAFE_TOKEN_RE.test(arg))\n return arg\n return `'${arg.replace(SINGLE_QUOTE_RE, '\\'\\\\\\'\\'')}'`\n}\n\n/**\n * Variant that always quotes — useful when the caller doesn't want a\n * conditional `unquoted-when-safe` branch (consistent log shape, paranoid\n * inputs that contain whitespace by construction).\n */\nexport function alwaysQuote(arg: string): string {\n return `'${arg.replace(SINGLE_QUOTE_RE, '\\'\\\\\\'\\'')}'`\n}\n","/**\n * `skills_run_script` tool — executes a script from an active skill's\n * `scripts/` directory via the agent's execution context.\n *\n * Path is validated against the skill's `baseDir` and constrained to the\n * `scripts/` subdirectory. Timeout is configurable via\n * `SkillsConfig.scriptTimeoutMs` (default 60 s).\n */\n\nimport type { SkillActivationState } from '../skills/activation'\nimport type { SkillConfig } from '../skills/types'\nimport type { ToolContext, ToolDef } from './types'\nimport { errorMessage } from '../errors'\nimport { validateResourcePath } from '../skills/validate'\nimport { alwaysQuote } from './shell-quote'\n\nexport interface SkillsRunScriptToolOptions {\n catalog: readonly SkillConfig[]\n state: SkillActivationState\n /** Script timeout in milliseconds. Default 60000. */\n scriptTimeoutMs?: number\n}\n\nconst ABS_WINDOWS_RE = /^[a-z]:[\\\\/]/i\nconst COLLAPSE_SLASHES_RE = /\\/+/g\n\nexport function createSkillsRunScriptTool(options: SkillsRunScriptToolOptions): ToolDef {\n const byName = new Map(options.catalog.map(s => [s.name, s]))\n const timeoutMs = options.scriptTimeoutMs ?? 60_000\n\n return {\n spec: {\n name: 'skills_run_script',\n description:\n 'Execute a script bundled with an active skill (from its scripts/ directory). '\n + 'The skill must have been activated via skills_use first. '\n + 'Returns stdout, stderr, and the exit code. Honors the script\\'s shebang.',\n inputSchema: {\n type: 'object',\n properties: {\n name: {\n type: 'string',\n enum: options.catalog.map(s => s.name),\n description: 'The name of the active skill.',\n },\n script: {\n type: 'string',\n description: 'Path to the script relative to the skill\\'s scripts/ directory (e.g. \"extract.py\", \"merge.sh\").',\n },\n args: {\n type: 'array',\n items: { type: 'string' },\n description: 'Optional argv array passed to the script.',\n },\n },\n required: ['name', 'script'],\n additionalProperties: false,\n },\n },\n\n async execute(input, ctx: ToolContext): Promise<string> {\n const skillName = input.name as string\n const scriptRel = input.script as string\n const args = (input.args as string[] | undefined) ?? []\n\n const skill = byName.get(skillName)\n if (!skill)\n return `Error: unknown skill \"${skillName}\".`\n\n if (!options.state.isActive(skillName))\n return `Error: skill \"${skillName}\" is not active. Call skills_use with name: \"${skillName}\" first.`\n\n if (!skill.baseDir)\n return `Error: skill \"${skillName}\" has no base directory (likely an inline skill); cannot run scripts.`\n\n // Reject absolute script paths up front — otherwise they'd be spuriously\n // \"normalized\" under scripts/ by the join below.\n if (scriptRel.startsWith('/') || ABS_WINDOWS_RE.test(scriptRel))\n return `Error: Absolute paths are not allowed (\"${scriptRel}\").`\n\n // Resolve under scripts/ and validate — rejects escapes via `..`.\n const joinedPath = `scripts/${scriptRel}`.replace(COLLAPSE_SLASHES_RE, '/')\n const validated = validateResourcePath(joinedPath, skill.baseDir)\n if (!validated.valid)\n return `Error: ${validated.error}`\n\n // Build a shell command that honors shebang (via POSIX exec). For\n // languages without shebang (e.g. Python on Windows-authored files),\n // the script's first-line `#!/usr/bin/env python3` handles it.\n const cmd = [validated.absolutePath, ...args].map(alwaysQuote).join(' ')\n try {\n // Forward `ctx.signal` so a per-call cancel (or run abort) sends\n // SIGTERM to the script — same rationale as the shell tool: without\n // it, a long-running script keeps consuming compute after the\n // model has already moved on with the cancellation marker.\n const result = await ctx.execution.exec(ctx.handle, cmd, {\n timeout: Math.max(1, Math.round(timeoutMs / 1000)),\n signal: ctx.signal,\n })\n return JSON.stringify({\n exitCode: result.exitCode,\n stdout: result.stdout,\n stderr: result.stderr,\n })\n }\n catch (err) {\n return `Error running script \"${scriptRel}\" for skill \"${skillName}\": ${errorMessage(err)}`\n }\n },\n }\n}\n","/**\n * `skills_use` tool — activates or deactivates a skill from the model side.\n *\n * Implements tier 2 of progressive disclosure per the Agent Skills spec.\n *\n * Two modes:\n *\n * - `mode: \"activate\"` (default, spec-mandated) — loads the skill's full\n * instructions, fires `skills:activate` with `via: 'model'`, and\n * returns the frontmatter-stripped body wrapped in `<skill_content>`\n * tags. Shell-interpolation (`!`` `cmd` ``) runs per-activation so\n * values like `gh pr diff` reflect the current state.\n * - `mode: \"deactivate\"` (zidane extension) — releases an active skill,\n * fires `skills:deactivate` with `reason: 'model'`. The matching\n * skill's `allowed-tools` restrictions stop applying on the next\n * dispatch. Use this when the skill's job is done and its tool\n * allow-list is now blocking unrelated follow-up work.\n *\n * The deactivate mode is what lets the model recover from a stuck\n * `AgentToolNotAllowedError` (the canonical hint of which points the\n * model right back here) without waiting for a run boundary.\n */\n\nimport type { Hookable } from 'hookable'\nimport type { AgentHooks } from '../agent'\nimport type { SkillActivationState } from '../skills/activation'\nimport type { SkillConfig } from '../skills/types'\nimport type { ToolContext, ToolDef } from './types'\nimport { interpolateShellCommands } from '../skills/interpolate'\nimport { escapeXml } from '../xml'\n\nexport interface SkillsUseToolOptions {\n /** Resolved skills catalog for this run. */\n catalog: readonly SkillConfig[]\n /** Per-agent activation state the tool mutates. */\n state: SkillActivationState\n /** Agent hooks — used to fire `skills:activate` on first activation. */\n hooks: Hookable<AgentHooks>\n}\n\nconst MAX_RESOURCE_LIST = 50\n\nfunction buildSkillContentWrapper(skill: SkillConfig, body: string): string {\n const parts: string[] = []\n parts.push(`<skill_content name=\"${escapeXml(skill.name)}\" spec_version=\"0.1\">`)\n parts.push(body)\n\n if (skill.baseDir) {\n parts.push('')\n parts.push(`Skill directory: ${skill.baseDir}`)\n parts.push('Relative paths resolve against this directory.')\n }\n\n if (skill.resources?.length) {\n parts.push('')\n parts.push('<skill_resources>')\n const shown = skill.resources.slice(0, MAX_RESOURCE_LIST)\n for (const res of shown) {\n parts.push(` <file type=\"${res.type}\">${escapeXml(res.path)}</file>`)\n }\n if (skill.resources.length > MAX_RESOURCE_LIST) {\n parts.push(` <!-- …(${skill.resources.length - MAX_RESOURCE_LIST} more) -->`)\n }\n parts.push('</skill_resources>')\n }\n\n if (skill.compatibility) {\n parts.push('')\n parts.push(`Compatibility: ${skill.compatibility}`)\n }\n\n if (skill.allowedTools?.length) {\n parts.push(`Allowed tools: ${skill.allowedTools.join(' ')}`)\n }\n\n parts.push('</skill_content>')\n return parts.join('\\n')\n}\n\n/**\n * Factory for `skills_use`. Auto-injected into the agent's tool set by the\n * agent runtime when a non-empty skills catalog is available (unless\n * `SkillsConfig.tool === false`).\n *\n * The tool schema's `name` property is `enum`-constrained to the resolved\n * catalog so the LLM cannot hallucinate a skill that doesn't exist.\n */\nexport function createSkillsUseTool(options: SkillsUseToolOptions): ToolDef {\n const byName = new Map(options.catalog.map(s => [s.name, s]))\n // Cache interpolated bodies for the lifetime of this tool instance. The\n // agent rebuilds skills tools per-run, so this cache naturally resets on\n // each run boundary — avoiding stale shell output from a prior run.\n const interpolatedBodyCache = new Map<string, string>()\n\n return {\n spec: {\n name: 'skills_use',\n description:\n 'Activate or deactivate a specialized skill. '\n + 'Call with `mode: \"activate\"` (default) to load a skill\\'s full instructions when a task matches its catalog description. '\n + 'Call with `mode: \"deactivate\"` to release a skill whose allowed-tools restrictions are now in the way — e.g. when the skill\\'s work is done or the active skill is blocking unrelated tool calls. '\n + 'After activating, follow the returned instructions; use skills_read to load referenced files and skills_run_script to execute bundled scripts.',\n inputSchema: {\n type: 'object',\n properties: {\n name: {\n type: 'string',\n enum: options.catalog.map(s => s.name),\n description: 'The name of the skill to activate or deactivate (must be in the available skills catalog).',\n },\n mode: {\n type: 'string',\n enum: ['activate', 'deactivate'],\n description: 'Whether to activate (load + apply the skill) or deactivate (release an active skill). Default: \"activate\".',\n },\n },\n required: ['name'],\n additionalProperties: false,\n },\n },\n\n async execute(input, ctx: ToolContext): Promise<string> {\n const skillName = input.name as string\n const skill = byName.get(skillName)\n if (!skill) {\n const available = [...byName.keys()].join(', ') || '<none>'\n return `Error: unknown skill \"${skillName}\". Available skills: ${available}.`\n }\n\n const mode = (input.mode as string | undefined) ?? 'activate'\n\n if (mode === 'deactivate') {\n const removed = options.state.deactivate(skillName)\n if (!removed) {\n // Idempotent: deactivating a non-active skill is a no-op rather\n // than an error — the model has no reliable way to know whether a\n // prior run-end pass already cleared the activation, and a\n // confirmation is more useful than a failure here.\n return `Skill \"${skillName}\" was not active — nothing to deactivate.`\n }\n await options.hooks.callHook('skills:deactivate', { skill: removed.skill, reason: 'model' })\n const remaining = options.state.active().map(a => a.skill.name)\n const tail = remaining.length > 0\n ? ` Remaining active skills: ${remaining.join(', ')}.`\n : ' No skills are currently active.'\n return `Skill \"${skillName}\" deactivated — its allowed-tools restrictions no longer apply.${tail}`\n }\n\n const wasActive = options.state.isActive(skillName)\n\n if (!wasActive) {\n const outcome = options.state.activate(skill, 'model')\n if (outcome === 'cap-reached') {\n const activeNames = options.state.active().map(a => a.skill.name).join(', ')\n return (\n `Error: cannot activate \"${skillName}\" — the maxActive skill cap has been reached. `\n + `Currently active: ${activeNames}. Call \\`skills_use\\` with \\`mode: \"deactivate\"\\` and one of those names first.`\n )\n }\n await options.hooks.callHook('skills:activate', { skill, via: 'model' })\n }\n\n // Interpolate `!\\`cmd\\`` once per (skill, tool-instance). A second\n // `skills_use` call on the same skill returns the cached body.\n let body = interpolatedBodyCache.get(skillName)\n if (body === undefined) {\n body = skill.instructions.includes('!`')\n ? await interpolateShellCommands(skill.instructions, ctx.execution, ctx.handle)\n : skill.instructions\n interpolatedBodyCache.set(skillName, body)\n }\n\n return buildSkillContentWrapper(skill, body)\n },\n }\n}\n","/**\n * `tool_search` — progressive tool disclosure.\n *\n * Counterpart to `skills_use` for the MCP tool surface: the catalog (name +\n * description) lives in the system prompt; full `inputSchema` payloads load\n * lazily through this tool. After a successful match, the matched tools'\n * canonical names are added to the per-run \"unlocked\" set and become callable\n * immediately (the loop rebuilds `formattedTools` before the next provider\n * request, advertising the unlocked tools to the provider).\n *\n * Aliasing: the tool advertises and matches on the **wire** (alias-rewritten)\n * name — the only name the model ever sees. Internally each lazy entry also\n * carries its `canonicalName`, which is what gets added to the `unlocked` set\n * (the loop's tool registry is keyed by canonical name).\n *\n * Result shape: structured pseudo-XML wrapping a JSON `inputSchema` payload.\n * The schema is inlined verbatim (NOT XML-escaped) so the model can reuse it\n * directly when constructing arguments — escaping the JSON would force the\n * model to mentally un-escape `&quot;` etc. when reasoning about field types.\n */\n\nimport type { ToolContext, ToolDef } from './types'\nimport { escapeXml } from '../xml'\n\nexport interface LazyToolEntry {\n /**\n * Wire name (after `toolAliases` rewrite). What the model sees in the\n * catalog, what `tool_search` matches against, and what the provider's\n * tool list will carry once the entry is unlocked.\n */\n name: string\n /**\n * Canonical (registry-key) name used for unlock-set membership and for the\n * loop's `ctx.tools[name]` dispatch lookup. Equal to `name` when no alias\n * is configured for this tool.\n */\n canonicalName: string\n description: string\n inputSchema: Record<string, unknown>\n /** Source MCP server, when applicable. Used for `server`-bulk unlock. */\n server?: string\n}\n\nexport interface ToolSearchToolOptions {\n /**\n * Snapshot of every lazy tool the model can discover. Built once per run by\n * the agent — the tool closes over this array and never mutates it.\n */\n catalog: readonly LazyToolEntry[]\n /**\n * Mutable per-run set of unlocked **canonical** tool names. The tool adds\n * matches in place; the loop reads the set when rebuilding the wire-level\n * tool list. Keyed by canonical (not wire) so dispatch lookups stay\n * alias-stable.\n */\n unlocked: Set<string>\n /** Default cap on returned matches when the model omits `limit`. */\n defaultLimit?: number\n}\n\nconst DEFAULT_LIMIT = 20\n\nfunction rankByQuery(catalog: readonly LazyToolEntry[], query: string): LazyToolEntry[] {\n const q = query.trim().toLowerCase()\n if (!q)\n return [...catalog]\n\n // Two-tier ranking: name hits beat description hits. Within each tier we\n // preserve registration order so deterministic catalogs produce\n // deterministic results.\n const nameHits: LazyToolEntry[] = []\n const descHits: LazyToolEntry[] = []\n for (const entry of catalog) {\n if (entry.name.toLowerCase().includes(q))\n nameHits.push(entry)\n else if (entry.description.toLowerCase().includes(q))\n descHits.push(entry)\n }\n return [...nameHits, ...descHits]\n}\n\n/**\n * Sanitise a JSON-stringified schema for embedding inside a pseudo-XML tag.\n *\n * The schema is inlined verbatim (NOT XML-escaped) so the model can reuse\n * field types directly when constructing arguments. The only transformation\n * we apply is replacing `<` with `\\u003c` inside string literals — this\n * keeps the JSON byte-equivalent for the model (JSON parsers decode the\n * escape) while preventing a hostile or buggy `inputSchema` from injecting\n * apparent XML tags (`</input_schema>`, `<evil>…`) that would muddle the\n * model's read of the surrounding result envelope.\n *\n * The substitution is safe because `<` is only meaningful when it appears\n * literally; the `\\u003c` form is not a tag character and is identical to\n * `<` after JSON parsing.\n */\nfunction sanitiseSchemaForXml(schemaJson: string): string {\n return schemaJson.replace(/</g, '\\\\u003c')\n}\n\nfunction formatMatch(entry: LazyToolEntry): string {\n const schema = sanitiseSchemaForXml(JSON.stringify(entry.inputSchema))\n const serverAttr = entry.server ? ` server=\"${escapeXml(entry.server)}\"` : ''\n return [\n ` <tool name=\"${escapeXml(entry.name)}\"${serverAttr}>`,\n ` <description>${escapeXml(entry.description)}</description>`,\n ` <input_schema>${schema}</input_schema>`,\n ` </tool>`,\n ].join('\\n')\n}\n\ninterface SelectionResult {\n shown: LazyToolEntry[]\n total: number\n truncated: boolean\n misses: string[]\n query: string | undefined\n server: string | undefined\n}\n\n/**\n * Pure resolver for a `tool_search` invocation. Mirrors the matching the\n * execute path performs (`names` → `server` → `query` → unconstrained fallback,\n * then cap by `limit`). Extracted so the unlock side-effect can be replayed\n * deterministically when a session is resumed — `run()` walks the persisted\n * `tool_search` tool_calls and feeds each historical input through here to\n * rebuild the `unlocked` set seeded by prior runs.\n *\n * Returns the entries the original call would have shown (post-limit), not the\n * full pre-cap match set — the cap is part of what the model actually saw.\n */\nexport function selectToolSearchMatches(\n catalog: readonly LazyToolEntry[],\n input: Record<string, unknown>,\n defaultLimit: number = DEFAULT_LIMIT,\n): SelectionResult {\n const byName = new Map(catalog.map(e => [e.name, e]))\n const byServer = new Map<string, LazyToolEntry[]>()\n for (const entry of catalog) {\n if (!entry.server)\n continue\n const list = byServer.get(entry.server) ?? []\n list.push(entry)\n byServer.set(entry.server, list)\n }\n const maxLimit = Math.max(catalog.length, 1)\n\n const rawQuery = typeof input.query === 'string' ? input.query.trim() : undefined\n const query = rawQuery || undefined\n const namesIn = Array.isArray(input.names)\n ? input.names.filter((n): n is string => typeof n === 'string' && n.length > 0)\n : undefined\n const server = typeof input.server === 'string' && input.server.length > 0 ? input.server : undefined\n const limitIn = typeof input.limit === 'number' && Number.isFinite(input.limit) && input.limit > 0\n ? Math.floor(input.limit as number)\n : defaultLimit\n const limit = Math.min(limitIn, maxLimit)\n\n const matches: LazyToolEntry[] = []\n const seen = new Set<string>()\n const misses: string[] = []\n\n if (namesIn && namesIn.length > 0) {\n for (const n of namesIn) {\n if (seen.has(n))\n continue\n const entry = byName.get(n)\n if (entry) {\n matches.push(entry)\n seen.add(n)\n }\n else {\n misses.push(n)\n }\n }\n }\n\n if (server) {\n const list = byServer.get(server) ?? []\n for (const entry of list) {\n if (seen.has(entry.name))\n continue\n matches.push(entry)\n seen.add(entry.name)\n }\n }\n\n if (query !== undefined) {\n for (const entry of rankByQuery(catalog, query)) {\n if (seen.has(entry.name))\n continue\n matches.push(entry)\n seen.add(entry.name)\n }\n }\n\n if (!namesIn?.length && !server && query === undefined) {\n for (const entry of catalog) {\n matches.push(entry)\n seen.add(entry.name)\n }\n }\n\n const truncated = matches.length > limit\n const shown = truncated ? matches.slice(0, limit) : matches\n return { shown, total: matches.length, truncated, misses, query, server }\n}\n\n/**\n * Replay the unlock side-effect of a single historical `tool_search` call.\n *\n * Used on session resume: every run starts with a fresh `unlocked` set\n * (seeded with eager tools only), but the resumed conversation already\n * shows the model that some lazy tools are callable. Without this replay\n * the model emits a `tool_use` for a tool the gate then refuses with\n * \"load via tool_search first\" — the failure the bug report named.\n *\n * Safe to call repeatedly with the same input; the `Set` add is idempotent.\n * Lookups missing from the current catalog (host changed `mcpServers` or\n * `disclosure` between runs) are silently dropped — the model will get a\n * normal \"tool not callable\" error on its next attempt, which is the\n * correct response when a tool genuinely no longer exists.\n */\nexport function applyToolSearchToUnlocked(\n catalog: readonly LazyToolEntry[],\n input: Record<string, unknown>,\n unlocked: Set<string>,\n defaultLimit?: number,\n): void {\n const { shown } = selectToolSearchMatches(catalog, input, defaultLimit ?? DEFAULT_LIMIT)\n for (const entry of shown)\n unlocked.add(entry.canonicalName)\n}\n\n/**\n * Factory for `tool_search`. Auto-injected by the agent when\n * `behavior.toolDisclosure === 'lazy'` and at least one MCP tool is in the\n * registry. Opt out via `behavior.toolSearch.tool === false`.\n */\nexport function createToolSearchTool(options: ToolSearchToolOptions): ToolDef {\n const defaultLimit = options.defaultLimit ?? DEFAULT_LIMIT\n\n return {\n // Read-only over the in-memory catalog — fan out with other reads.\n isConcurrencySafe: true,\n spec: {\n name: 'tool_search',\n description:\n 'Discover and load schemas for additional tools listed in <searchable_tools>. '\n + 'Tools listed there are advertised by name + description only — their input '\n + 'schemas are not loaded into context until you surface them through this tool. '\n + 'Pass `query` for a substring search, `names` to load specific tools, or `server` '\n + 'to load every tool from one MCP server. Returned tools become callable for the '\n + 'rest of this run.',\n inputSchema: {\n type: 'object',\n properties: {\n query: {\n type: 'string',\n description: 'Substring to match against tool name + description (case-insensitive).',\n },\n names: {\n type: 'array',\n items: { type: 'string' },\n description: 'Explicit tool names to load (bypasses ranking). Use the names shown in <searchable_tools>.',\n },\n server: {\n type: 'string',\n description: 'MCP server name — load every tool from this server.',\n },\n limit: {\n type: 'integer',\n minimum: 1,\n description: `Cap on returned matches. Default: ${defaultLimit}.`,\n },\n },\n additionalProperties: false,\n },\n },\n\n async execute(input, ctx: ToolContext): Promise<string> {\n // Cheap signal check — refuses cleanly if the run was aborted between\n // the model emitting the call and the executor running it.\n if (ctx.signal?.aborted)\n return '<tool_search_results matches=\"0\" aborted=\"true\">Run aborted.</tool_search_results>'\n\n if (options.catalog.length === 0)\n return '<tool_search_results matches=\"0\">No lazy tools registered for this run.</tool_search_results>'\n\n const { shown, total, truncated, misses, query, server } = selectToolSearchMatches(\n options.catalog,\n input,\n defaultLimit,\n )\n\n // Unlock by canonical name — the loop's tool registry is keyed on\n // canonical, so this is what `buildFormattedTools` checks.\n for (const entry of shown)\n options.unlocked.add(entry.canonicalName)\n\n const parts: string[] = []\n const queryAttr = query ? ` query=\"${escapeXml(query)}\"` : ''\n const serverAttr = server ? ` server=\"${escapeXml(server)}\"` : ''\n parts.push(`<tool_search_results matches=\"${shown.length}\" total=\"${total}\"${queryAttr}${serverAttr}>`)\n if (shown.length === 0) {\n parts.push(' No matches. Try a broader query, or omit all parameters to list everything.')\n }\n else {\n for (const entry of shown)\n parts.push(formatMatch(entry))\n parts.push('')\n parts.push(' These tools are now callable. Invoke them by name in subsequent turns.')\n if (truncated) {\n parts.push(` ${total - shown.length} additional matches were truncated — refine the query or raise \\`limit\\`.`)\n }\n }\n if (misses.length > 0) {\n parts.push(` <misses>${misses.map(escapeXml).join(', ')}</misses>`)\n }\n parts.push('</tool_search_results>')\n\n return parts.join('\\n')\n },\n }\n}\n","/**\n * Agent creation and state management.\n */\n\nimport type { Hookable } from 'hookable'\n\nimport type { ExecutionContext, ExecutionHandle, TaskExitInfo } from './contexts'\nimport type { McpConnection } from './mcp'\nimport type { Provider, StreamOptions, ToolResult, ToolSpec } from './providers'\nimport type { Session } from './session'\nimport type { PairingRepair } from './session/messages'\nimport type { ActivationVia, ActiveSkill, DeactivationReason } from './skills/activation'\nimport type { SkillConfig, SkillsConfig } from './skills/types'\nimport type { ReadStateMap } from './tools/read-state'\nimport type { LazyToolEntry } from './tools/tool-search'\nimport type { ToolDef } from './tools/types'\nimport type { AgentBehavior, AgentClock, AgentRunOptions, AgentStats, ChildRunStats, McpServerConfig, McpToolHookContext, McpToolSchema, OAuthRefreshHookContext, SessionContentBlock, SessionEndStatus, SessionHookContext, SessionMessage, SessionTurn, SpawnHookContext, StreamHookContext, ToolHookContext, ToolResultContent, TurnUsage } from './types'\nimport { resolve as pathResolve } from 'node:path'\nimport { createHooks } from 'hookable'\nimport { buildAliasMaps } from './aliasing'\nimport { formatTaskSummary } from './chat/format'\nimport { createProcessContext } from './contexts'\nimport { installDedupToolsGate } from './dedup-tools'\nimport { AgentBudgetExceededError, errorMessage } from './errors'\nimport { runLoop } from './loop'\nimport { connectMcpServers } from './mcp'\nimport { buildPromptMessage, canonicalizePrompt } from './prompt'\nimport { detectTurnInterruption, filterUnresolvedToolUses, SYNTHETIC_TOOL_RESULT_PLACEHOLDER } from './session/messages'\nimport { createSkillActivationState } from './skills/activation'\nimport { installAllowedToolsGate } from './skills/allowed-tools'\nimport { buildCatalog } from './skills/catalog'\nimport { resolveSkills } from './skills/resolve'\nimport { installToolBudgetsGate } from './tool-budgets'\nimport { shell as builtinShell, createShellTool } from './tools/shell'\nimport { createSkillsReadTool } from './tools/skills-read'\nimport { createSkillsRunScriptTool } from './tools/skills-run-script'\nimport { createSkillsUseTool } from './tools/skills-use'\nimport { applyToolSearchToUnlocked, createToolSearchTool } from './tools/tool-search'\nimport { DEFAULT_AGENT_CLOCK } from './types'\nimport { escapeXml } from './xml'\n\n// ---------------------------------------------------------------------------\n// Hook definitions\n// ---------------------------------------------------------------------------\n\nexport interface AgentHooks {\n // System\n 'system:before': (ctx: { system: string }) => void\n\n // Agent lifecycle (start)\n /**\n * Fires once per `agent.run()` invocation, right after `session:start`\n * (when a session is bound) and before the first `turn:before`. Symmetric\n * with {@link AgentHooks['agent:done']}.\n *\n * `tracingContext` carries opaque parent trace metadata propagated from\n * the spawning agent (typically a W3C `{ traceparent, tracestate }`\n * carrier injected by the parent's tracer on `spawn:before`). Tracers\n * read it on the child run to continue the parent trace; absent on\n * top-level runs.\n */\n 'agent:start': (ctx: {\n runId: string\n parentRunId?: string\n depth: number\n agentName?: string\n /**\n * Provider identity for this run, lower-cased canonical form\n * (`anthropic`, `openai`, `openrouter`, `cerebras`, …). Exposed so\n * observability adapters can stamp `gen_ai.system` (Sentry / OTel\n * Gen AI semantic-convention identifier) on every child span of\n * this run without needing a back-channel to the agent. Empty\n * when the provider declined to identify itself.\n */\n providerName?: string\n startedAt: number\n tracingContext?: Readonly<Record<string, string>>\n }) => void\n\n // Turn lifecycle\n 'turn:before': (ctx: { turn: number, turnId: string, options: StreamOptions }) => void\n /**\n * Fires after each assistant turn (before its tool-result follow-up\n * dispatches; the loop iterates back to a fresh `turn:before` once the\n * tool results are produced).\n *\n * `toolCounts.turn` — calls **emitted** by the model in this assistant\n * turn, keyed by canonical tool name. Reflects what the model asked for,\n * regardless of downstream gate outcome. Most useful for spotting per-turn\n * spikes (\"the model called todowrite 4 times in one turn\").\n *\n * `toolCounts.run` — cumulative running counter of **dispatched** calls\n * scoped to this `runId`, captured at fire time. Excludes calls that were\n * `block`ed by `tool:gate` handlers. Includes calls short-circuited via\n * `tool:gate` `result` substitution (the model still asked, the framework\n * just answered without the tool running). Resumed sessions start a fresh\n * run with empty counts.\n *\n * Both fields are frozen snapshots; mutate-safe.\n */\n 'turn:after': (ctx: {\n turn: number\n turnId: string\n usage: TurnUsage\n message: SessionTurn\n toolCounts: {\n turn: Readonly<Record<string, number>>\n run: Readonly<Record<string, number>>\n }\n /**\n * Run-cumulative token + cost rollup **including this turn**. Computed\n * locally in the loop from `priorUsage + usage`. Lets observability\n * consumers stamp running totals onto turn spans / metrics without\n * re-accumulating across handlers.\n *\n * `turns` is the number of completed parent-loop turns inclusive of this\n * one (1-indexed for the first turn). `cost` is summed from per-turn\n * `TurnUsage.cost` reports; absent when nothing reported a non-zero cost.\n *\n * Children's spend is NOT folded in here — `turn:after` fires on parent\n * turns; child rollup lands on `spawn:complete` / final `agent:done`.\n */\n cumulativeUsage: Readonly<{\n input: number\n output: number\n cacheRead: number\n cacheCreation: number\n cost?: number\n turns: number\n }>\n }) => void\n\n /**\n * Fires after a tool-results user turn is pushed onto the conversation,\n * before* any byte-budget enforcement or follow-up steering. Symmetric\n * with `turn:after` but for the user-role tool_result turn that closes the\n * round-trip.\n *\n * Why it exists: persistence layers must write tool_use/tool_result turn\n * pairs together — if only the assistant turn (carrying tool_use blocks)\n * is durable, a process death between turns leaves the DB with an orphan\n * tool_use that Anthropic rejects on resume. Subscribe here to persist\n * the tool-results turn before the loop continues.\n */\n 'tool-results:after': (ctx: {\n turn: number\n turnId: string\n message: SessionTurn\n /** Frozen list of tool results that were packed into `message`. */\n results: readonly ToolResult[]\n }) => void\n\n // Streaming\n /**\n * Fires immediately before the loop calls `provider.stream()`. Pairs with\n * `stream:end` / `stream:error`; lets observers timestamp the moment the\n * request leaves the client to compute wait-vs-generate split and TTFT\n * relative to provider dispatch (not run start).\n *\n * `startedAt` is `Date.now()` at fire time. Observational — handlers\n * cannot mutate the in-flight `StreamOptions` (use `turn:before` /\n * `context:transform` / `system:transform` for that).\n */\n 'stream:start': (ctx: StreamHookContext & { startedAt: number }) => void\n 'stream:text': (ctx: StreamHookContext & { delta: string, text: string }) => void\n 'stream:end': (ctx: StreamHookContext & { text: string }) => void\n 'stream:thinking': (ctx: StreamHookContext & { delta: string, thinking: string }) => void\n /**\n * Fires when the provider's stream rejects BEFORE `stream:end` — provider\n * errors, network blips, malformed `tools` payloads (400 invalid_request_error).\n *\n * `err` is the original thrown value, not the typed `AgentProviderError`\n * the loop subsequently constructs; subscribe here to capture the native\n * SDK exception (status codes, request ids) for telemetry without having\n * to walk `cause` chains. Observational — the loop still wraps + throws\n * after this hook resolves, so handlers cannot suppress the failure.\n *\n * Best-effort convenience fields extracted from common SDK shapes\n * (Anthropic, OpenAI, OpenRouter, Cerebras) when present on `err`:\n * - `statusCode` — HTTP status (e.g. 400, 429, 500, 502, 529)\n * - `requestId` — provider request id for grep-the-logs correlation\n *\n * Both absent when the SDK doesn't expose them (network/early rejects,\n * stream-internal protocol errors). Walk `err.cause` chains yourself for\n * deeper diagnostics.\n *\n * Does NOT fire on user-initiated aborts; those land on `agent:abort`\n * instead. Use `err instanceof Error && err.name === 'AbortError'` to\n * distinguish if you also subscribe here for raw error logging.\n */\n 'stream:error': (ctx: StreamHookContext & {\n err: unknown\n statusCode?: number\n requestId?: string\n }) => void\n 'oauth:refresh': (ctx: OAuthRefreshHookContext) => void\n\n // Tool execution\n /**\n * Fires before validation, `tool:before`, and `execute`. Two ways to\n * intercept:\n *\n * - Set `block = true` (with a `reason`) to refuse the call. The model\n * sees a `Blocked: <reason>` tool result; `tool:before` / `tool:after`\n * do **not** fire.\n * - Set `result` to substitute a successful tool_result and skip\n * execution. The model sees the substitute as a normal tool_result;\n * `tool:before` does not fire, but `tool:after` and `tool:transform`\n * do — so byte budgets, telemetry, and post-mutation hooks see the\n * substitute. Useful for cache hits, dedup, idempotency guards,\n * plan-mode synthetic acks.\n *\n * If multiple handlers along the chain set both `block` and `result`,\n * `block` wins — refusal beats substitution, so a policy gate\n * (skills allow-list, custom security) can always override an upstream\n * consumer's cache substitute. Mirrors the writable-`result` shape on\n * `tool:unknown` and `tool:error` so consumers learn one pattern.\n *\n * `runToolCounts` — frozen pre-call snapshot of per-tool dispatched\n * counts in this run. Use it to self-throttle, drive observability, or\n * implement budget guards. Counts every call that passed gate, including\n * dedup substitutes (Z19); excludes `block`ed calls.\n *\n * Concurrent fleets see the same pre-batch snapshot on every member's\n * `tool:gate`; the built-in budget / dedup middleware reserves per-call\n * atomically so `behavior.toolBudgets` enforces correctly even when\n * multiple `tool:gate` callbacks fire in the same microtask.\n */\n 'tool:gate': (ctx: ToolHookContext & {\n block: boolean\n reason: string\n result?: string | ToolResultContent[]\n runToolCounts: Readonly<Record<string, number>>\n }) => void\n 'tool:before': (ctx: ToolHookContext & {\n coercions?: readonly string[]\n runToolCounts: Readonly<Record<string, number>>\n /**\n * Pre-write file snapshot — set by `spawn.ts`'s enricher when the\n * call is `write_file` and the target path is readable. Forwarded\n * through to the parent's `child:tool:before` bubble so hosts can\n * render a true `old → new` diff. Absent for every other tool, and\n * for `write_file` on a fresh-create.\n */\n priorContent?: string\n }) => void\n /**\n * Symmetric notification fired ONCE per call the model dispatched,\n * regardless of the path the call ultimately took through the loop.\n * Pair with `tool:after` (which also fires uniformly across paths) to\n * get a guaranteed `tool` ↔ `tool-result` event pairing for downstream\n * consumers reconstructing message history from live events.\n *\n * Distinct from `tool:before`: `tool:before` only fires when the tool\n * body is about to execute (the legacy \"I'm running this tool\" notification),\n * skipping gate-block / gate-substitute / unknown-tool / validation-reject\n * paths. `tool:dispatched` fires on all five paths with an `outcome`\n * discriminator so live consumers never see a `tool:after` whose\n * matching dispatch event was suppressed.\n *\n * Strict ordering: `tool:dispatched` fires AFTER `tool:gate` /\n * `validation:reject` / `tool:unknown` so the resolved `outcome` is\n * always accurate. Fires BEFORE `tool:before` on the execute path so\n * consumers binding both events see dispatched-then-before.\n *\n * `reason` is set only when `outcome === 'gate-block'` and carries the\n * gate's refusal text (so consumers can render denied-diff badges, etc.\n * without a side channel).\n */\n 'tool:dispatched': (ctx: ToolHookContext & {\n outcome: 'execute' | 'gate-substitute' | 'gate-block' | 'unknown' | 'invalid-input'\n reason?: string\n runToolCounts: Readonly<Record<string, number>>\n }) => void\n /**\n * Fires after a tool body produced a result (the execute path) or a\n * `tool:gate` listener substituted one via `ctx.result` (the Z20\n * cache-substitute path). Carries the post-`tool:transform` payload —\n * what the model actually sees on the wire.\n *\n * Does NOT fire on gate-block / unknown-tool / validation-reject\n * paths: the loop synthesizes their `tool_result` text inline and\n * sends it back to the model, but no tool body produced output and\n * no `tool:transform` ran. Consumers wanting symmetric per-call\n * notification across every dispatch path should listen to\n * `tool:dispatched` instead (which fires once per call regardless of\n * path, with an `outcome` discriminator).\n *\n * This narrow contract is deliberate: tracing spans opened in\n * `tool:before` only get closed where they were opened, byte budgets\n * only count real tool output, and chat-layer transcript renderers\n * keep their `tool` ↔ `tool-result` pairing without special-casing\n * the synthesized paths.\n */\n 'tool:after': (ctx: ToolHookContext & {\n result: string | ToolResultContent[]\n outputBytes: number\n coercions?: readonly string[]\n runToolCounts: Readonly<Record<string, number>>\n }) => void\n /**\n * Fires when a tool throws during execution. Mutate `result` to substitute a\n * tool-output payload that gets sent back to the model in place of the\n * default `Tool error: <msg>` string — useful for OSS-model error rewriting\n * (collapse stack traces, hide internal paths, prepend recovery hints).\n *\n * The post-hook value flows through `tool:transform` like a normal output, so\n * downstream byte-budgeting and image-stripping still apply.\n */\n 'tool:error': (ctx: ToolHookContext & { error: Error, result?: string | ToolResultContent[] }) => void\n 'tool:transform': (ctx: ToolHookContext & { result: string | ToolResultContent[], isError: boolean, outputBytes: number, coercions?: readonly string[] }) => void\n /**\n * Fires before the generic \"Unknown tool\" error when the model invokes a tool\n * that isn't registered (hallucinated names, dropped MCP servers, dangling\n * aliases). Mutate `result` to substitute a friendly response or set\n * `suppressError: true` to skip the companion `tool:error` emission.\n *\n * Fires for any unknown tool name — including hallucinated MCP-style names\n * (`mcp_supabase_xxx`); branch on `name.startsWith('mcp_')` to differentiate.\n */\n 'tool:unknown': (ctx: ToolHookContext & {\n result?: string | ToolResultContent[]\n suppressError: boolean\n }) => void\n /**\n * Fires when a single in-flight tool call is cancelled mid-flight via\n * `agent.cancelTool(callId)` — typically the TUI's \"cancel this tool\"\n * affordance. Distinguished from `tool:error` (the tool body threw),\n * `agent:abort` (whole run aborted), and the batch-layer\n * `INTERRUPT_MESSAGE_FOR_TOOL_USE` shape (sibling drain on full abort)\n * so consumers can split the four causes.\n *\n * Semantics:\n * - The tool's body has already received an abort signal (via the\n * combined per-call + run signal); whether it actually unwound is\n * up to the body's signal awareness.\n * - The framework returns the canonical\n * {@link TOOL_USE_CANCELLED_MESSAGE} to the model with\n * `isError: true` so the assistant turn closes cleanly and the\n * model can continue with the next batch call.\n * - `tool:after` and `tool:transform` do **not** fire for a cancelled\n * call — there's no real result the loop should account for.\n *\n * `runToolCounts` mirrors the `tool:gate` / `tool:before` snapshot for\n * this call (frozen, pre-increment) so a single per-call observer can\n * subscribe to either hook without re-walking the run.\n */\n 'tool:cancelled': (ctx: ToolHookContext & {\n reason: string\n runToolCounts: Readonly<Record<string, number>>\n }) => void\n /**\n * Fires when `validateToolArgs` rejects an input that could not be auto-coerced\n * to satisfy the tool's `inputSchema`. Observational — the tool call still\n * surfaces a `Validation error: …` string back to the model. Useful for\n * counting validation failures separately from runtime tool errors.\n */\n 'validation:reject': (ctx: ToolHookContext & {\n reason: string\n schema: Record<string, unknown>\n }) => void\n /**\n * Fires when `validateToolArgs` successfully auto-coerced one or more input\n * fields to satisfy the tool's `inputSchema`. **Only fires when at least one\n * coercion happened** — never on perfectly-shaped inputs. Useful for counting\n * model \"wrongness rate\" without re-running validation downstream.\n *\n * `coercions` lists the field names that were coerced. The values landed in\n * the input that the tool actually received; consumers wanting before/after\n * comparison can re-run `validateToolArgs(ctx.input, ctx.schema)`.\n */\n 'validation:coerce': (ctx: ToolHookContext & {\n coercions: readonly string[]\n schema: Record<string, unknown>\n }) => void\n\n // Context\n 'context:transform': (ctx: { messages: SessionMessage[] }) => void\n /**\n * Fires per request, after `context:transform` and before the request goes\n * out. Mutating `ctx.system` updates the system prompt the provider sends\n * for this turn — useful for runtime-derived sections (e.g. listing files\n * already read in the session, surfacing live tool budgets, injecting\n * skill activation reminders).\n *\n * Cache breakpoints are applied inside the provider after this hook, so\n * mutations land in the cache key naturally — repeated turns with the\n * same derived system text still hit the cache.\n *\n * `messages` is read-only here; use `context:transform` for message\n * surgery. `session` is `undefined` when the run is sessionless.\n */\n 'system:transform': (ctx: { system: string, messages: readonly SessionMessage[], turn: number, turnId: string, session?: Session }) => void\n 'steer:inject': (ctx: { message: string }) => void\n\n // Spawn\n 'spawn:before': (ctx: SpawnHookContext) => void\n 'spawn:complete': (ctx: ChildRunStats) => void\n 'spawn:error': (ctx: SpawnHookContext & { error: Error }) => void\n\n // Child hook bubbling — re-fires of a subagent's in-turn events on the\n // parent's hook bus. Each context is the original child event payload\n // augmented with `childId` (spawn id) and `depth` (subagent depth). Parents\n // opt in by listening; default behavior is silent. Grandchildren bubble\n // through their immediate parent's spawn tool, so a top-level listener\n // sees the full transitive stream.\n 'child:stream:text': (ctx: StreamHookContext & { delta: string, text: string, childId: string, depth: number }) => void\n 'child:stream:thinking': (ctx: StreamHookContext & { delta: string, thinking: string, childId: string, depth: number }) => void\n 'child:stream:end': (ctx: StreamHookContext & { text: string, childId: string, depth: number }) => void\n /** Bubbled `stream:error` from a subagent's turn. See {@link AgentHooks['stream:error']}. */\n 'child:stream:error': (ctx: StreamHookContext & { err: unknown, childId: string, depth: number }) => void\n /**\n * Gate-style child events. Unlike the other `child:*` events, the bubble\n * passes the **same `ctx` reference** the subagent's loop is awaiting on:\n * setting `ctx.block` / `ctx.reason` / `ctx.result` on a parent listener\n * propagates straight back to the child, refusing or substituting the call.\n *\n * Use these to gate subagent tool calls (native + MCP) from the parent\n * without registering listeners on every child agent. The parent's own\n * `tool:gate` / `mcp:tool:gate` listeners are NOT auto-shared with\n * children — that would also share their budgets and dedup state.\n */\n 'child:tool:gate': (ctx: ToolHookContext & {\n block: boolean\n reason: string\n result?: string | ToolResultContent[]\n runToolCounts: Readonly<Record<string, number>>\n childId: string\n depth: number\n }) => void\n 'child:mcp:tool:gate': (ctx: McpToolHookContext & {\n block: boolean\n reason: string\n result?: string | ToolResultContent[]\n childId: string\n depth: number\n }) => void\n 'child:tool:before': (ctx: ToolHookContext & {\n coercions?: readonly string[]\n runToolCounts: Readonly<Record<string, number>>\n childId: string\n depth: number\n /**\n * Pre-write file snapshot — only set by `spawn.ts`'s enricher when\n * `ctx.name === 'write_file'` and the child agent's execution\n * context succeeded in reading the target path. Absent for every\n * other tool, and absent for `write_file` on a fresh-create (file\n * didn't exist). The TUI's edit-diff renderer treats absence as an\n * empty old buffer to render an all-add view.\n */\n priorContent?: string\n }) => void\n /**\n * Bubbled sibling of {@link AgentHooks['tool:dispatched']} — fires when a\n * subagent dispatches a call. Symmetric with `child:tool:after` so live\n * UI rendering of subagent transcripts stays paired across all five\n * dispatch paths (gate-block/gate-substitute/unknown/invalid-input/execute).\n */\n 'child:tool:dispatched': (ctx: ToolHookContext & {\n outcome: 'execute' | 'gate-substitute' | 'gate-block' | 'unknown' | 'invalid-input'\n reason?: string\n runToolCounts: Readonly<Record<string, number>>\n childId: string\n depth: number\n }) => void\n 'child:tool:after': (ctx: ToolHookContext & {\n result: string | ToolResultContent[]\n outputBytes: number\n coercions?: readonly string[]\n runToolCounts: Readonly<Record<string, number>>\n childId: string\n depth: number\n }) => void\n /**\n * Same-ref bubble of a subagent's `tool:transform` hook. Parent\n * listeners may mutate `ctx.result` to rewrite the child tool's\n * output — used by the TUI to append per-edit annotation blocks\n * onto a child agent's `multi_edit` / `edit` / `write_file`\n * results when a parent-level approval gate produced a partial\n * decision.\n */\n 'child:tool:transform': (ctx: ToolHookContext & {\n result: string | ToolResultContent[]\n isError: boolean\n outputBytes: number\n coercions?: readonly string[]\n childId: string\n depth: number\n }) => void\n 'child:tool:error': (ctx: ToolHookContext & { error: Error, childId: string, depth: number }) => void\n /**\n * Bubbled sibling of {@link AgentHooks['tool:cancelled']} — fires when a\n * subagent's in-flight tool was cancelled (either directly on the parent\n * via `agent.cancelTool(callId)` reaching a child's call, or by the child\n * agent's own consumers). Symmetric with `child:tool:error` and\n * `child:tool:after` so the TUI's transcript renderer can paint the\n * cancelled badge against the right subagent row.\n */\n 'child:tool:cancelled': (ctx: ToolHookContext & {\n reason: string\n runToolCounts: Readonly<Record<string, number>>\n childId: string\n depth: number\n }) => void\n /** Bubbled from a subagent's `background:start`. Same payload + `childId` / `depth`. */\n 'child:background:start': (ctx: {\n taskId: string\n pid: number\n command: string\n cwd: string\n outputPath: string\n startedAt: number\n childId: string\n depth: number\n }) => void\n /** Bubbled from a subagent's `background:exit`. Same payload + `childId` / `depth`. */\n 'child:background:exit': (ctx: {\n taskId: string\n status: 'exited' | 'killed'\n exitCode: number\n signal?: NodeJS.Signals\n outputPath: string\n durationMs: number\n command: string\n childId: string\n depth: number\n }) => void\n /**\n * Bubbled from a subagent's `background:reassign`. Fires when a\n * grandchild's tasks get promoted to the child's handle on the\n * grandchild's destroy — gives the parent a chance to mirror the\n * ownership change in any UI it maintains.\n */\n 'child:background:reassign': (ctx: {\n taskId: string\n fromHandleId: string\n toHandleId: string\n childId: string\n pid: number\n command: string\n outputPath: string\n startedAt: number\n depth: number\n }) => void\n 'child:turn:after': (ctx: {\n turn: number\n turnId: string\n usage: TurnUsage\n message: SessionTurn\n toolCounts: { turn: Readonly<Record<string, number>>, run: Readonly<Record<string, number>> }\n childId: string\n depth: number\n }) => void\n\n // MCP server lifecycle\n /**\n * Fires once a server's client is live (transport connected, SDK handshake\n * complete). For servers bootstrapped eagerly, fires right after bootstrap;\n * for servers with `lazyConnect: true`, fires after the first `tools/call`\n * triggers the deferred connect — `lazy: true` lets the host distinguish.\n */\n 'mcp:connect': (ctx: { name: string, transport: string, tools: string[], lazy?: boolean }) => void\n 'mcp:error': (ctx: { name: string, error: Error }) => void\n 'mcp:close': (ctx: { name: string }) => void\n /**\n * Fires at the start of a per-server bootstrap attempt, before any network I/O.\n * Pairs with `mcp:bootstrap:end` and is always emitted, regardless of outcome.\n */\n 'mcp:bootstrap:start': (ctx: { name: string, transport: string }) => void\n /**\n * Fires at the end of a per-server bootstrap attempt. `durationMs` spans from\n * the matching `mcp:bootstrap:start`. On `ok: false` carries the originating\n * error so consumers can log / trace without relying on a separate `mcp:error`.\n *\n * `lazy: true` on the success arm signals the server registered its tools\n * without opening a connection (see `McpServerConfig.lazyConnect`); the\n * connect cost is deferred to the first `tools/call`. `cached: true` signals\n * the bootstrap skipped `tools/list` because the host provided\n * `cachedTools` — orthogonal to `lazy` (a server can be cached and eager,\n * cached and lazy, or neither).\n *\n * `warnings` (success arm only) collects soft signals that surfaced during\n * bootstrap without aborting it — currently just \"server rejected\n * `notifications/initialized` with 4xx\" from the tolerant client. Hosts\n * can render a \"connected with caveats\" badge from this; consumers that\n * don't care can ignore the field.\n */\n 'mcp:bootstrap:end': (ctx: { name: string, transport: string, durationMs: number } & ({ ok: true, toolCount: number, lazy?: boolean, cached?: boolean, warnings?: string[] } | { ok: false, error: Error })) => void\n /**\n * Fires once per successful bootstrap with the **raw, pre-filter** tool\n * schemas the server advertised via `tools/list` (or that the host\n * pre-seeded via `McpServerConfig.cachedTools`). Useful for hosts that\n * want to:\n *\n * - Persist a per-server tool catalog to disk so a settings UI can\n * show every available tool — including ones currently hidden by\n * `disabledTools` — without re-bootstrapping.\n * - Build per-tool toggle UIs that reflect the full server surface.\n * - Diff successive bootstraps to detect newly-added / removed tools.\n *\n * Read-only — handlers must NOT mutate `tools`. Shape the model-facing\n * list via `mcp:tools:filter` instead (which fires immediately after\n * this hook and IS the mutation surface).\n *\n * Does NOT fire when the bootstrap fails. Does NOT fire on lazy-connect\n * servers' deferred first-call (the schemas were already known from\n * `cachedTools` at the agent-construction boundary).\n */\n 'mcp:tools:list': (ctx: { name: string, transport: 'stdio' | 'sse' | 'streamable-http', tools: readonly McpToolSchema[] }) => void\n /**\n * Fires when a server's bootstrap was skipped because it requires OAuth\n * login and no tokens are stored. The host (TUI) surfaces this as a\n * \"needs-auth\" status so the user can opt in to interactive login via\n * `loginMcpServer`. Pairs with `mcp:bootstrap:end ok:false` — both fire,\n * since the bootstrap did fail (just for a recoverable reason).\n *\n * `reason`:\n * - `'no-tokens'` — config has `auth: 'oauth'` set (or normalized from\n * Cursor's `authMethod: 'mcpOAuth'`) and the credential store is empty.\n * - `'auto-promoted'` — server returned 401 + RFC 9728 metadata. No\n * static Authorization header was set, so the server is eligible for\n * OAuth login even though the config didn't explicitly request it.\n */\n 'mcp:auth:required': (ctx: {\n name: string\n transport: 'stdio' | 'sse' | 'streamable-http'\n reason: 'no-tokens' | 'auto-promoted'\n }) => void\n /**\n * Fires during an INTERACTIVE OAuth login flow — when the SDK wants the\n * user agent to navigate to the authorization URL. Hosts open a browser\n * and surface the URL in the TUI (in case the browser auto-open fails).\n * Distinct from `mcp:auth:required` (which fires from bootstrap when\n * tokens are missing); this only fires from `loginMcpServer`.\n */\n 'mcp:auth:url': (ctx: { name: string, url: string }) => void\n /** Interactive login completed and tokens were persisted. */\n 'mcp:auth:success': (ctx: { name: string }) => void\n /** Interactive login failed (browser error, abort, exchange failure). */\n 'mcp:auth:error': (ctx: { name: string, error: Error }) => void\n /**\n * Fires once per server after `listTools()` and after the config-side filters\n * (`enabledTools` / `disabledTools` / `toolFilter`) have applied, but BEFORE\n * tools are registered. Handlers may mutate `ctx.tools` in place — splicing,\n * reordering, or replacing entries — to further narrow what the model sees.\n *\n * Composes with config-side filters: config drops tools the host's static\n * policy excludes; this hook is the runtime escape hatch for per-user, per-\n * environment, or capability-driven decisions that the config can't express.\n *\n * Items are upstream tool descriptors (NOT yet namespaced as `mcp_<server>_<tool>`).\n */\n 'mcp:tools:filter': (ctx: {\n server: string\n transport: 'stdio' | 'sse' | 'streamable-http'\n tools: Array<{ name: string, description?: string | null, inputSchema?: unknown }>\n }) => void\n\n // MCP tool execution\n /**\n * MCP-side counterpart of `tool:gate`. Same shape: set `block` to refuse,\n * set `result` to substitute a successful payload and skip the upstream\n * MCP `callTool`. When both are set across the handler chain, `block` wins.\n *\n * Fires INSIDE the MCP wrapper's `execute`, after the loop's `tool:gate`\n * already ran. Does **not** carry `runToolCounts` — those are loop-level\n * and already exposed on `tool:gate` for MCP tools (which are registered\n * as agent tools under their namespaced name `mcp_<server>_<tool>`). Use\n * `tool:gate` for budget / dedup logic; reserve `mcp:tool:gate` for\n * MCP-specific concerns (per-server routing, transport-aware refusals).\n */\n 'mcp:tool:gate': (ctx: McpToolHookContext & {\n block: boolean\n reason: string\n result?: string | ToolResultContent[]\n }) => void\n 'mcp:tool:before': (ctx: McpToolHookContext) => void\n 'mcp:tool:after': (ctx: McpToolHookContext & { result: string | ToolResultContent[], outputBytes: number }) => void\n 'mcp:tool:transform': (ctx: McpToolHookContext & { result: string | ToolResultContent[], outputBytes: number }) => void\n 'mcp:tool:error': (ctx: McpToolHookContext & { error: Error }) => void\n\n // Background tasks\n /**\n * Fires when a background task is spawned via `ExecutionContext.execBackground`.\n * Observational — telemetry consumers can mint a \"task started\" span.\n *\n * `outputPath` is the file the context is streaming stdout+stderr to;\n * the model reads it via `read_file` on demand. `pid` is the OS pid of\n * the spawned shell wrapper (a process-group leader on POSIX, so a\n * `process.kill(-pid)` on cleanup tears down the whole subtree).\n */\n 'background:start': (ctx: {\n taskId: string\n pid: number\n command: string\n cwd: string\n outputPath: string\n startedAt: number\n }) => void\n\n /**\n * Fires exactly once per task when the child process terminates —\n * natural exit, host-issued kill, or `agent.destroy()` teardown. Same\n * payload the agent enqueues into `pendingTaskNotifications` for\n * injection on the next turn; useful for hosts that want to render\n * a \"task done\" toast immediately rather than wait for the model's\n * next prompt.\n */\n 'background:exit': (ctx: {\n taskId: string\n status: 'exited' | 'killed'\n exitCode: number\n signal?: NodeJS.Signals\n outputPath: string\n durationMs: number\n command: string\n }) => void\n\n /**\n * Ownership of a background task was transferred from one execution\n * handle to another — typically the spawn tool reassigning a\n * subagent's still-running tasks up to the parent's handle so they\n * outlive the subagent's `destroy()`.\n *\n * Hosts that listed the task under the source handle should update\n * their view to reflect the new owner. The TUI's `backgroundTasks`\n * registry drops the `childId` tag on the matching entry so the\n * `ctrl+k` picker shows it as parent-level (and `killBackgroundTask`\n * actually finds it).\n *\n * Fires on the new owner's hook bus (the parent's, in the spawn\n * case). The old owner is typically already torn down — wiring the\n * event the other way would deliver it to a destroyed hookable.\n */\n 'background:reassign': (ctx: {\n taskId: string\n /** `ExecutionHandle.id` of the previous owner. */\n fromHandleId: string\n /** `ExecutionHandle.id` of the new owner. */\n toHandleId: string\n /** Subagent label (`child-N`) the task originated under, when known. */\n childId?: string\n pid: number\n command: string\n outputPath: string\n startedAt: number\n }) => void\n\n // Skills\n 'skills:resolve': (ctx: { skills: SkillConfig[] }) => void\n 'skills:catalog': (ctx: { catalog: string, skills: SkillConfig[] }) => void\n 'skills:activate': (ctx: { skill: SkillConfig, via: ActivationVia }) => void\n 'skills:deactivate': (ctx: { skill: SkillConfig, reason: DeactivationReason }) => void\n\n // Pairing repair\n /**\n * Fires once per repair performed by the pre-send pairing pass\n * ({@link ensureToolResultPairing}). The pass runs immediately before every\n * provider call and patches over the wire-level corruption modes that\n * Anthropic / OpenAI 400 on (orphan `tool_use` / `tool_result`, duplicate\n * ids, compaction-stranded blocks).\n *\n * Observational — handlers can mirror to Sentry/PostHog/etc. for\n * postmortems on new corruption sources but cannot suppress the repair.\n * The companion `behavior.strictToolPairing` flag is the consumer escape\n * hatch for \"throw instead of repair\" (training-data collectors).\n *\n * `turnId` lets handlers correlate a repair burst with the run-loop turn\n * that observed it. Absent on repairs detected from the synthetic\n * fallback path in `agent.run()`'s error catch (the orphan-tool_use\n * fallback for crash recovery), where no turn is in flight.\n */\n 'pairing:repair': (ctx: PairingRepair & { turnId?: string }) => void\n\n // Observability seams\n /**\n * Privacy seam invoked by observability adapters (tracing, metrics,\n * logging, persistence sinks) before attaching potentially-sensitive\n * payloads as span attributes / log lines / external storage.\n *\n * Handlers mutate `ctx.redacted` — the field starts equal to `value` and\n * the final post-hook string is what consumers ship downstream. Default\n * (no handlers) is no redaction. Multiple handlers compose left-to-right;\n * the last writer wins.\n *\n * `kind` lets a single handler do shape-aware scrubbing (system prompts\n * vs. tool args vs. tool output have very different secret surfaces).\n * `meta` carries context-specific identifiers (`toolName`, `server`,\n * `turnId`, `callId`) the adapter knows at the call site — handlers use\n * it for policy decisions (\"skip redaction on internal `read_file`\n * results\"). Adapters that don't fire this hook simply attach raw values.\n *\n * Synchronous-only by design: observability stays on the hot path. Heavy\n * scrubbing (regex over MB of output) belongs in `tool:transform` instead.\n */\n 'tracing:redact': (ctx: {\n kind:\n | 'system'\n | 'prompt'\n | 'tool-input'\n | 'tool-result'\n | 'mcp-tool-input'\n | 'mcp-tool-result'\n | 'stream-text'\n | 'stream-thinking'\n value: string\n redacted: string\n meta?: Readonly<Record<string, unknown>>\n }) => void\n\n // Usage and output\n 'usage': (ctx: { turn: number, turnId: string, usage: TurnUsage, totalIn: number, totalOut: number }) => void\n 'output': (ctx: { output: Record<string, unknown>, schema: Record<string, unknown> }) => void\n /**\n * Fires when a turn's total tool-output bytes exceed `behavior.toolOutputBudget`.\n * Measured post-`tool:transform`. Loop injects a synthetic user message after\n * the tool-results turn instructing the model to summarize.\n */\n 'budget:exceeded': (ctx: { turn: number, turnId: string, bytes: number, budget: number }) => void\n /**\n * Fires when a per-tool budget configured via `behavior.toolBudgets` is\n * exceeded for a specific tool. `mode` reflects how the framework reacted:\n * `'steer'` lets the call run and queues a post-turn nudge; `'block'`\n * refuses the call outright with `Blocked: <message>`.\n *\n * `count` is the run-cumulative dispatched count just before this call.\n * Use `turnId` to correlate with `turn:after` if you need the integer turn\n * index. Distinct from `budget:exceeded` (byte-level) so consumers can\n * subscribe specifically; both can fire in the same turn.\n */\n 'tool-budget:exceeded': (ctx: {\n tool: string\n count: number\n max: number\n turnId: string\n mode: 'steer' | 'block'\n }) => void\n\n // Agent lifecycle\n 'agent:abort': (ctx: object) => void\n /**\n * Run finished — fires on all exit paths (completion, maxTurns, abort).\n *\n * Since 4.0 the `AgentStats` carried here is **cumulative** across the\n * parent agent loop and every recursively-spawned sub-agent\n * (`totalIn` / `totalOut` / `cost` / `totalCacheRead` / `totalCacheCreation`).\n * For parent-loop-only counts use `ctx.turnUsage` (parent-only array);\n * for tree-wide turn counts use `flattenTurns(ctx).length`.\n */\n 'agent:done': (ctx: AgentStats) => void\n\n // Session\n 'session:start': (ctx: SessionHookContext & { runId: string, prompt: string }) => void\n 'session:end': (ctx: SessionHookContext & { runId: string, status: SessionEndStatus, turnRange: [number, number] }) => void\n 'session:turns': (ctx: SessionHookContext & { turns: SessionTurn[], count: number }) => void\n 'session:meta': (ctx: SessionHookContext & { key: string, value: unknown }) => void\n 'session:save': (ctx: SessionHookContext) => void\n}\n\n/**\n * Authoritative list of hook event names. Kept in sync with `AgentHooks` at\n * compile time: the `satisfies` assertion below rejects any drift.\n */\n/**\n * Canonical XML wire format for a background-task completion notification.\n *\n * Rendered as a leading `text` content block in the next user-turn so\n * the model can pattern-match on the outer tag. The shape uses kebab-case\n * tags (matching zidane's other XML conventions). Every field is\n * structured — `<summary>` is a derived display string, NOT the source\n * of truth for the underlying data (replay reads `<command>` /\n * `<duration-ms>` / etc. directly, never the summary, so changing the\n * summary format can't break replay).\n *\n * Field-level escaping uses {@link escapeXml} so commands containing\n * `<` / `>` / `&` round-trip cleanly through persistence + replay.\n */\nfunction renderTaskNotificationXml(info: TaskExitInfo): string {\n // Display-only summary — short, single-line, formatted by\n // `formatTaskSummary` so the wire format matches the TUI banner +\n // shell_kill tool output exactly. Replay does NOT parse this string\n // back; the structured fields below carry the canonical data.\n const summary = formatTaskSummary(info)\n const lines = [\n '<task-notification>',\n ` <task-id>${escapeXml(info.taskId)}</task-id>`,\n ` <status>${info.status}</status>`,\n ` <exit-code>${info.exitCode}</exit-code>`,\n ...(info.signal ? [` <signal>${escapeXml(info.signal)}</signal>`] : []),\n ` <command>${escapeXml(info.command)}</command>`,\n ` <output-file>${escapeXml(info.outputPath)}</output-file>`,\n ` <duration-ms>${info.durationMs}</duration-ms>`,\n ` <summary>${escapeXml(summary)}</summary>`,\n '</task-notification>',\n ]\n return lines.join('\\n')\n}\n\nconst HOOK_EVENT_NAMES = [\n 'system:before',\n 'agent:start',\n 'turn:before',\n 'turn:after',\n 'tool-results:after',\n 'stream:start',\n 'stream:text',\n 'stream:end',\n 'stream:thinking',\n 'stream:error',\n 'oauth:refresh',\n 'tool:gate',\n 'tool:dispatched',\n 'tool:before',\n 'tool:after',\n 'tool:error',\n 'tool:cancelled',\n 'tool:transform',\n 'tool:unknown',\n 'validation:reject',\n 'validation:coerce',\n 'context:transform',\n 'system:transform',\n 'steer:inject',\n 'spawn:before',\n 'spawn:complete',\n 'spawn:error',\n 'child:stream:text',\n 'child:stream:thinking',\n 'child:stream:end',\n 'child:stream:error',\n 'child:tool:gate',\n 'child:mcp:tool:gate',\n 'child:tool:dispatched',\n 'child:tool:before',\n 'child:tool:after',\n 'child:tool:transform',\n 'child:tool:error',\n 'child:tool:cancelled',\n 'child:background:start',\n 'child:background:exit',\n 'child:background:reassign',\n 'child:turn:after',\n 'mcp:connect',\n 'mcp:error',\n 'mcp:close',\n 'mcp:bootstrap:start',\n 'mcp:bootstrap:end',\n 'mcp:tools:list',\n 'mcp:auth:required',\n 'mcp:auth:url',\n 'mcp:auth:success',\n 'mcp:auth:error',\n 'mcp:tools:filter',\n 'mcp:tool:gate',\n 'mcp:tool:before',\n 'mcp:tool:after',\n 'mcp:tool:transform',\n 'mcp:tool:error',\n 'background:start',\n 'background:exit',\n 'background:reassign',\n 'skills:resolve',\n 'skills:catalog',\n 'skills:activate',\n 'skills:deactivate',\n 'usage',\n 'output',\n 'budget:exceeded',\n 'tool-budget:exceeded',\n 'pairing:repair',\n 'tracing:redact',\n 'agent:abort',\n 'agent:done',\n 'session:start',\n 'session:end',\n 'session:turns',\n 'session:meta',\n 'session:save',\n] as const satisfies readonly (keyof AgentHooks)[]\n\nconst HOOK_EVENT_SET: ReadonlySet<string> = new Set(HOOK_EVENT_NAMES)\n\nfunction isKnownHookEvent(event: string): event is keyof AgentHooks {\n return HOOK_EVENT_SET.has(event)\n}\n\n/**\n * Strongly-typed hook registration map accepted on {@link AgentOptions.hooks}\n * (and therefore on any {@link Preset}). Each entry is a single handler or an\n * array of handlers — arrays are what {@link composePresets} produces when\n * multiple presets register handlers for the same event.\n *\n * Handlers registered here live for the **lifetime of the agent**. They fire\n * across every `run()`, mirroring a manual `agent.hooks.hook(event, fn)` call\n * made right after `createAgent` returns. For per-run handlers (auto-detached\n * at run end) use {@link AgentRunOptions.hooks} instead.\n */\nexport type AgentHookMap = Partial<{\n [K in keyof AgentHooks]: AgentHooks[K] | AgentHooks[K][]\n}>\n\n/**\n * If the trailing assistant turn in `turns` carries `tool_call` blocks with\n * no matching `tool_result` in a following user turn, mutate `turns` in\n * place to append a synthetic tool-results turn that closes every dangling\n * tool_use id. Used by the error path of `agent.run` to prevent a thrown\n * loop from leaving the persisted session with an orphan tool_use — which\n * Anthropic rejects on resume.\n *\n * Each synthetic result carries the shared\n * {@link SYNTHETIC_TOOL_RESULT_PLACEHOLDER} text (so consumers can pattern-\n * match the same way they would on a live pre-send repair) and\n * `isError: true` so the model treats it as a failed call.\n *\n * Fires `pairing:repair` (mode `orphan-tool-use-append`) for each synthetic\n * result, mirroring the wire-level repair pass's observability so postmortem\n * dashboards see the same telemetry shape regardless of where the orphan\n * was detected.\n *\n * No-op when:\n * - The trailing turn isn't an assistant turn (already closed, or session\n * ends with the seeded user prompt).\n * - The assistant turn has no `tool_call` blocks.\n * - All tool_use ids are already answered by a tool_result somewhere later\n * in the conversation (defensive — shouldn't happen but cheap to check).\n */\nasync function synthesizeMissingToolResults(\n turns: SessionTurn[],\n syntheticTurnId: string,\n runId: string,\n provider: Provider,\n hooks: Hookable<AgentHooks>,\n clock: AgentClock,\n): Promise<void> {\n if (turns.length === 0)\n return\n const last = turns[turns.length - 1]\n if (last.role !== 'assistant')\n return\n const pendingIds: string[] = []\n for (const block of last.content) {\n if (block.type === 'tool_call')\n pendingIds.push(block.id)\n }\n if (pendingIds.length === 0)\n return\n // Defensive: if a tool-results turn already exists later, drop the IDs\n // it covered. Belt-and-suspenders; the caller only invokes us when the\n // assistant turn is the last one.\n const answered = new Set<string>()\n for (const turn of turns.slice(0, -1)) {\n for (const block of turn.content) {\n if (block.type === 'tool_result')\n answered.add(block.callId)\n }\n }\n const dangling = pendingIds.filter(id => !answered.has(id))\n if (dangling.length === 0)\n return\n const results: ToolResult[] = dangling.map(id => ({\n id,\n content: SYNTHETIC_TOOL_RESULT_PLACEHOLDER,\n isError: true,\n }))\n const msg = provider.toolResultsMessage(results)\n turns.push({\n id: syntheticTurnId,\n runId,\n role: msg.role,\n content: msg.content,\n createdAt: await clock.now(),\n })\n for (const callId of dangling) {\n await hooks.callHook('pairing:repair', {\n mode: 'orphan-tool-use-append',\n callId,\n messageIndex: turns.length - 2,\n })\n }\n}\n\n// ---------------------------------------------------------------------------\n// Agent interface\n// ---------------------------------------------------------------------------\n\nexport interface AgentOptions {\n provider: Provider\n /** Display name for the agent (used in traces/logs). */\n name?: string\n /** Default system prompt injected when no system is provided at run time. */\n system?: string\n /** Tool definitions available to the agent. Defaults to no tools. */\n tools?: Record<string, ToolDef>\n /**\n * Map canonical tool names to LLM-facing (aliased) names.\n *\n * Aliasing is **LLM-boundary-only**: the alias is what the provider's tool spec\n * carries and what the model calls the tool; the canonical name is what lives in\n * `session.turns` and what the agent uses to look up the tool implementation.\n */\n toolAliases?: Record<string, string>\n /** Agent-level behavior defaults (overridden by run-level behavior) */\n behavior?: AgentBehavior\n /** Execution context: where tools run. Defaults to in-process. */\n execution?: ExecutionContext\n /** MCP servers to connect and expose as tools */\n mcpServers?: McpServerConfig[]\n /** Session for identity, turn persistence, and run tracking */\n session?: Session\n /**\n * Explicit read-state map for `read_file` dedup + `requireReadBeforeEdit`\n * tracking. When omitted, the read-state lives in a `WeakMap<Session, …>`\n * keyed by the agent's session; providing an explicit map lets a parent\n * agent share its tracking with a sessionless child (the canonical caller\n * is `spawn`'s `shareReadState: true` option). Tools resolve uniformly\n * via `resolveReadStateMap(ctx)` so the explicit map (when present)\n * wins over the session-keyed lookup.\n */\n readState?: ReadStateMap\n /** Skills configuration */\n skills?: SkillsConfig\n /**\n * Agent-lifetime hook registrations. Registered once when `createAgent`\n * returns; identical in effect to calling `agent.hooks.hook(event, fn)` for\n * each entry. Use this to bake observability / policy hooks into a\n * {@link Preset} so consumers get them automatically via `...spread`.\n *\n * Handlers may be a single function or an array — the array form is what\n * `composePresets()` produces when multiple presets register handlers for\n * the same event. Unknown event names throw at agent construction so typos\n * never silently no-op.\n *\n * For per-run handlers (auto-detached at run end) use\n * {@link AgentRunOptions.hooks}.\n */\n hooks?: AgentHookMap\n /**\n * Test seam — replaces the default MCP connector with a custom\n * implementation. Bypasses the `mcpServers` normalization layer entirely\n * and is **not** part of the supported public API. Subject to change or\n * removal in any release.\n *\n * @internal\n */\n mcpConnector?: (configs: McpServerConfig[]) => Promise<McpConnection>\n /**\n * Pre-connect MCP servers in the background as soon as `createAgent` returns,\n * instead of deferring the bootstrap to the first `agent.run()`.\n *\n * Useful when MCP latency is the dominant cost of a cold start: callers that\n * construct the agent early (e.g. at process init) can hide the bootstrap\n * behind other setup work. If bootstrap fails, the error is stored and\n * surfaced on the first `agent.run()` / `agent.warmup()`; the in-flight\n * promise is `await`ed by both paths so the error is never silently lost.\n *\n * No-op when `mcpServers` is empty. Default: `false`.\n */\n eager?: boolean\n /**\n * Time + UUID source for all journaled metadata (turn ids, `createdAt`\n * stamps, `runId`s, hook payloads consumers may persist). Defaults to\n * {@link DEFAULT_AGENT_CLOCK} (`Date.now()` / `crypto.randomUUID()`).\n *\n * Override at the run level via {@link AgentRunOptions.clock}. Live-only\n * measurements (TTFT, elapsed counters) keep `Date.now()` regardless.\n *\n * Primary use: durable-execution adapters (Restate, Temporal) inject a\n * journaled variant so replay regenerates byte-identical session\n * metadata across attempts.\n */\n clock?: AgentClock\n}\n\nexport interface Agent {\n hooks: Hookable<AgentHooks>\n run: (options: AgentRunOptions) => Promise<AgentStats>\n abort: () => void\n /**\n * Cancel a single in-flight tool call by id without aborting the rest of\n * the run. The matching call's `ctx.signal` flips and a `tool:cancelled`\n * hook fires; the call's wire result becomes the canonical\n * {@link TOOL_USE_CANCELLED_MESSAGE} with `isError: true`, while every\n * other concurrent call keeps running and the assistant turn closes\n * normally on the next batch boundary.\n *\n * Returns `true` when a live call with that id was found and the cancel\n * flag was flipped (idempotent — repeated calls return `false`).\n * `false` when no such call is currently dispatching — the lookup is\n * scoped to *currently in-flight* calls only, so a cancel issued after\n * the call has already produced a tool_result is a no-op.\n *\n * The optional `reason` is forwarded to the `tool:cancelled` hook ctx\n * and to the abort signal's `reason` field — useful for telemetry that\n * wants to distinguish \"user clicked cancel\" from a host-side policy.\n */\n cancelTool: (callId: string, reason?: string) => boolean\n /**\n * Host-side kill for a background task spawned via\n * `shell({ run_in_background: true })`. Routes through the execution\n * context's `killBackground` primitive, which SIGTERMs the whole\n * process group and awaits the output stream's flush.\n *\n * Returns `true` when a task was found and killed (or was already\n * exited — the call is idempotent on terminated tasks).\n * Returns `false` when:\n * - the task id doesn't exist in the context's registry, OR\n * - the execution context doesn't support `killBackground`\n * (some remote sandboxes), OR\n * - the agent isn't bound to an execution handle yet (no run\n * has ever fired).\n *\n * Distinct from the model-facing `shell_kill` tool — that one routes\n * through the model's turn so the kill is visible in the transcript.\n * `killBackgroundTask` is for the host's UI (\"cancel this task\" in\n * the TUI's `ctrl+k` picker); the kill is observational from the\n * model's perspective, surfacing as the usual `<task-notification>`\n * on the next turn.\n */\n killBackgroundTask: (taskId: string) => Promise<boolean>\n steer: (message: string) => void\n followUp: (message: string) => void\n waitForIdle: () => Promise<void>\n /**\n * Clear the agent's in-memory state (turns, queues, skill activations).\n * Fires `skills:deactivate` with `reason: 'reset'` for each previously active\n * skill. Awaiting lets host apps observe listener rejections.\n */\n reset: () => Promise<void>\n /**\n * Destroy the execution context and clean up resources.\n * Idempotent — safe to call from both a `finally` block and a signal handler.\n */\n destroy: () => Promise<void>\n /**\n * Explicitly activate a skill by name. Fires `skills:activate` with\n * `via: 'explicit'`. Throws if the skill isn't in the resolved catalog or\n * if the `maxActive` cap is reached. Idempotent — activating an already-active\n * skill is a no-op.\n */\n activateSkill: (name: string) => Promise<void>\n /**\n * Deactivate a skill by name. Fires `skills:deactivate` with `reason: 'explicit'`.\n * No-op when the skill wasn't active.\n */\n deactivateSkill: (name: string) => Promise<void>\n /**\n * Pre-connect MCP servers without running a turn. Idempotent and concurrency-safe:\n * - No MCP servers configured → resolves immediately.\n * - Connection already established → resolves immediately.\n * - Another `warmup()` / `run()` is bootstrapping → awaits the in-flight promise.\n *\n * Use from host code that wants to hide MCP bootstrap latency behind other\n * startup work (UI init, auth, etc.). Safe to call multiple times and from\n * multiple callers concurrently.\n */\n warmup: () => Promise<void>\n readonly isRunning: boolean\n readonly turns: SessionTurn[]\n readonly execution: ExecutionContext\n readonly handle: ExecutionHandle | null\n readonly session: Session | null\n /** Snapshot of currently active skills. */\n readonly activeSkills: readonly ActiveSkill[]\n /**\n * Frozen view of the underlying `provider.meta`. Read-only to prevent\n * accidental cross-agent contamination — writes are rejected at runtime\n * (via `Object.freeze`) and at compile time (via `Readonly`). To override\n * model / capability defaults, construct a new provider.\n */\n readonly meta: Readonly<Record<string, unknown>>\n}\n\n// ---------------------------------------------------------------------------\n// Behavior resolution\n// ---------------------------------------------------------------------------\n\nfunction resolveBehavior(\n agentBehavior?: AgentBehavior,\n runBehavior?: AgentBehavior,\n) {\n return {\n maxConcurrentTools: runBehavior?.maxConcurrentTools ?? agentBehavior?.maxConcurrentTools,\n maxTurns: runBehavior?.maxTurns ?? agentBehavior?.maxTurns,\n maxCostUsd: runBehavior?.maxCostUsd ?? agentBehavior?.maxCostUsd,\n maxTotalTokens: runBehavior?.maxTotalTokens ?? agentBehavior?.maxTotalTokens,\n maxTokens: runBehavior?.maxTokens ?? agentBehavior?.maxTokens,\n thinkingBudget: runBehavior?.thinkingBudget ?? agentBehavior?.thinkingBudget,\n schema: runBehavior?.schema ?? agentBehavior?.schema,\n cache: runBehavior?.cache ?? agentBehavior?.cache ?? true,\n toolOutputBudget: runBehavior?.toolOutputBudget ?? agentBehavior?.toolOutputBudget,\n toolOutputBudgetExcludeTools: runBehavior?.toolOutputBudgetExcludeTools ?? agentBehavior?.toolOutputBudgetExcludeTools,\n compactStrategy: runBehavior?.compactStrategy ?? agentBehavior?.compactStrategy ?? 'off' as const,\n compactThreshold: runBehavior?.compactThreshold ?? agentBehavior?.compactThreshold,\n compactKeepTurns: runBehavior?.compactKeepTurns ?? agentBehavior?.compactKeepTurns,\n thinkingDecay: runBehavior?.thinkingDecay ?? agentBehavior?.thinkingDecay,\n dedupReads: runBehavior?.dedupReads ?? agentBehavior?.dedupReads,\n dedupTools: runBehavior?.dedupTools ?? agentBehavior?.dedupTools,\n requireReadBeforeEdit: runBehavior?.requireReadBeforeEdit ?? agentBehavior?.requireReadBeforeEdit,\n toolBudgets: runBehavior?.toolBudgets ?? agentBehavior?.toolBudgets,\n readLineNumbers: runBehavior?.readLineNumbers ?? agentBehavior?.readLineNumbers,\n elideStaleReads: runBehavior?.elideStaleReads ?? agentBehavior?.elideStaleReads,\n toolDisclosure: runBehavior?.toolDisclosure ?? agentBehavior?.toolDisclosure ?? 'eager' as const,\n toolSearch: runBehavior?.toolSearch ?? agentBehavior?.toolSearch,\n persistThreshold: runBehavior?.persistThreshold ?? agentBehavior?.persistThreshold,\n persistExcludeTools: runBehavior?.persistExcludeTools ?? agentBehavior?.persistExcludeTools,\n persistDir: runBehavior?.persistDir ?? agentBehavior?.persistDir,\n persistMaxBytes: runBehavior?.persistMaxBytes ?? agentBehavior?.persistMaxBytes,\n tasksDir: runBehavior?.tasksDir ?? agentBehavior?.tasksDir,\n disableBackgroundTasks: runBehavior?.disableBackgroundTasks ?? agentBehavior?.disableBackgroundTasks,\n strictToolPairing: runBehavior?.strictToolPairing ?? agentBehavior?.strictToolPairing ?? false,\n }\n}\n\n// ---------------------------------------------------------------------------\n// Tool disclosure (progressive)\n// ---------------------------------------------------------------------------\n\n/**\n * Resolve which configured MCP server a namespaced tool name belongs to.\n *\n * Tools coming through {@link connectMcpServers} are keyed `mcp_<server>_<tool>`.\n * Server names may themselves contain underscores, so a naive `split('_')[1]`\n * would mis-attribute tools — instead, we test each configured server name as\n * a prefix candidate and pick the longest match. This is O(N×M) per lookup\n * but the loop runs once at run start; not on a hot path.\n */\nfunction resolveServerForTool(\n toolName: string,\n servers: readonly McpServerConfig[] | undefined,\n): McpServerConfig | undefined {\n if (!servers || servers.length === 0)\n return undefined\n let best: McpServerConfig | undefined\n let bestLen = -1\n for (const server of servers) {\n const prefix = `mcp_${server.name}_`\n if (toolName.startsWith(prefix) && server.name.length > bestLen) {\n best = server\n bestLen = server.name.length\n }\n }\n return best\n}\n\ninterface DisclosureResult {\n /** Canonical names of tools that get full schemas in every turn's wire-level tool list. */\n eagerCanonicalNames: Set<string>\n /** Canonical names of every lazy tool — kept as a `Set` for fast O(1) gate lookup. */\n lazyCanonicalNames: Set<string>\n /** Lazy entries the catalog + `tool_search` advertise to the model (wire names). */\n lazyEntries: LazyToolEntry[]\n}\n\n/**\n * Partition a tool registry into eager and lazy buckets.\n *\n * Native + skill tools always end up in the eager bucket — only MCP tools\n * are eligible for lazy disclosure. The `tool_search` tool itself, when\n * registered, must be eager (otherwise the model has no way to discover\n * anything).\n *\n * Lazy entries carry both the wire (`name`, alias-rewritten) and the\n * canonical (`canonicalName`) so:\n * - The catalog and `tool_search` results show wire names — the only names\n * the model ever sees on the provider side.\n * - The unlock set is keyed by canonical names — the loop's `ctx.tools` map\n * and the dispatch path are alias-stable.\n */\nfunction partitionToolDisclosure(\n toolsBySpecName: Record<string, ToolDef>,\n mcpToolNames: ReadonlySet<string>,\n servers: readonly McpServerConfig[] | undefined,\n globalMode: 'eager' | 'lazy',\n toolAliases: Record<string, string> | undefined,\n): DisclosureResult {\n const eagerCanonicalNames = new Set<string>()\n const lazyCanonicalNames = new Set<string>()\n const lazyEntries: LazyToolEntry[] = []\n\n function wireFor(canonical: string): string {\n const aliased = toolAliases?.[canonical]\n return typeof aliased === 'string' && aliased.length > 0 ? aliased : canonical\n }\n\n for (const [canonicalName, def] of Object.entries(toolsBySpecName)) {\n if (!mcpToolNames.has(canonicalName)) {\n eagerCanonicalNames.add(canonicalName)\n continue\n }\n const server = resolveServerForTool(canonicalName, servers)\n const mode = server?.disclosure ?? globalMode\n if (mode === 'lazy') {\n lazyCanonicalNames.add(canonicalName)\n lazyEntries.push({\n name: wireFor(canonicalName),\n canonicalName,\n description: def.spec.description || '',\n inputSchema: (def.spec.inputSchema ?? { type: 'object', properties: {} }) as Record<string, unknown>,\n ...(server ? { server: server.name } : {}),\n })\n }\n else {\n eagerCanonicalNames.add(canonicalName)\n }\n }\n\n return { eagerCanonicalNames, lazyCanonicalNames, lazyEntries }\n}\n\ninterface BuildCatalogOptions {\n /**\n * When `'tool_search'` (or another wire name), the catalog's preface tells\n * the model to call that tool. When `null`, no call-to-action is emitted —\n * useful when the host opted out of the auto-injected `tool_search` and\n * wants the catalog text to stay non-misleading.\n */\n discoveryToolName: string | null\n}\n\nfunction buildSearchableCatalog(\n entries: readonly LazyToolEntry[],\n options: BuildCatalogOptions,\n): string {\n // Group by server so the model sees a coherent \"what's available where\"\n // view. Tools without a server attribution (shouldn't happen for MCP, but\n // defensive) land in an \"ungrouped\" bucket at the end.\n const byServer = new Map<string, LazyToolEntry[]>()\n const ungrouped: LazyToolEntry[] = []\n for (const entry of entries) {\n if (!entry.server) {\n ungrouped.push(entry)\n continue\n }\n const list = byServer.get(entry.server) ?? []\n list.push(entry)\n byServer.set(entry.server, list)\n }\n\n // Stable alphabetical group order so the catalog text doesn't shift between\n // runs based on parallel-bootstrap completion ordering. The system-prompt\n // cache breakpoint depends on byte-stability across turns/runs; without\n // this, a re-bootstrap could thrash the cache for no semantic reason.\n const serverNames = [...byServer.keys()].sort()\n\n const parts: string[] = []\n if (options.discoveryToolName) {\n // The catalog block is computed once at run start and embedded into the\n // system prompt so the system-prompt cache breakpoint stays byte-stable\n // across turns. After `tool_search` unlocks a tool, it still appears in\n // this catalog (stale by design) but its full schema is also in the\n // request's `tools` array — there's no duplicate schema definition.\n // The \"if you have not already surfaced it\" caveat keeps the model\n // from re-calling `tool_search` for tools it has already loaded.\n parts.push(\n 'The following tools are available but their input schemas are NOT loaded in your context.',\n `Call the \\`${options.discoveryToolName}\\` tool to load schemas for any tool below that you have not already surfaced. Surfaced tools persist for the rest of the run.`,\n '',\n )\n }\n parts.push('<searchable_tools>')\n for (const server of serverNames) {\n parts.push(` <server name=\"${escapeXml(server)}\">`)\n for (const entry of byServer.get(server)!)\n parts.push(` <tool name=\"${escapeXml(entry.name)}\">${escapeXml(entry.description)}</tool>`)\n parts.push(' </server>')\n }\n for (const entry of ungrouped)\n parts.push(` <tool name=\"${escapeXml(entry.name)}\">${escapeXml(entry.description)}</tool>`)\n parts.push('</searchable_tools>')\n return parts.join('\\n')\n}\n\n/**\n * Install a `tool:gate` listener that refuses dispatch on lazy tools the\n * model hasn't surfaced via `tool_search` yet. Production providers\n * (Anthropic, OpenAI) already enforce this server-side because the model\n * can only emit `tool_use` for tools in the request's `tools` list — but\n * relying on that alone leaves custom / OSS / mock providers (and any\n * future lenient validator) able to bypass the gate by quoting a name from\n * the catalog. The middleware closes the gap so lazy disclosure is a real\n * boundary, not just an advertisement filter.\n *\n * Returns an uninstall function the run-end teardown calls so handlers\n * never leak across runs.\n */\nfunction installLazyDisclosureGate(\n hooks: Hookable<AgentHooks>,\n lazyCanonicalNames: ReadonlySet<string>,\n unlocked: ReadonlySet<string>,\n discoveryToolName: string | null,\n): () => void {\n if (lazyCanonicalNames.size === 0)\n return () => {}\n return hooks.hook('tool:gate', (ctx) => {\n if (ctx.block)\n return\n if (!lazyCanonicalNames.has(ctx.name))\n return\n if (unlocked.has(ctx.name))\n return\n ctx.block = true\n ctx.reason = discoveryToolName\n ? `Tool \"${ctx.name}\" is listed in <searchable_tools> but its schema has not been loaded. Call the \\`${discoveryToolName}\\` tool with names: [\"${ctx.name}\"] first, then re-issue the call.`\n : `Tool \"${ctx.name}\" is listed in <searchable_tools> but its schema has not been loaded.`\n })\n}\n\n// ---------------------------------------------------------------------------\n// createAgent\n// ---------------------------------------------------------------------------\n\n/**\n * Pick the next safe value for `runCounter` so `run_${++counter}` mints\n * an id that doesn't collide with any runId already referenced by the\n * session — whether the runId lives in `session.runs` or only in\n * `session.turns[].runId`. The regex matches the canonical `run_<int>`\n * shape minted by this module; any caller-supplied custom id schemes\n * are ignored (they don't conflict with `run_N`).\n *\n * Returning 0 for a sessionless / clean session preserves the original\n * \"first run is run_1\" semantics.\n */\nfunction initialRunCounter(session?: Session): number {\n if (!session)\n return 0\n let max = 0\n const consider = (id: string | undefined) => {\n if (!id)\n return\n const m = /^run_(\\d+)$/.exec(id)\n if (!m)\n return\n const n = Number.parseInt(m[1], 10)\n if (Number.isFinite(n) && n > max)\n max = n\n }\n for (const r of session.runs)\n consider(r.id)\n for (const t of session.turns)\n consider(t.runId)\n return max\n}\n\nexport function createAgent({ provider, name: agentName, system: agentSystem, tools: agentTools, toolAliases, behavior: agentBehavior, execution, mcpServers, session, readState: agentReadState, skills: agentSkills, mcpConnector, eager, hooks: initialHooks, clock: agentClock }: AgentOptions): Agent {\n const hooks = createHooks<AgentHooks>()\n const executionContext = execution ?? createProcessContext()\n const sourceTools = agentTools ?? {}\n\n // Agent-lifetime hooks declared on `AgentOptions.hooks` (typically supplied\n // by a preset). Registered here so they fire across every `run()` — same\n // semantics as a manual `agent.hooks.hook(...)` call right after createAgent.\n // Unknown event names throw immediately so typos in preset definitions\n // never silently no-op. Mirrors the per-run registration block in `run()`.\n if (initialHooks) {\n for (const [event, handler] of Object.entries(initialHooks)) {\n if (!isKnownHookEvent(event)) {\n throw new Error(\n `Unknown hook event \"${event}\" passed to createAgent(). See AgentHooks for valid events.`,\n )\n }\n const handlerList = Array.isArray(handler) ? handler : [handler]\n for (const fn of handlerList) {\n if (typeof fn !== 'function')\n continue\n hooks.hook(event, fn as AgentHooks[typeof event])\n }\n }\n }\n\n let abortController: AbortController | undefined\n let running = false\n let idleResolve: (() => void) | undefined\n let idlePromise: Promise<void> | undefined\n\n // Per-agent queue of background-task exits the model hasn't been told\n // about yet. Drained at the start of every `agent.run()` — each entry\n // becomes a `<task-notification>` text block prepended to the seeded\n // user turn. Suppression (via `tool:after` hook for `shell_kill` and\n // `read_file`) deletes entries before they're drained, so a model\n // that's already aware of an exit doesn't get a duplicate signal.\n //\n // Map keyed by `taskId` so re-enqueueing the same task overwrites\n // rather than duplicates. Cleared on `agent.destroy()` AND on `reset()`\n // so the next agent instance starts clean.\n const pendingTaskNotifications = new Map<string, TaskExitInfo>()\n // Per-callId abort registry mutated by the loop's `executeSingleTool` —\n // shared with `cancelTool()` below so an external caller (TUI keybind,\n // SDK consumer) can flip a single in-flight call's signal without\n // unwinding the whole run. The loop adds the entry before gate/execute\n // and removes it in a finally, so the map only ever holds currently\n // dispatching calls. Lives on the agent (not the run scope) so the\n // same instance survives across the loop's per-batch reuses; cleared\n // when destroyed.\n const pendingToolCancels = new Map<string, AbortController>()\n let executionHandle: ExecutionHandle | null = null\n let mcpConnection: McpConnection | null = null\n // Shared in-flight warmup promise. Guarantees that concurrent `warmup()` calls\n // and the lazy bootstrap inside `run()` observe the same outcome — we never\n // kick off the connector twice, and a failure in one caller propagates to\n // every awaiter instead of getting masked by a retry.\n let mcpWarmupPromise: Promise<void> | null = null\n const allMcpServers = mcpServers ?? []\n const steeringQueue: string[] = []\n const followUpQueue: string[] = []\n let conversationTurns: SessionTurn[] = session?.turns.slice() ?? []\n // `runCounter` mints sequential `run_${N}` ids via `++runCounter`. The\n // naive `session.runs.length` initializer is unsound when the loaded\n // session's `runs[]` is thinner than the runIds referenced by its\n // turns — which happens to forks (the fork starts with the parent's\n // turns but a fresh empty `runs[]`) and to any persistence path that\n // ever lost runs while keeping turns. In those cases starting from\n // `length` would re-mint `run_1`, colliding with a runId already\n // referenced by a turn in the same session. We instead pick the max\n // numeric id ever observed in EITHER `runs` or `turns[].runId` so the\n // next mint is guaranteed unique within this session.\n let runCounter = initialRunCounter(session)\n\n // Skills — resolved lazily and idempotently via `ensureSkillsResolved()`.\n // First call from `run()` / `warmup()` / `activateSkill()` wins; concurrent\n // callers converge on `skillsResolvePromise` so we never scan twice.\n const skillsConfig = agentSkills\n const skillsEnabledValue = skillsConfig?.enabled\n const skillsDisabled = skillsEnabledValue === false || (Array.isArray(skillsEnabledValue) && skillsEnabledValue.length === 0)\n let resolvedSkills: SkillConfig[] | null = null\n let skillsCatalog: string | null = null\n // Shared in-flight skills-resolution promise. Mirrors `mcpWarmupPromise`:\n // guarantees a single resolution pass even under concurrent callers, and\n // surfaces failures to every awaiter instead of masking them with a retry.\n let skillsResolvePromise: Promise<void> | null = null\n // Cleanup fn for the inline-skills temp directory (created by\n // `resolveSkills` when `skills.write` is set). Replaced with the real\n // teardown on first resolution; called from `destroy()` so the OS temp\n // doesn't fill up across many short-lived agents.\n let skillsCleanup: () => void = () => {}\n\n /**\n * Resolve skills once for the lifetime of the agent. Idempotent and\n * concurrency-safe; no-op when `skills` is disabled or omitted.\n *\n * Used by `run()` (lazy bootstrap), `warmup()` (eager bootstrap), and\n * `activateSkill()` (so the public API doesn't leak the timing of `run()`).\n * Fires `skills:resolve` (mutable `skills` array) and `skills:catalog`\n * (mutable `catalog`) in that order — exactly once per agent.\n */\n async function ensureSkillsResolved(): Promise<void> {\n if (skillsDisabled || !skillsConfig)\n return\n if (resolvedSkills)\n return\n if (skillsResolvePromise)\n return skillsResolvePromise\n\n skillsResolvePromise = (async () => {\n // Shell interpolation was previously applied here once per agent — it\n // now runs per `skills_use` invocation (Phase 3) so command output is\n // fresh on each activation rather than cached at resolution time.\n const bundle = await resolveSkills(skillsConfig)\n resolvedSkills = bundle.skills\n skillsCleanup = bundle.cleanup\n await hooks.callHook('skills:resolve', { skills: resolvedSkills })\n\n // The catalog prose branches on whether the `skills_use` tool will be\n // auto-injected into this agent's tool set. We predict the same gate\n // `run()` applies at tool-merge time: skills tool is registered iff\n // the config doesn't opt out AND the catalog is non-empty. A run-level\n // `tools` override can still suppress injection, but we don't yet know\n // `options.tools` — that's an acceptable mismatch (the model reads the\n // system prompt once; consumers doing aggressive per-run tool overrides\n // already accept that context reflects the agent default).\n const skillsToolRegistered = skillsConfig.tool !== false && resolvedSkills.length > 0\n const catalogCtx = {\n catalog: buildCatalog(resolvedSkills, { skillsToolRegistered }),\n skills: resolvedSkills,\n }\n await hooks.callHook('skills:catalog', catalogCtx)\n skillsCatalog = catalogCtx.catalog\n })()\n\n try {\n await skillsResolvePromise\n }\n catch (err) {\n // Drop the cached promise so a subsequent call gets a fresh attempt.\n // Resolution is local-FS I/O — failures (e.g. tmpdir creation) are\n // usually transient or signal a misconfigured `skills.write`.\n skillsResolvePromise = null\n throw err\n }\n }\n\n // Per-agent activation state (lives across runs; `run-end` deactivates every\n // skill at each run boundary, `reset` clears everything, `resume` rehydrates).\n const skillActivationState = createSkillActivationState({\n maxActive: skillsConfig?.maxActive,\n })\n\n async function run(options: AgentRunOptions): Promise<AgentStats> {\n if (running) {\n throw new Error('Agent is already running. Use steer() or followUp() to queue messages, or waitForIdle().')\n }\n\n // Validate: prompt is required unless resuming a session with turns.\n //\n // The resume guard inspects the trailing turn AFTER filtering unresolved\n // tool_uses (L1) — a session that ended on an orphan assistant turn\n // (process death mid-tool, kill -9) gets its trailing assistant dropped\n // before the \"trailing must be user\" check fires, so the caller doesn't\n // get a `cannot resume without prompt` error on a session the harness\n // can perfectly well recover from. The filtered view is cached on the\n // closure and reused below in the resume block to avoid walking the\n // turn list twice for every resumed run.\n const hasSessionTurns = session && session.turns.length > 0\n if (!options.prompt && !hasSessionTurns) {\n throw new Error('prompt is required when no session with existing turns is provided')\n }\n let resumeFilteredTurns: SessionTurn[] | undefined\n if (hasSessionTurns)\n resumeFilteredTurns = filterUnresolvedToolUses(session!.turns)\n if (!options.prompt && resumeFilteredTurns) {\n const lastTurn = resumeFilteredTurns.at(-1)\n if (lastTurn && lastTurn.role !== 'user') {\n const tail = detectTurnInterruption(resumeFilteredTurns)\n const detail = tail === 'completed'\n ? 'last turn is a completed assistant message'\n : 'last turn is mid-stream assistant content'\n throw new Error(`cannot resume without prompt: ${detail}. Pass a prompt to agent.run({ prompt: … }).`)\n }\n }\n\n // Resolve effective clock for this run. Run-level wins over agent-level;\n // both fall back to `DEFAULT_AGENT_CLOCK`. Captured in the run closure\n // so every journaled-metadata callsite uses the same source — under a\n // durable-execution adapter (Restate) replay regenerates byte-identical\n // session metadata.\n const clock: AgentClock = options.clock ?? agentClock ?? DEFAULT_AGENT_CLOCK\n\n // Capture the external-signal listener at outer scope so the `finally`\n // below can always remove it. Previously the listener was registered\n // with `{ once: true }`, which only fires on abort — leaving the\n // listener attached for the full lifetime of long-lived\n // `options.signal`s and pinning every `abortController` instance through\n // the closure. (C2.)\n let externalAbortListener: (() => void) | undefined\n const externalSignal = options.signal\n\n // Outer try/finally guarantees `running` always resets, idle promise\n // always resolves, and the external-signal listener is always removed —\n // even when setup below `running = true` throws (warmup failure, skills\n // resolution, alias collision, unknown hook event, seed appendTurns\n // I/O). Before this, a setup throw left `running = true`, every\n // subsequent `agent.run()` died on \"Agent is already running\", and\n // `agent.waitForIdle()` blocked forever. (C1.)\n running = true\n try {\n abortController = new AbortController()\n // Re-sync runCounter against the session BEFORE minting. Each\n // agent has its OWN counter closure — sufficient when one agent\n // owns the session, but the `spawn` tool spins up a CHILD agent\n // that ALSO mints into the SAME session. Without this re-sync\n // the child mints, say, `run_17`, then back on the parent the\n // next user prompt re-mints `run_17` because the parent's\n // closure never saw the child's increment. The resulting\n // duplicate ids confuse `eventsFromTurns` (runById collapses\n // multiple entries into one, ancestryOf returns the wrong\n // depth, the transcript renders subagent markers at the wrong\n // indent, etc — exactly the \"weird display\" rendering bug).\n // `initialRunCounter` scans BOTH session.runs and\n // session.turns[].runId, so any sibling agent's mint that has\n // either landed in runs[] OR persisted a turn is visible.\n runCounter = Math.max(runCounter, initialRunCounter(session))\n const runId = `run_${++runCounter}`\n\n // Track run in session\n // `startRun` / `session:start` both expect a string prompt — coerce PromptPart[]\n // down to its concatenated text for bookkeeping. Full multimodal content still\n // lives in the turn pushed later.\n const promptLabel = typeof options.prompt === 'string'\n ? options.prompt\n : Array.isArray(options.prompt)\n ? options.prompt\n .filter((p): p is { type: 'text', text: string } => p.type === 'text')\n .map(p => p.text)\n .join('\\n')\n : ''\n // Default depth to 0 for top-level runs so the `runs` list always has a\n // numeric depth — lets consumers group/filter by level without null-checking.\n session?.startRun(runId, promptLabel, {\n ...(options.parentRunId ? { parentRunId: options.parentRunId } : {}),\n depth: typeof options.depth === 'number' ? options.depth : 0,\n })\n if (session) {\n await session.updateStatus('running')\n await hooks.callHook('session:start', { sessionId: session.id, runId, prompt: promptLabel })\n }\n\n // Run-start observability event. Fires once per `agent.run()`,\n // symmetric with `agent:done`. Carries the tracingContext carrier\n // (W3C `traceparent`, vendor variants) propagated from the spawning\n // agent so child tracers stitch this root span as a continuation of\n // the parent's spawn span. Absent / empty on top-level runs.\n const runStartedAt = await clock.now()\n await hooks.callHook('agent:start', {\n runId,\n ...(options.parentRunId ? { parentRunId: options.parentRunId } : {}),\n depth: typeof options.depth === 'number' ? options.depth : 0,\n ...(agentName ? { agentName } : {}),\n ...(provider.name ? { providerName: provider.name } : {}),\n startedAt: runStartedAt,\n ...(options.tracingContext ? { tracingContext: Object.freeze({ ...options.tracingContext }) } : {}),\n })\n\n // If an external signal is provided, wire it to our internal controller.\n // Listener captured in `externalAbortListener` (outer scope) so the\n // outer-`finally` can detach it on every exit — completion as well as\n // abort. Otherwise `{ once: true }` leaks the listener across runs.\n if (externalSignal) {\n if (externalSignal.aborted) {\n abortController.abort()\n }\n else {\n externalAbortListener = () => abortController?.abort()\n externalSignal.addEventListener('abort', externalAbortListener, { once: true })\n }\n }\n\n idlePromise = new Promise<void>((resolve) => {\n idleResolve = resolve\n })\n\n // Collect child agent stats reported via spawn:complete hook\n const childrenStats: ChildRunStats[] = []\n const unregisterSpawnHook = hooks.hook('spawn:complete', (ctx) => {\n childrenStats.push(ctx)\n })\n\n // Per-run hook registrations. Registered before runLoop, unregistered in finally,\n // so handlers never leak across runs even on throw paths.\n //\n // A typed hook registration interface is exposed on `options.hooks` so TS\n // catches signature mismatches at the call-site; here we have to cross the\n // `Object.entries` (string-keyed) barrier, so we validate the event name\n // against the known hook surface at runtime. Unknown keys would otherwise\n // silently never fire.\n const perRunUnregisters: Array<() => void> = []\n if (options.hooks) {\n for (const [event, handler] of Object.entries(options.hooks)) {\n if (!isKnownHookEvent(event)) {\n throw new Error(\n `Unknown hook event \"${event}\" passed to run(). See AgentHooks for valid events.`,\n )\n }\n const handlerList = Array.isArray(handler) ? handler : [handler]\n for (const fn of handlerList) {\n if (typeof fn !== 'function')\n continue\n // Safe cast: event was validated via isKnownHookEvent. Handler arity\n // is host-validated via the AgentRunOptions['hooks'] surface type.\n perRunUnregisters.push(hooks.hook(event, fn as AgentHooks[typeof event]))\n }\n }\n }\n\n // Spawn execution context\n if (!executionHandle) {\n executionHandle = await executionContext.spawn()\n }\n\n // Connect MCP servers lazily on first run. Delegated to `warmup()` so the\n // fast-path (\"already connected\") and the slow-path (\"pending warmup from\n // `eager: true` or a concurrent `agent.warmup()` call\") share one promise\n // and one outcome.\n if (allMcpServers.length > 0 && !mcpConnection) {\n await warmup()\n }\n\n // Skills bootstrap. Idempotent — a no-op when `warmup()` already\n // resolved them (eager bootstrap or explicit pre-warm); pays the\n // cost here on the first run otherwise.\n await ensureSkillsResolved()\n\n // Session-resume rehydration: when resuming a session with prior turns,\n // scan for `skills_use` tool_call blocks and rebuild activation state\n // from the *most recent* mode per skill. A model-side\n // `skills_use({ mode: \"deactivate\", name })` is a load-bearing intent\n // — without honoring it, the next run would silently re-activate the\n // skill (because the catalog still lists it and an earlier `activate`\n // block is still in history) and the user's earlier \"unstuck\" gesture\n // would silently undo itself. The fix is order-aware: walk chronologically\n // and keep only the last mode per skill, then activate the skills\n // whose last mode is `'activate'`. Bad / missing `mode` is treated as\n // `'activate'` for backward compatibility with the pre-deactivate\n // schema.\n //\n // Fires `skills:activate` with `via: 'resume'` so consumers can\n // distinguish a fresh activation from a rehydrated one. Does NOT\n // fire `skills:deactivate` for skills with a trailing deactivate\n // block — there's nothing to deactivate (the state started empty),\n // and emitting a spurious `'resume'`-flavored deactivate would\n // confuse listeners expecting deactivate to follow activate.\n if (resolvedSkills && session && session.turns.length > 0 && skillActivationState.active().length === 0) {\n const skillsByName = new Map(resolvedSkills.map(s => [s.name, s]))\n const lastModeBySkill = new Map<string, 'activate' | 'deactivate'>()\n for (const turn of session.turns) {\n if (turn.role !== 'assistant')\n continue\n for (const block of turn.content) {\n if (block.type !== 'tool_call' || block.name !== 'skills_use')\n continue\n const input = block.input as { name?: string, mode?: string } | undefined\n const skillName = input?.name\n if (!skillName)\n continue\n const mode = input?.mode === 'deactivate' ? 'deactivate' : 'activate'\n lastModeBySkill.set(skillName, mode)\n }\n }\n for (const [skillName, mode] of lastModeBySkill) {\n if (mode !== 'activate')\n continue\n const skill = skillsByName.get(skillName)\n if (!skill)\n continue\n if (skillActivationState.activate(skill, 'resume') === 'ok') {\n await hooks.callHook('skills:activate', { skill, via: 'resume' })\n }\n }\n }\n\n const thinking = options.thinking ?? 'off'\n const model = options.model ?? provider.meta.defaultModel\n const resolvedBehavior = resolveBehavior(agentBehavior, options.behavior)\n const { maxConcurrentTools, maxTurns, maxCostUsd, maxTotalTokens, maxTokens, thinkingBudget, schema, cache, toolOutputBudget, toolOutputBudgetExcludeTools, compactStrategy, compactThreshold, compactKeepTurns, thinkingDecay, dedupTools, toolBudgets, elideStaleReads, toolDisclosure, toolSearch, persistThreshold, persistExcludeTools, persistDir, persistMaxBytes, strictToolPairing } = resolvedBehavior\n\n // System prompt: run-time option > agent default > fallback\n let system = options.system || agentSystem || 'You are a helpful assistant.'\n\n // Append skills catalog to system prompt\n if (skillsCatalog) {\n system = `${system}\\n\\n${skillsCatalog}`\n }\n\n // Tool resolution: run-level override > agent tools + MCP tools\n const runBaseTools = options.tools !== undefined\n ? options.tools\n : (mcpConnection\n ? { ...sourceTools, ...mcpConnection.tools }\n : sourceTools)\n\n // Track which tools came from MCP — only these are eligible for lazy\n // disclosure. A run-level `tools` override skips MCP entirely so the set\n // stays empty in that case.\n const mcpToolNames: ReadonlySet<string>\n = options.tools === undefined && mcpConnection\n ? new Set(Object.keys(mcpConnection.tools))\n : new Set<string>()\n\n // Auto-inject `skills_use` / `skills_read` / `skills_run_script` when the\n // resolved skills catalog is non-empty. Opt out via `SkillsConfig.tool: false`.\n //\n // Skill tools are merged at the same tier as agent/MCP — a run-level\n // `tools: {}` override still wins (consistent with \"no tools means no tools\").\n // Agent-defined tools with the same name win over auto-injected ones.\n const shouldInjectSkillTools\n = options.tools === undefined\n && !!resolvedSkills\n && resolvedSkills.length > 0\n && skillsConfig?.tool !== false\n\n const mergedWithSkills = shouldInjectSkillTools\n ? {\n // Auto-injected first so agent + MCP tools can override by name.\n skills_use: createSkillsUseTool({\n catalog: resolvedSkills!,\n state: skillActivationState,\n hooks,\n }),\n skills_read: createSkillsReadTool({\n catalog: resolvedSkills!,\n state: skillActivationState,\n }),\n skills_run_script: createSkillsRunScriptTool({\n catalog: resolvedSkills!,\n state: skillActivationState,\n scriptTimeoutMs: skillsConfig?.scriptTimeoutMs,\n }),\n ...runBaseTools,\n }\n : runBaseTools\n\n // Build a spec-name-indexed map for the loop (models call tools by spec name, not map key)\n const toolsPreSearch: Record<string, typeof mergedWithSkills[string]> = {}\n for (const tool of Object.values(mergedWithSkills)) {\n toolsPreSearch[tool.spec.name] = tool\n }\n\n // Per-run rewrite of the built-in `shell` tool. Two adjustments\n // ride on the same identity check:\n //\n // 1. Background-mode gate. Strips `run_in_background` from the\n // schema and the background-related paragraphs from the\n // description when:\n // - `behavior.tasksDir` is unset (no log dir wired), OR\n // - `behavior.disableBackgroundTasks: true` (explicit opt-out).\n // So the model never sees a flag it can't use.\n //\n // 2. Dedicated-tool nudge. When the agent also registers siblings\n // like `read_file`, `glob`, `grep`, `list_files`, `edit`, or\n // `write_file`, the description gains a \"prefer the dedicated\n // tool\" block for each one that's present — targeting the\n // `ls`/`cat`-to-re-verify loop some models fall into when both\n // a dedicated tool and `shell` are visible. Aliases are\n // threaded through so the printed name matches the wire-level\n // name the model sees in the tool spec.\n //\n // Only the framework's identity-equal built-in `shell` is rewritten —\n // host-customized shell-named tools are left alone (the host owns\n // the spec they registered). Hosts who want explicit control can\n // register `createShellTool({ ... })` directly.\n if (toolsPreSearch.shell === builtinShell) {\n const bgAllowed = typeof resolvedBehavior?.tasksDir === 'string'\n && resolvedBehavior.tasksDir.length > 0\n && resolvedBehavior.disableBackgroundTasks !== true\n toolsPreSearch.shell = createShellTool({\n allowBackground: bgAllowed,\n registeredCanonicals: new Set(Object.keys(toolsPreSearch)),\n ...(toolAliases ? { toolAliases } : {}),\n })\n }\n\n // Progressive tool disclosure — partition the registry into eager vs lazy\n // buckets and prepare the per-run `unlocked` set the loop reads when\n // rebuilding the wire-level tool list.\n //\n // Native (non-MCP) tools are always eager; `tool_search` itself must be\n // eager too (the discovery path can't gate on its own surfacing). The\n // `unlocked` set seeds with every eager canonical tool name and grows as\n // the model surfaces lazy tools through `tool_search`.\n //\n // We pass `toolAliases` so lazy entries can carry their wire-level\n // (alias-rewritten) display name — what the catalog and `tool_search`\n // results show to the model — while keeping the canonical name on the\n // entry for unlock-set membership and dispatch-map lookup.\n const disclosure = partitionToolDisclosure(toolsPreSearch, mcpToolNames, mcpServers, toolDisclosure, toolAliases)\n const unlocked = new Set<string>(disclosure.eagerCanonicalNames)\n const hostDefinedToolSearch = !!toolsPreSearch.tool_search\n const shouldInjectToolSearch\n = disclosure.lazyEntries.length > 0\n && toolSearch?.tool !== false\n && !hostDefinedToolSearch\n let tools = toolsPreSearch\n if (shouldInjectToolSearch) {\n const toolSearchTool = createToolSearchTool({\n catalog: disclosure.lazyEntries,\n unlocked,\n ...(toolSearch?.limit !== undefined ? { defaultLimit: toolSearch.limit } : {}),\n })\n tools = { ...toolsPreSearch, [toolSearchTool.spec.name]: toolSearchTool }\n unlocked.add(toolSearchTool.spec.name)\n }\n\n // Decide which discovery-tool name (if any) the catalog text should\n // point at. `tool_search` when we auto-injected, the host's tool when\n // they brought their own (we use the wire/alias name in both cases —\n // that's what the model is told to call), and `null` when they fully\n // opted out so the catalog stays accurate without misleading prose.\n const discoveryToolName: string | null = shouldInjectToolSearch\n ? 'tool_search'\n : (hostDefinedToolSearch\n ? (toolAliases?.tool_search ?? 'tool_search')\n : null)\n\n // Append the lazy-tool catalog AFTER the skills catalog so both use the\n // same anchor point — the catalog text is stable across turns and rides\n // the system-prompt cache breakpoint.\n if (disclosure.lazyEntries.length > 0) {\n system = `${system}\\n\\n${buildSearchableCatalog(disclosure.lazyEntries, { discoveryToolName })}`\n }\n\n // Build alias maps once per run. Throws on alias collisions.\n const aliasMaps = buildAliasMaps(toolAliases, Object.keys(tools))\n\n // (Lazy-disclosure gate is installed below, AFTER the skill/budget/dedup\n // gates, so its `tool:gate` handler runs last in registration order. See\n // the install call further down.)\n\n // Closure that recomputes the wire-level tool list from the current\n // `unlocked` snapshot. Called once up-front for the seed value and again\n // per-iteration in the loop when lazy disclosure may have grown the set.\n // Safe to call repeatedly — `provider.formatTools` is a pure mapping.\n //\n // Caching note: each successful `tool_search` appends one or more tools\n // to the formatted list, which moves provider-side cache breakpoints\n // (Anthropic marks the last tool, OpenAI-compat is similar). That costs\n // one tool-list cache miss per discovery wave; subsequent turns with the\n // same unlocked set hit cache normally. With many lazy tools and few\n // discovery waves this still beats eager (which always sends every\n // schema), but it is not a free optimisation.\n function buildFormattedTools(): unknown[] {\n const specs: ToolSpec[] = []\n for (const t of Object.values(tools)) {\n if (!unlocked.has(t.spec.name))\n continue\n specs.push({\n name: aliasMaps.aliasByCanonical.get(t.spec.name) ?? t.spec.name,\n description: t.spec.description || '',\n inputSchema: t.spec.inputSchema,\n })\n }\n return specs.length > 0 ? provider.formatTools(specs) : []\n }\n const formattedTools = buildFormattedTools()\n\n // Build initial turns — carry forward existing session history if resuming\n const turns: SessionTurn[] = []\n\n // Resume: prepend prior conversation turns from session.\n //\n // Triggers on re-runs (runs > 0) or promptless runs (session turns already\n // contain the user message). **Excluded for child agents**: when the spawn\n // tool runs with `persist: true`, the child agent shares the parent's\n // session for *storage* but its conversation must start fresh from the\n // task prompt. Otherwise the child would pull in the parent's in-flight\n // assistant turn — including the parent's pending `spawn` tool_use — and\n // every provider (Anthropic loudly, OpenAI implicitly) rejects a message\n // history where a `tool_use` lacks its matching `tool_result`.\n const isResume = session\n && session.turns.length > 0\n && (session.runs.length > 0 || !options.prompt)\n && !options.parentRunId\n if (isResume) {\n // Filter out turns from subagent runs (depth > 0). With `persist: true`\n // every child agent appends its own user/assistant turns to the shared\n // `session.turns` array, sandwiched between the parent's `tool_call`\n // (for `spawn`) and the parent's `tool_result`. Replaying those child\n // turns into the resumed parent's conversation puts an unrelated\n // user/assistant pair right after the `tool_call`, breaking the\n // Anthropic `tool_use → tool_result` adjacency rule and crashing the\n // very first API call after reload.\n //\n // We keep turns with no `runId` (legacy data) and turns whose `runId`\n // belongs to a top-level run; everything that came from a subagent run\n // stays in the persisted session for transcript reconstruction (see\n // `eventsFromTurns`) but is invisible to the resumed conversation.\n const childRunIds = new Set(\n session!.runs.filter(r => (r.depth ?? 0) > 0).map(r => r.id),\n )\n const resumed = childRunIds.size === 0\n ? session!.turns\n : session!.turns.filter(t => !t.runId || !childRunIds.has(t.runId))\n // L1 — defense at resume time: drop assistant turns whose every\n // `tool_call` is unresolved (no matching `tool_result` later in\n // the transcript). Without this, a session killed mid-tool would\n // resume with an orphan tool_use that Anthropic rejects on the\n // very first API call. The runtime conversation uses the\n // filtered view; the persisted session keeps the original turns\n // so transcript replay still shows what the model tried to do.\n //\n // When the resume guard above already filtered the unmodified\n // `session.turns`, reuse that cached result; otherwise (subagent\n // filter removed something) filter the post-subagent view.\n const filteredForRuntime = (\n resumeFilteredTurns && resumed === session!.turns\n )\n ? resumeFilteredTurns\n : filterUnresolvedToolUses(resumed)\n turns.push(...filteredForRuntime)\n\n // Lazy disclosure — replay every resolved `tool_search` from the\n // resumed history so the per-run `unlocked` set reflects what the\n // model has already been told is callable. Without this seeding a\n // resumed conversation shows the model \"These tools are now\n // callable\" in the prior tool_result, but the next turn's\n // formattedTools and the `tool:gate` hook still treat the tools\n // as locked — every subsequent `tool_use` is rejected with\n // \"load via tool_search first\", which the model can't recover\n // from without a fresh `tool_search` round-trip.\n //\n // Gated on `shouldInjectToolSearch`: when the host wires their\n // own discovery tool they own the unlock state machine, and\n // re-running our matcher against `tool_search` calls they didn't\n // emit would be incorrect.\n if (shouldInjectToolSearch && disclosure.lazyEntries.length > 0) {\n // Collect callIds of successful tool_results first; only replay\n // `tool_search` calls whose result the model actually consumed.\n // A tool_call without a matching tool_result is either an\n // unresolved pair `filterUnresolvedToolUses` already stripped or\n // a structural error — either way, unlocking from it would be\n // speculative.\n const resolvedCallIds = new Set<string>()\n for (const turn of filteredForRuntime) {\n for (const block of turn.content) {\n if (block.type === 'tool_result' && !block.isError)\n resolvedCallIds.add(block.callId)\n }\n }\n for (const turn of filteredForRuntime) {\n for (const block of turn.content) {\n if (block.type !== 'tool_call')\n continue\n if (block.name !== 'tool_search')\n continue\n if (!resolvedCallIds.has(block.id))\n continue\n applyToolSearchToUnlocked(\n disclosure.lazyEntries,\n block.input,\n unlocked,\n toolSearch?.limit,\n )\n }\n }\n }\n }\n\n // Track where this run's turns start (after any resumed turns)\n const runTurnStart = turns.length\n\n if (options.system) {\n await hooks.callHook('system:before', { system: options.system })\n }\n\n // Drain pending background-task notifications. Each entry becomes\n // a `<task-notification>` text block prepended to the user turn so\n // the model sees task completions on its very next prompt, without\n // polling. The notifications survive the run-end deactivate-all\n // (they're agent-scoped, not run-scoped) and were either populated\n // by `background:exit` since the last `run()` OR suppressed by a\n // `shell_kill` / `read_file` against the matching task. See\n // `docs/RUN_IN_BACKGROUND.md` §Completion notification.\n const drainedNotifications: SessionContentBlock[] = []\n if (pendingTaskNotifications.size > 0) {\n for (const notif of pendingTaskNotifications.values()) {\n drainedNotifications.push({\n type: 'text',\n text: renderTaskNotificationXml(notif),\n })\n }\n pendingTaskNotifications.clear()\n }\n\n // Snapshot the high-water mark BEFORE pushing the seeded user\n // turn for this run. Every entry currently in `turns` came from\n // the resume path (they were in `session.turns` when we loaded),\n // so they're already durable. The seeded user turn (next) and\n // subsequent assistant / tool-results turns are NEW, and the\n // persistence loop below uses `turns.slice(lastPersistedTurnCount)`\n // to figure out what to write.\n //\n // Subtle: we must NOT use `session.turns.length` here. When the\n // session carries subagent turns from a prior run, the resume\n // path FILTERS them out of `turns` (subagent runs are top-level-\n // invisible per the `childRunIds` filter above), so `turns.length`\n // is smaller than `session.turns.length`. A previous version used\n // session.turns.length as the high-water mark — `turns.slice(N)`\n // returned empty for every subsequent persist call, and the\n // entire follow-up run silently failed to persist (the seeded\n // user turn never landed in `session.turns`, the loop produced\n // assistant turns but they never persisted either, the user's\n // transcript saw a frozen session with no apparent error). The\n // local-count high-water mark sidesteps this entirely.\n let lastPersistedTurnCount = turns.length\n\n const promptParts = canonicalizePrompt(options.prompt)\n if (promptParts || drainedNotifications.length > 0) {\n const promptMsg = promptParts ? buildPromptMessage(provider, promptParts) : null\n // Prepend the notifications BEFORE the prompt content so the\n // model reads them as ambient context before the user's actual\n // request. Multiple notifications stack in completion order\n // (Map preserves insertion order; insertion order = onExit\n // arrival order from `background:exit`).\n const content: SessionContentBlock[] = [\n ...drainedNotifications,\n ...(promptMsg ? promptMsg.content : []),\n ]\n turns.push({\n id: clock.randomUUID(),\n runId,\n role: promptMsg ? promptMsg.role : 'user',\n content,\n createdAt: await clock.now(),\n })\n }\n\n conversationTurns = turns\n\n // Persist + emit the seeded user turn before any assistant `turn:before` fires.\n // Without this, the seeded turn was persisted only after the first `turn:after`,\n // so DB rows ordered by `created_at` placed the user turn AFTER its assistant\n // response. Consumers already dedupe `session:turns` payloads, so emitting the\n // seeded turn separately is safe.\n if (session && turns.length > lastPersistedTurnCount) {\n const seededTurns = turns.slice(lastPersistedTurnCount)\n await session.appendTurns(seededTurns)\n lastPersistedTurnCount = turns.length\n await hooks.callHook('session:turns', { sessionId: session.id, turns: seededTurns, count: turns.length })\n }\n\n // Incremental turn persistence — fires after both the assistant turn\n // (`turn:after`) and the tool-results user turn (`tool-results:after`),\n // so a tool_use block is always durable alongside its matching\n // tool_result. Previously only `turn:after` was subscribed, leaving a\n // crash window where a persisted assistant turn referenced tool_use\n // IDs that had no on-disk counterpart — and Anthropic rejects orphan\n // tool_use blocks on resume.\n const persistPendingTurns = async () => {\n if (!session)\n return\n const newTurns = turns.slice(lastPersistedTurnCount)\n if (newTurns.length === 0)\n return\n await session.appendTurns(newTurns)\n lastPersistedTurnCount = turns.length\n await hooks.callHook('session:turns', { sessionId: session.id, turns: newTurns, count: turns.length })\n }\n const unregisterTurnSync = session ? hooks.hook('turn:after', persistPendingTurns) : undefined\n const unregisterToolResultsSync = session ? hooks.hook('tool-results:after', persistPendingTurns) : undefined\n\n // Persist any turns not yet synced to the session store.\n //\n // Orphan-tool_use guard (`failureFallback: true`): when called from the\n // run-loop's catch path the trailing assistant turn may carry tool_use\n // blocks whose matching tool_result was never produced (the throw\n // happened during dispatch or before the tool-results turn was pushed).\n // Persisting it as-is leaves the DB with an unanswered tool_use that\n // Anthropic rejects on resume. We synthesize a `Aborted: run failed`\n // tool_result for every dangling tool_use ID and push that as the next\n // user turn before persisting — adjacency restored, resume works.\n async function flushTurns(opts: { failureFallback?: boolean } = {}) {\n if (!session)\n return\n if (opts.failureFallback) {\n const turnId = (await session.generateTurnId?.()) ?? clock.randomUUID()\n await synthesizeMissingToolResults(turns, turnId, runId, provider, hooks, clock)\n }\n const remaining = turns.slice(lastPersistedTurnCount)\n if (remaining.length > 0) {\n await session.appendTurns(remaining)\n lastPersistedTurnCount = turns.length\n await hooks.callHook('session:turns', { sessionId: session.id, turns: remaining, count: turns.length })\n }\n }\n\n // Deactivate every active skill with `reason: 'run-end'`. Called at each\n // natural run boundary regardless of status (completed / aborted / error).\n async function deactivateAllSkills() {\n for (const record of skillActivationState.clear())\n await hooks.callHook('skills:deactivate', { skill: record.skill, reason: 'run-end' })\n }\n\n // Finalize session: persist run status and fire session:end\n async function finalizeSession(status: SessionEndStatus) {\n if (!session)\n return\n const run = session.runs.find(r => r.id === runId)\n if (run)\n await session.updateRun(run)\n await session.updateStatus(status === 'aborted' ? 'idle' : status)\n await hooks.callHook('session:end', { sessionId: session.id, runId, status, turnRange: [runTurnStart, turns.length - 1] })\n }\n\n // Install the skill allowed-tools gate for this run; remove it in `finally`\n // so tool:gate listeners never leak across runs.\n const uninstallAllowedToolsGate = installAllowedToolsGate(hooks, skillActivationState)\n\n // Per-tool soft call budgets. Installed BEFORE dedup so a budget block\n // wins over a dedup hit — otherwise a model spamming a dedup-eligible\n // tool would bypass the cap entirely (every dedup hit short-circuits\n // budgets that fire later in the chain). Order: allowed-tools → budgets\n // → dedup → consumer hooks. The middleware owns its own per-tool\n // approval counter so within-batch ordering is correct in parallel mode.\n const uninstallToolBudgets = installToolBudgetsGate(\n hooks,\n () => toolBudgets,\n msg => steeringQueue.push(msg),\n )\n\n // Per-tool argument-dedup middleware. No-op when `behavior.dedupTools` is\n // empty/unset or when the run is sessionless. Behavior is the field-merged\n // resolution of agent + run defaults — same precedence as every other\n // behavior knob.\n const uninstallDedupTools = installDedupToolsGate(\n hooks,\n () => dedupTools,\n () => session ?? undefined,\n )\n\n // Hard gate for lazy tools — refuses dispatch on canonical names not yet\n // in `unlocked`. Production providers already enforce this via their\n // tool-list validator, but the middleware closes the gap for custom /\n // mock / lenient providers and for any path where a model quotes a name\n // straight from the catalog.\n //\n // Registered LAST among the four gates so its handler fires after\n // allowed-tools, budgets, and dedup. Lazy gate early-returns when an\n // upstream gate already set `ctx.block` (see `installLazyDisclosureGate`\n // body), so a skill refusal or budget block always wins over a \"load\n // via tool_search\" message. The early-return turns the order into a\n // safety net rather than a hard invariant — but the order-and-the-\n // early-return together make the precedence regression-proof.\n const uninstallLazyDisclosureGate = installLazyDisclosureGate(\n hooks,\n disclosure.lazyCanonicalNames,\n unlocked,\n discoveryToolName,\n )\n\n const runStartMs = await clock.now()\n\n const runDepth = typeof options.depth === 'number' ? options.depth : 0\n\n try {\n const stats = await runLoop({\n provider,\n hooks,\n agentName,\n agentSystem,\n agentTools: sourceTools,\n agentToolAliases: toolAliases,\n agentMcpServers: mcpServers,\n agentSkills,\n // Forward the resolved view (agent + run merged) so per-run overrides\n // of `dedupReads` / `requireReadBeforeEdit` / etc. are visible to\n // tools via `ToolContext.behavior`.\n agentBehavior: resolvedBehavior,\n tools,\n formattedTools,\n rebuildFormattedTools: disclosure.lazyEntries.length > 0 ? buildFormattedTools : undefined,\n aliasMaps,\n model,\n system,\n thinking,\n ...(maxConcurrentTools !== undefined ? { maxConcurrentTools } : {}),\n signal: abortController.signal,\n execution: executionContext,\n handle: executionHandle!,\n steeringQueue,\n followUpQueue,\n turns,\n runId,\n generateTurnId: () => session?.generateTurnId() ?? clock.randomUUID(),\n clock,\n maxTurns,\n ...(maxCostUsd !== undefined ? { maxCostUsd } : {}),\n ...(maxTotalTokens !== undefined ? { maxTotalTokens } : {}),\n maxTokens,\n ...(session ? { session } : {}),\n ...(agentReadState ? { readState: agentReadState } : {}),\n ...(options.parentRunId ? { parentRunId: options.parentRunId } : {}),\n depth: runDepth,\n thinkingBudget,\n schema,\n cache,\n toolOutputBudget,\n ...(toolOutputBudgetExcludeTools !== undefined ? { toolOutputBudgetExcludeTools } : {}),\n compactStrategy,\n compactThreshold,\n compactKeepTurns,\n ...(elideStaleReads !== undefined ? { elideStaleReads } : {}),\n ...(thinkingDecay !== undefined ? { thinkingDecay } : {}),\n ...(persistThreshold !== undefined ? { persistThreshold } : {}),\n ...(persistExcludeTools !== undefined ? { persistExcludeTools } : {}),\n ...(persistDir !== undefined ? { persistDir } : {}),\n ...(persistMaxBytes !== undefined ? { persistMaxBytes } : {}),\n ...(strictToolPairing ? { strictToolPairing: true } : {}),\n providerName: provider.name,\n runStartMs,\n runToolCounts: {},\n pendingToolCancels,\n })\n\n // `stats` is the parent-loop view returned by `runLoop` —\n // `totalIn/Out/turnUsage` cover this run's loop only. Children are\n // collected separately via `spawn:complete` and folded into the\n // returned `AgentStats` below; the loop view is preserved verbatim\n // for `session.completeRun(...)` so per-run session ledgers stay\n // additive (children are persisted as their own runs in the same\n // session — folding cumulative numbers in here would double-count).\n const parentTurnCost = stats.turnUsage\n ?.reduce((sum, t) => sum + (t.cost ?? 0), 0) ?? 0\n\n // Roll children's already-cumulative totals into the parent. Each\n // child's `stats.totalIn/Out/cost/totalCacheRead/totalCacheCreation`\n // was finalized through this same path before bubbling up via\n // `spawn:complete`, so a single-level sum lands grandchildren\n // transitively.\n let childrenIn = 0\n let childrenOut = 0\n let childrenCost = 0\n let childrenCacheRead = 0\n let childrenCacheCreation = 0\n for (const c of childrenStats) {\n childrenIn += c.stats.totalIn\n childrenOut += c.stats.totalOut\n childrenCost += c.stats.cost ?? 0\n childrenCacheRead += c.stats.totalCacheRead\n childrenCacheCreation += c.stats.totalCacheCreation\n }\n\n const cumulativeCost = parentTurnCost + childrenCost\n\n const finalStats: AgentStats = {\n ...stats,\n totalIn: stats.totalIn + childrenIn,\n totalOut: stats.totalOut + childrenOut,\n totalCacheRead: stats.totalCacheRead + childrenCacheRead,\n totalCacheCreation: stats.totalCacheCreation + childrenCacheCreation,\n ...(cumulativeCost > 0 ? { cost: cumulativeCost } : {}),\n children: childrenStats.length > 0 ? childrenStats : undefined,\n }\n\n await flushTurns()\n\n // If aborted during loop (loop broke cleanly without throwing).\n // Pass `stats` through so the persisted run record reflects real\n // token consumption instead of the historic `0 in / 0 out`.\n if (abortController.signal.aborted) {\n session?.abortRun(runId, {\n turns: stats.turns,\n tokensIn: stats.totalIn,\n tokensOut: stats.totalOut,\n turnUsage: stats.turnUsage,\n cost: parentTurnCost > 0 ? parentTurnCost : undefined,\n })\n await finalizeSession('aborted')\n await hooks.callHook('agent:done', finalStats)\n return finalStats\n }\n\n session?.completeRun(runId, {\n turns: stats.turns,\n tokensIn: stats.totalIn,\n tokensOut: stats.totalOut,\n turnUsage: stats.turnUsage,\n cost: parentTurnCost > 0 ? parentTurnCost : undefined,\n })\n await finalizeSession('completed')\n\n await hooks.callHook('agent:done', finalStats)\n return finalStats\n }\n catch (err) {\n // Synthesize tool_result fallbacks for any orphaned tool_use blocks on\n // the trailing assistant turn — see `synthesizeMissingToolResults` for\n // why this matters (resume rejects orphan tool_use).\n await flushTurns({ failureFallback: true })\n\n // If aborted, provider may throw — return gracefully\n if (abortController.signal.aborted) {\n session?.abortRun(runId)\n await finalizeSession('aborted')\n const stats: AgentStats = {\n totalIn: 0,\n totalOut: 0,\n totalCacheRead: 0,\n totalCacheCreation: 0,\n turns: 0,\n elapsed: 0,\n }\n await hooks.callHook('agent:done', stats)\n return stats\n }\n\n // Budget circuit breaker — semantically a \"clean stop\", not an\n // error. Finalize the run as `aborted` (matching the abort path\n // above) so the persisted record reflects the operator-imposed\n // cap rather than mis-attributing the trip to a runtime failure,\n // then re-throw so callers can branch on the typed class. The\n // run record's token / cost columns stay zero here because the\n // loop already threw before we could read its `stats` — a host\n // that wants the breaching spend on disk should hook\n // `turn:after` to mirror cumulative usage into its own ledger\n // (the predicate fires post-`turn:after` precisely for this).\n if (err instanceof AgentBudgetExceededError) {\n session?.abortRun(runId)\n await finalizeSession('aborted')\n await hooks.callHook('agent:done', {\n totalIn: 0,\n totalOut: 0,\n totalCacheRead: 0,\n totalCacheCreation: 0,\n turns: 0,\n elapsed: 0,\n })\n throw err\n }\n\n session?.errorRun(runId, errorMessage(err))\n await finalizeSession('error')\n throw err\n }\n finally {\n // Deactivate every skill active at run end. Runs on every exit path —\n // completed, aborted, or thrown — so activation state never leaks across\n // run boundaries. Explicit activation via `agent.activateSkill()` is\n // subject to the same rule: it lives until the next run boundary.\n await deactivateAllSkills()\n\n // Teardown run-scoped skill handlers (allowed-tools gate, etc.).\n uninstallAllowedToolsGate()\n uninstallDedupTools()\n uninstallToolBudgets()\n uninstallLazyDisclosureGate()\n\n unregisterSpawnHook()\n unregisterTurnSync?.()\n unregisterToolResultsSync?.()\n for (const unregister of perRunUnregisters)\n unregister()\n running = false\n abortController = undefined\n steeringQueue.length = 0\n followUpQueue.length = 0\n idleResolve?.()\n idlePromise = undefined\n idleResolve = undefined\n }\n }\n finally {\n // Outer finally — runs even if setup above the inner try threw, so\n // global state never gets stranded. Idempotent with the inner finally\n // on the happy path: re-assigning `false`/`undefined` over already\n // cleared state is a no-op, and `removeEventListener` of a never-\n // registered listener is also a no-op.\n running = false\n abortController = undefined\n idleResolve?.()\n idlePromise = undefined\n idleResolve = undefined\n if (externalSignal && externalAbortListener)\n externalSignal.removeEventListener('abort', externalAbortListener)\n }\n }\n\n function abort() {\n abortController?.abort()\n }\n\n function cancelTool(callId: string, reason?: string): boolean {\n const controller = pendingToolCancels.get(callId)\n if (!controller || controller.signal.aborted)\n return false\n // `reason` rides the abort signal so a signal-aware tool body that\n // surfaces `signal.reason` in its error path gets the same string the\n // `tool:cancelled` hook receives — keeps observability consistent.\n controller.abort(reason ?? 'user-cancelled-tool')\n return true\n }\n\n async function killBackgroundTask(taskId: string): Promise<boolean> {\n if (!executionHandle)\n return false\n if (!executionContext.killBackground)\n return false\n const info = await executionContext.killBackground(executionHandle, taskId)\n // `null` is the context's \"no such task\" return — surface as `false`\n // so the host UI can distinguish \"kill happened\" from \"id was unknown\".\n return info !== null\n }\n\n function steer(message: string) {\n steeringQueue.push(message)\n }\n\n function followUpFn(message: string) {\n followUpQueue.push(message)\n }\n\n function waitForIdle(): Promise<void> {\n return idlePromise ?? Promise.resolve()\n }\n\n async function reset() {\n // Reject mid-run resets explicitly. The loop holds its own reference to\n // the in-flight `turns` array (built per-`run()`), so clearing\n // `conversationTurns` here would have left the loop's view untouched\n // and the next `turn:after` would re-link it back to the unflushed\n // buffer — silently making the call a no-op. Throwing forces callers\n // to `await agent.waitForIdle()` first so reset's invariant\n // (\"everything cleared\") actually holds.\n if (running) {\n throw new Error(\n 'Cannot reset() while the agent is running. Call `agent.abort()` and `await agent.waitForIdle()` first.',\n )\n }\n conversationTurns = []\n steeringQueue.length = 0\n followUpQueue.length = 0\n // Background-task notifications are agent-scoped (they survive\n // `run()` boundaries). A `reset()` is a hard wipe — drop them too\n // so the next run starts clean.\n pendingTaskNotifications.clear()\n // Activation state is agent-scoped — a reset should clear it too.\n // We fire `skills:deactivate` with reason='reset' for each previously active\n // skill so consumer telemetry stays consistent. Previously fire-and-forget;\n // awaiting ensures a listener's rejection surfaces rather than silently\n // breaking the invariant \"reset clears activation\".\n const cleared = skillActivationState.clear()\n for (const record of cleared)\n await hooks.callHook('skills:deactivate', { skill: record.skill, reason: 'reset' })\n }\n\n async function activateSkill(name: string): Promise<void> {\n // Resolve the catalog on demand so hosts can `activateSkill` at any\n // point in the agent lifecycle — including before the first `run()`.\n // Idempotent and concurrency-safe (see `ensureSkillsResolved`).\n await ensureSkillsResolved()\n if (!resolvedSkills) {\n throw new Error(\n `Cannot activate skill \"${name}\" — skills are disabled for this agent.`,\n )\n }\n const skill = resolvedSkills.find(s => s.name === name)\n if (!skill) {\n const available = resolvedSkills.map(s => s.name).join(', ') || '<none>'\n throw new Error(`Unknown skill \"${name}\". Available skills: ${available}.`)\n }\n const outcome = skillActivationState.activate(skill, 'explicit')\n if (outcome === 'cap-reached') {\n throw new Error(\n `Cannot activate skill \"${name}\" — the maxActive cap of ${skillsConfig?.maxActive} has been reached.`,\n )\n }\n if (outcome === 'ok')\n await hooks.callHook('skills:activate', { skill, via: 'explicit' })\n }\n\n async function deactivateSkill(name: string): Promise<void> {\n const removed = skillActivationState.deactivate(name)\n if (removed)\n await hooks.callHook('skills:deactivate', { skill: removed.skill, reason: 'explicit' })\n }\n\n // Background-task plumbing.\n //\n // Two listeners, both registered for the agent's lifetime:\n //\n // 1. `background:exit` → enqueue a notification keyed by `taskId`.\n // The shell tool body fires this hook from its `execBackground`\n // `onExit` callback; the context-owned `child.on('close')` is what\n // settles the lifecycle. Map.set overwrites by id so a duplicate\n // enqueue (defensive — shouldn't happen with the context's at-most-\n // once settle latch) doesn't multiply notifications.\n //\n // 2. `tool:after` → suppress the would-be notification when the model\n // already learned about the exit through other means:\n // - `shell_kill`: the tool's own return value tells the model the\n // task is dead. Delete by `task_id` from the call's input.\n // - `read_file` / `read`: the model reading the task's output file\n // is a strong \"I'm aware of this task\" signal. Delete by\n // matching the `path` argument against any pending notification's\n // outputPath.\n //\n // Suppression fires AFTER the body settled, so the enqueue (in onExit)\n // and the delete (in tool:after) happen in that order — Map.delete is\n // idempotent against a missing key. See `docs/RUN_IN_BACKGROUND.md`\n // §\"code-quality checklist\" items 1 + 9 for the failure modes this\n // wiring guards against.\n hooks.hook('background:exit', (ctx) => {\n pendingTaskNotifications.set(ctx.taskId, {\n taskId: ctx.taskId,\n status: ctx.status,\n exitCode: ctx.exitCode,\n ...(ctx.signal ? { signal: ctx.signal } : {}),\n outputPath: ctx.outputPath,\n durationMs: ctx.durationMs,\n command: ctx.command,\n })\n })\n hooks.hook('tool:after', (ctx) => {\n if (ctx.name === 'shell_kill') {\n const taskId = ctx.input?.task_id\n if (typeof taskId === 'string')\n pendingTaskNotifications.delete(taskId)\n return\n }\n if (ctx.name === 'read_file' || ctx.name === 'read') {\n const rawPath = ctx.input?.path\n if (typeof rawPath !== 'string' || rawPath.length === 0)\n return\n // The shell tool returns absolute paths in its background-mode\n // result, so the model normally reads back with the same absolute\n // string. But a paranoid model (or a slash-command, or copy-paste\n // typo) can hand us a relative / `~/…` / non-canonical path that\n // resolves to the same file. Normalize both sides via\n // `path.resolve` so suppression survives those cases.\n //\n // `resolve` against `process.cwd()` matches the read_file tool's\n // own resolution behavior (it joins against the execution\n // handle's cwd, which equals process.cwd() for the default\n // process context). For sandbox/docker contexts the handle's\n // cwd may differ; the worst-case there is a missed suppression\n // (model gets a redundant notification it can ignore) — never a\n // false suppression.\n const requested = pathResolve(rawPath)\n for (const notif of pendingTaskNotifications.values()) {\n if (pathResolve(notif.outputPath) === requested) {\n pendingTaskNotifications.delete(notif.taskId)\n return\n }\n }\n }\n })\n\n // Wrap session methods to fire hooks\n if (session) {\n const originalSave = session.save.bind(session)\n const originalSetMeta = session.setMeta.bind(session)\n\n session.save = async () => {\n await originalSave()\n await hooks.callHook('session:save', { sessionId: session.id })\n }\n\n // `setMeta` has a synchronous surface (hosts `await setMeta` wouldn't gain\n // anything), but we must still surface listener errors — a promise\n // without a handler becomes an unhandled rejection. Route rejections\n // through a no-op `.catch` that records on the session via updateStatus\n // is overkill; logging here is sufficient because the primary side effect\n // (the metadata write) has already succeeded.\n session.setMeta = (key: string, value: unknown) => {\n originalSetMeta(key, value)\n // `callHook` returns `void | Promise<...>` depending on whether any\n // listener is async. Normalize to a promise so we can attach a rejection\n // handler without leaking an unhandled rejection. Intentionally do not\n // re-throw — the sync contract must not leak a listener failure back to\n // the caller of setMeta.\n void Promise.resolve(hooks.callHook('session:meta', { sessionId: session.id, key, value })).catch((err: unknown) => {\n console.error('[zidane] session:meta listener rejected:', err)\n })\n }\n }\n\n let destroyed = false\n\n /**\n * Pre-connect MCP servers. Returns the shared in-flight promise when a\n * bootstrap is already running, so concurrent callers converge on one\n * connection. Clears the cached promise on failure so the next caller can\n * retry — leaving a rejected promise cached would permanently poison future\n * runs on the same agent.\n */\n async function ensureMcpConnected(): Promise<void> {\n if (mcpConnection || allMcpServers.length === 0)\n return\n if (mcpWarmupPromise)\n return mcpWarmupPromise\n\n mcpWarmupPromise = (async () => {\n const connection = mcpConnector\n ? await mcpConnector(allMcpServers)\n : await connectMcpServers(allMcpServers, undefined, hooks)\n // If destroy() fired while we were bootstrapping, close the fresh\n // connection immediately rather than leak it into a destroyed agent.\n // destroy() awaits this promise before returning, so its subsequent\n // `if (mcpConnection) close()` branch would handle this too — but only\n // when assignment wins the race. Closing here unconditionally is safer.\n if (destroyed) {\n await connection.close().catch(() => {})\n return\n }\n mcpConnection = connection\n })()\n\n try {\n await mcpWarmupPromise\n }\n catch (err) {\n // Drop the cached promise so the next `run()` / `warmup()` call gets a\n // fresh attempt. MCP failures at bootstrap are typically transient\n // (network blip, slow stdio spawn) and the partial-failure path in\n // `connectMcpServers` already tolerates any server that does come up.\n mcpWarmupPromise = null\n throw err\n }\n }\n\n async function warmup(): Promise<void> {\n // Post-destroy no-op. Without this guard, a `warmup()` (or a `run()` that\n // calls warmup) after `destroy()` would open a fresh MCP connection that\n // nobody ever closes — the `mcpConnection.close()` branch of `destroy` has\n // already run and won't run again.\n if (destroyed)\n return\n // MCP + skills are independent — bootstrap them in parallel. Either side\n // is a no-op when already settled, so repeat `warmup()` calls stay cheap.\n // A rejection on one side still propagates via Promise.all; the other\n // side's promise stays cached and gets reused on the next call.\n await Promise.all([ensureMcpConnected(), ensureSkillsResolved()])\n }\n\n async function destroy() {\n // Idempotent: a host may call destroy() in a `finally` block AND a\n // process-level signal handler; racing double-frees crash the MCP SDK.\n if (destroyed)\n return\n destroyed = true\n\n // Teardown ordering rule: first the things that were HAPPENING\n // INSIDE the session (background tasks, in-flight tool cancels);\n // then the things the session DEPENDED ON (MCP, execution handle,\n // skills cache). Background tasks must flush their output streams\n // before the execution handle disappears underneath them.\n\n // ① INSIDE-the-session — work the session was producing.\n // Background tasks: walk the execution context's registry and let\n // the context's own `destroy(handle)` SIGTERM each task's process\n // group, flush its output stream, and clear the entry. We can't\n // walk it ourselves (the registry is private to the context) —\n // the context's destroy path handles all of this. We call it via\n // the executionHandle below; the ORDERING here just ensures we\n // don't tear down MCP first.\n //\n // The notification queue is local to this agent — drop it\n // unconditionally so a session swap that reconstructs the agent\n // doesn't replay stale notifications on the next run.\n pendingTaskNotifications.clear()\n\n // Per-call cancels: flip every still-pending one as part of\n // teardown. Without this, a destroy mid-tool would leave tool\n // bodies running against an execution context the loop is about to\n // tear down — the cancel signal gives them one last chance to\n // unwind cleanly. The loop's `finally` also drops these entries,\n // but it may not have run yet if destroy races a still-awaiting\n // call.\n for (const controller of pendingToolCancels.values()) {\n if (!controller.signal.aborted)\n controller.abort('agent-destroyed')\n }\n pendingToolCancels.clear()\n\n // If destroy races with a still-pending warmup, wait for it to finish\n // before closing. Otherwise `mcpConnection` could be populated after\n // destroy() returns and leak clients past the agent's lifetime.\n if (mcpWarmupPromise) {\n try {\n await mcpWarmupPromise\n }\n catch {\n // Bootstrap failure is already surfaced to the warmup caller; destroy\n // path just wants to clean up whatever partial state exists.\n }\n }\n\n // ② NEEDED-for-the-session — infra the session was sitting on.\n if (mcpConnection) {\n await mcpConnection.close()\n mcpConnection = null\n }\n if (executionHandle) {\n // `executionContext.destroy(handle)` is what walks the context's\n // own task registry, kills survivors, flushes their streams, and\n // drops the handle. The ordering here is what makes it safe for\n // tasks to call back into the handle while we're draining.\n await executionContext.destroy(executionHandle)\n executionHandle = null\n }\n // Defensive re-clear: the `background:exit` hook listener stays\n // registered for the agent's lifetime, so the SIGTERMs `destroy()`\n // just issued may have re-populated the queue while we were\n // awaiting `executionContext.destroy()`. Without this, the queue\n // ends up holding entries that will never drain (no more `run()`)\n // — purely cosmetic at runtime but confuses telemetry consumers\n // that snapshot agent state post-destroy.\n pendingTaskNotifications.clear()\n // Remove the inline-skills temp directory if one was materialized.\n // No-op when `skills.write` was unused.\n skillsCleanup()\n skillsCleanup = () => {}\n }\n\n // Eager bootstrap: kick off MCP connection + skills resolution in the\n // background so the first `run()` doesn't pay the full bootstrap cost.\n // Rejection is captured on the shared warmup promises; the next `warmup()`\n // / `run()` caller observes it. A detached `.catch` swallows the rejection\n // here only to prevent Node's unhandled-rejection warning — the error is\n // still attached to the promise for the next awaiter.\n const eagerHasWork = allMcpServers.length > 0 || (!skillsDisabled && !!skillsConfig)\n if (eager && eagerHasWork) {\n void warmup().catch(() => {})\n }\n\n return {\n hooks,\n run,\n abort,\n cancelTool,\n killBackgroundTask,\n steer,\n followUp: followUpFn,\n waitForIdle,\n reset,\n destroy,\n warmup,\n activateSkill,\n deactivateSkill,\n get isRunning() { return running },\n get turns() { return conversationTurns },\n get execution() { return executionContext },\n get handle() { return executionHandle },\n get session() { return session ?? null },\n get activeSkills() { return skillActivationState.active() },\n // Expose a frozen view of provider.meta. Hosts previously could mutate\n // the underlying provider meta (e.g. via `agent.meta.defaultModel = …`),\n // which quietly affected every other agent sharing the same provider\n // instance. Freezing forces callers to construct a new provider when\n // they want to override model/capabilities.\n meta: Object.freeze({ ...provider.meta }),\n }\n}\n","/**\n * Internal helpers shared between the `edit` and `multi_edit` tools.\n *\n * Not part of the public API — intentionally not re-exported from `tools/index.ts`\n * or the package barrel.\n */\n\n/**\n * Count exact (non-overlapping) occurrences of `needle` in `haystack`.\n * Returns 0 for an empty needle — both edit tools reject empty `old_string`\n * up front, so this branch is defensive rather than semantic.\n */\nexport function countExactMatches(haystack: string, needle: string): number {\n if (needle.length === 0)\n return 0\n let count = 0\n let idx = 0\n while (true) {\n const next = haystack.indexOf(needle, idx)\n if (next === -1)\n break\n count++\n idx = next + needle.length\n }\n return count\n}\n\n// ---------------------------------------------------------------------------\n// Curly-quote normalization + SDK-sanitization fallbacks\n// ---------------------------------------------------------------------------\n//\n// Models can't reliably emit Unicode curly quotes, and the Anthropic API\n// silently sanitizes a handful of XML-like tags before they hit the model\n// (`<name>` → `<n>`, etc.). When `old_string` doesn't match exactly, we retry\n// against the same content with both transforms applied so an \"old_string not\n// found\" failure that's only typographical resolves on the second pass.\n//\n// Mirrors the Claude Code Edit tool's recovery strategy. Verbatim port of the\n// quote constants and the desanitization table; the resolve loop below is\n// reorganized to share state between exact / quote-normalized / desanitized.\n\nexport const LEFT_SINGLE_CURLY_QUOTE = '\\u2018'\nexport const RIGHT_SINGLE_CURLY_QUOTE = '\\u2019'\nexport const LEFT_DOUBLE_CURLY_QUOTE = '\\u201C'\nexport const RIGHT_DOUBLE_CURLY_QUOTE = '\\u201D'\n\n/** Map curly quotes (any of the four) to their straight ASCII equivalents. */\nexport function normalizeQuotes(str: string): string {\n return str\n .replaceAll(LEFT_SINGLE_CURLY_QUOTE, '\\'')\n .replaceAll(RIGHT_SINGLE_CURLY_QUOTE, '\\'')\n .replaceAll(LEFT_DOUBLE_CURLY_QUOTE, '\"')\n .replaceAll(RIGHT_DOUBLE_CURLY_QUOTE, '\"')\n}\n\n/**\n * Substitutions Anthropic's API applies to assistant output before the model\n * sees it. The model emits the sanitized form; the file on disk contains the\n * unsanitized form. We undo the substitutions on `old_string` so the search\n * lands on the actual file contents.\n *\n * Verbatim from `claude-code/tools/FileEditTool/utils.ts`.\n */\nconst DESANITIZATIONS: ReadonlyArray<readonly [string, string]> = [\n ['<fnr>', '<function_results>'],\n ['<n>', '<name>'],\n ['</n>', '</name>'],\n ['<o>', '<output>'],\n ['</o>', '</output>'],\n ['<e>', '<error>'],\n ['</e>', '</error>'],\n ['<s>', '<system>'],\n ['</s>', '</system>'],\n ['<r>', '<result>'],\n ['</r>', '</result>'],\n ['< META_START >', '<META_START>'],\n ['< META_END >', '<META_END>'],\n ['< EOT >', '<EOT>'],\n ['< META >', '<META>'],\n ['< SOS >', '<SOS>'],\n ['\\n\\nH:', '\\n\\nHuman:'],\n ['\\n\\nA:', '\\n\\nAssistant:'],\n]\n\n/**\n * Apply the SDK desanitization table to a string. Exported so the edit tools\n * can apply it to `new_string` whenever `old_string` matched via a\n * desanitize-class fallback — keeps the file's unsanitized form on disk\n * instead of writing the model's abbreviated form back.\n */\nexport function desanitize(s: string): string {\n let out = s\n for (const [from, to] of DESANITIZATIONS)\n out = out.replaceAll(from, to)\n return out\n}\n\n/**\n * Strip line-number prefixes from each line of a needle, used as a recovery\n * fallback when the model pastes a `read_file` chunk verbatim into\n * `old_string` — the on-disk file doesn't carry the metadata prefix.\n *\n * Accepts three separator characters so a model that learned on a different\n * agent stack still works here: `\\t` (Claude Code compact, our default),\n * `|`, and `→`. Pattern: optional leading whitespace, 1-9 digits, then one\n * of `\\t | →`. The 9-digit ceiling covers files up to ~1B lines without\n * overshooting into legitimate `\\d{N}<sep>` content like Markdown table\n * cells with long numeric IDs.\n */\nconst LINE_NUMBER_PREFIX_RE = /^[ \\t]*\\d{1,9}[\\t|\\u2192]/gm\nexport function stripLineNumberPrefixes(s: string): string {\n return s.replace(LINE_NUMBER_PREFIX_RE, '')\n}\n\n/**\n * Locate the actual substring in `haystack` that corresponds to `needle`,\n * recovering from typographical mismatch on six escalating fallbacks:\n *\n * 1. Exact substring match (the happy path — no transformation).\n * 2. Curly-quote normalization on both sides — when the model emits straight\n * quotes but the file has curly ones (or vice versa), the slice of the\n * file at the matched position carries the file's actual typography.\n * 3. Anthropic-sanitization undo on the needle — the model's `<n>` becomes\n * the file's `<name>`, etc.\n * 4. Combined: desanitize + quote normalization on the needle.\n * 5. Line-number-prefix strip — when the model pasted a numbered `read_file`\n * chunk verbatim into `old_string` and the file carries no such prefix.\n * 6. Combined: line-number strip + quote normalization — paste-back of a\n * numbered chunk against a file whose typography differs from the model's.\n *\n * Returns `{ actual, occurrences }` — `actual` is the string that exists in\n * the file (use this to do the actual replace; preserves the file's\n * typography). `occurrences` is the count of those matches in the file.\n *\n * Returns `null` when no recovery worked.\n */\nexport interface ResolvedMatch {\n actual: string\n occurrences: number\n /** Recovery path — `'exact'` when no transformation was applied. */\n via: 'exact' | 'quotes' | 'desanitize' | 'quotes+desanitize' | 'line-numbers' | 'quotes+line-numbers'\n}\n\n/**\n * Search `target` in `normFile` and slice the matching span out of the\n * original `haystack`, counting all non-overlapping occurrences. `normFile`\n * is the haystack with whatever transform (quotes / desanitize / combined)\n * was applied to make the indices align — slicing the original haystack\n * preserves the file's actual typography so `replace_all` writes back the\n * file's form, not the model's.\n *\n * Pre-condition: `normFile.length === haystack.length` (every transform\n * we use is one-to-one). Returns null on miss.\n */\nfunction locateAndCount(\n haystack: string,\n normFile: string,\n target: string,\n via: ResolvedMatch['via'],\n): ResolvedMatch | null {\n const idx = normFile.indexOf(target)\n if (idx === -1)\n return null\n const actual = haystack.slice(idx, idx + target.length)\n let occ = 0\n let cursor = 0\n while (true) {\n const next = normFile.indexOf(target, cursor)\n if (next === -1)\n break\n occ++\n cursor = next + target.length\n }\n return { actual, occurrences: occ, via }\n}\n\nexport function resolveOldString(haystack: string, needle: string): ResolvedMatch | null {\n // 1. Exact match — happy path.\n const exact = countExactMatches(haystack, needle)\n if (exact > 0)\n return { actual: needle, occurrences: exact, via: 'exact' }\n\n // 2. Curly-quote normalization. Either side can carry curly quotes:\n // model emitted straight + file has curly, OR model emitted curly +\n // file has straight. Normalize both and look there. Slice the original\n // haystack to recover the actual file-side typography.\n const normNeedle = normalizeQuotes(needle)\n const normFile = normalizeQuotes(haystack)\n if (normNeedle !== needle || normFile !== haystack) {\n const m = locateAndCount(haystack, normFile, normNeedle, 'quotes')\n if (m)\n return m\n }\n\n // 3. Desanitize the needle — model-side substitution undo (`<n>` → `<name>`,\n // etc.) The file always has the unsanitized form; only the model emits\n // the abbreviated version.\n const desan = desanitize(needle)\n if (desan !== needle) {\n const desanCount = countExactMatches(haystack, desan)\n if (desanCount > 0)\n return { actual: desan, occurrences: desanCount, via: 'desanitize' }\n }\n\n // 4. Combined: desanitize + quote normalization. Hit when the model\n // emitted both sanitized tokens and straight quotes, but the file has\n // the unsanitized form with curly quotes.\n const combo = desanitize(normNeedle)\n if (combo !== needle) {\n const m = locateAndCount(haystack, normFile, combo, 'quotes+desanitize')\n if (m)\n return m\n }\n\n // 5. Line-number-prefix strip. When `read_file` returns line-numbered\n // output and the model pasted the chunk verbatim into `old_string`,\n // the on-disk file doesn't carry those prefixes. Strip them and\n // retry; the file-side `actual` is the prefix-free needle so the\n // replacement lands on real bytes.\n //\n // Guard against a needle that strips down to whitespace-only (e.g.\n // `1\\t\\n2\\t\\n3\\t` → `\\n\\n`). Searching for `\\n\\n` would match arbitrary\n // blank-line patterns anywhere in the file — a high false-positive risk.\n // Require the stripped needle to carry at least one non-whitespace\n // character before we trust the result.\n const stripped = stripLineNumberPrefixes(needle)\n if (stripped !== needle && stripped.trim().length > 0) {\n const count = countExactMatches(haystack, stripped)\n if (count > 0)\n return { actual: stripped, occurrences: count, via: 'line-numbers' }\n\n // 6. Combined: line-number strip + curly-quote normalization. The\n // typography mismatch may live in the file, the needle, or both.\n const strippedNorm = normalizeQuotes(stripped)\n if (strippedNorm !== stripped || normFile !== haystack) {\n const m = locateAndCount(haystack, normFile, strippedNorm, 'quotes+line-numbers')\n if (m)\n return m\n }\n }\n\n return null\n}\n\n/**\n * Apply the same recovery transforms used to find `old_string` to\n * `new_string`, so the file gets back its native form: desanitize when\n * the model emitted `<n>` for `<name>`, strip line-number prefixes when\n * the match required them, then re-curlify when the match required\n * quote normalization. Shared between `edit` and `multi_edit`.\n */\nexport function styleReplacementForVia(\n replacement: string,\n via: ResolvedMatch['via'],\n actual: string,\n): string {\n let out = replacement\n if (via === 'desanitize' || via === 'quotes+desanitize')\n out = desanitize(out)\n if (via === 'line-numbers' || via === 'quotes+line-numbers')\n out = stripLineNumberPrefixes(out)\n if (via === 'quotes' || via === 'quotes+desanitize' || via === 'quotes+line-numbers')\n out = preserveQuoteStyle(actual, out)\n return out\n}\n\n/**\n * When `old_string` matched via curly-quote normalization, re-style\n * `new_string` so the file's typography is preserved across the edit.\n * Detects whether the matched file region had curly singles, doubles, or\n * both, and applies the matching curlification to the replacement.\n *\n * Apostrophes in contractions (`don't`, `it's`) get the right-single curly\n * quote regardless of opening context — that's the canonical typographer's\n * convention for English. Other quotes use a simple\n * preceded-by-whitespace-or-opening-punctuation heuristic.\n */\nexport function preserveQuoteStyle(actual: string, replacement: string): string {\n const hasDouble = actual.includes(LEFT_DOUBLE_CURLY_QUOTE) || actual.includes(RIGHT_DOUBLE_CURLY_QUOTE)\n const hasSingle = actual.includes(LEFT_SINGLE_CURLY_QUOTE) || actual.includes(RIGHT_SINGLE_CURLY_QUOTE)\n if (!hasDouble && !hasSingle)\n return replacement\n\n let out = replacement\n if (hasDouble)\n out = applyCurly(out, '\"', LEFT_DOUBLE_CURLY_QUOTE, RIGHT_DOUBLE_CURLY_QUOTE, false)\n if (hasSingle)\n out = applyCurly(out, '\\'', LEFT_SINGLE_CURLY_QUOTE, RIGHT_SINGLE_CURLY_QUOTE, true)\n return out\n}\n\nfunction applyCurly(\n s: string,\n straight: string,\n left: string,\n right: string,\n contractionAware: boolean,\n): string {\n const chars = [...s]\n const result: string[] = []\n for (let i = 0; i < chars.length; i++) {\n if (chars[i] !== straight) {\n result.push(chars[i])\n continue\n }\n if (contractionAware) {\n const prev = i > 0 ? chars[i - 1] : ''\n const next = i < chars.length - 1 ? chars[i + 1] : ''\n // Letter-quote-letter is a contraction, not a quote — always right.\n if (/\\p{L}/u.test(prev) && /\\p{L}/u.test(next)) {\n result.push(right)\n continue\n }\n }\n result.push(isOpeningContext(chars, i) ? left : right)\n }\n return result.join('')\n}\n\nfunction isOpeningContext(chars: string[], i: number): boolean {\n if (i === 0)\n return true\n const prev = chars[i - 1]\n return prev === ' ' || prev === '\\t' || prev === '\\n' || prev === '\\r'\n || prev === '(' || prev === '[' || prev === '{'\n || prev === '\\u2014' || prev === '\\u2013' // em / en dash\n}\n","/**\n * Path-suggestion helper for file-not-found errors.\n *\n * When `read_file` / `edit` / `multi_edit` get a missing-file error, walk the\n * parent directory in the agent's execution context for files sharing the\n * same basename (sans extension) — common cause: model picked the wrong\n * extension (`.js` vs `.ts`, `.md` vs `.mdx`). Returns the closest sibling\n * filename so the tool can append `Did you mean X?` to the error.\n *\n * Mirrors `claude-code/utils/file.ts` `findSimilarFile`. Goes through the\n * `ExecutionContext.listFiles` seam so it works in process / docker / sandbox.\n */\n\nimport type { ExecutionContext, ExecutionHandle } from '../contexts'\n\n/**\n * Find a sibling file in the same directory sharing `path`'s basename\n * (sans extension), excluding the missing path itself. Returns just the\n * filename (not the full path) when found, otherwise `null`.\n *\n * Silent on errors — a missing parent directory or a `listFiles` failure\n * means we have no suggestion, not that anything is wrong.\n */\nexport async function findSimilarFile(\n execution: ExecutionContext,\n handle: ExecutionHandle,\n path: string,\n): Promise<string | null> {\n const slash = path.lastIndexOf('/')\n const dir = slash === -1 ? '.' : (path.slice(0, slash) || '/')\n const target = slash === -1 ? path : path.slice(slash + 1)\n const dot = target.lastIndexOf('.')\n const targetBase = dot === -1 ? target : target.slice(0, dot)\n\n if (targetBase.length === 0)\n return null\n\n let entries: string[]\n try {\n entries = await execution.listFiles(handle, dir)\n }\n catch {\n return null\n }\n\n for (const entry of entries) {\n if (entry === target)\n continue\n const entryDot = entry.lastIndexOf('.')\n const entryBase = entryDot === -1 ? entry : entry.slice(0, entryDot)\n if (entryBase === targetBase)\n return entry\n }\n return null\n}\n\n/**\n * Format a `Did you mean X?` suffix for missing-file errors. Returns an empty\n * string when no suggestion is available so callers can string-concat\n * unconditionally.\n */\nexport async function suggestionFor(\n execution: ExecutionContext,\n handle: ExecutionHandle,\n path: string,\n): Promise<string> {\n const sibling = await findSimilarFile(execution, handle, path)\n return sibling ? ` Did you mean ${sibling}?` : ''\n}\n","import type { ToolContext, ToolDef } from './types'\nimport { resolveOldString, stripLineNumberPrefixes, styleReplacementForVia } from './edit-utils'\nimport { suggestionFor } from './path-suggest'\nimport { hashContent, readStateKey, resolveReadStateMap } from './read-state'\n\n/**\n * Surgical edit — replace `old_string` with `new_string` in a single file.\n *\n * Mirrors Claude Code's `Edit` semantics so models post-trained on Anthropic's\n * tool surface need no relearning. Fails clearly when `old_string` isn't unique\n * (unless `replace_all: true`) and when not found, with a nearest-match preview\n * so the model can recover without a separate `read_file` round-trip.\n */\n\nexport const edit: ToolDef = {\n spec: {\n name: 'edit',\n description: 'Replace exact `old_string` with `new_string` in a file. Fails if `old_string` is not unique unless `replace_all: true`. Prefer over `write_file` for surgical changes — preserves the rest of the file. Tolerates `read_file` line-number prefixes (`<N>\\\\t…`, `<N>|…`, or `<N>→…`) in `old_string` / `new_string` — they are stripped before matching/writing, so you can paste a numbered chunk verbatim.',\n inputSchema: {\n type: 'object',\n properties: {\n path: { type: 'string', description: 'Relative file path.' },\n old_string: { type: 'string', description: 'Exact substring to find.' },\n new_string: { type: 'string', description: 'Replacement substring.' },\n replace_all: { type: 'boolean', description: 'Replace every occurrence. Default: false.' },\n },\n required: ['path', 'old_string', 'new_string'],\n },\n },\n async execute({ path, old_string, new_string, replace_all }, ctx: ToolContext) {\n const target = path as string\n const find = old_string as string\n const replacement = new_string as string\n const replaceAll = replace_all === true\n\n if (find === replacement)\n return `Edit error: old_string and new_string are identical — nothing to change in ${target}.`\n\n if (find.length === 0)\n return `Edit error: old_string is empty. Use write_file to create or fully overwrite a file.`\n\n let original: string\n try {\n original = await ctx.execution.readFile(ctx.handle, target)\n }\n catch {\n const hint = await suggestionFor(ctx.execution, ctx.handle, target)\n return `Edit error: file not found: ${target}.${hint}`\n }\n\n // Read-before-edit guard. When `behavior.requireReadBeforeEdit` is on\n // and a read-state map is available (session-keyed or forwarded via\n // `ctx.readState` from a spawn `shareReadState` parent), refuse edits\n // against files the model hasn't read this session, and against files\n // whose on-disk content has drifted from what the model last saw —\n // both produce silent corruption when an edit applies a substring\n // against bytes the model \"remembers\" but no longer reflect reality.\n //\n // Without any read-state map (no session, no explicit forward) the\n // guard is skipped: tracking has nowhere to live, so the gate has\n // nothing to check against.\n if (ctx.behavior?.requireReadBeforeEdit) {\n const readState = resolveReadStateMap(ctx)\n if (readState) {\n const absKey = readStateKey(ctx.handle.cwd, target)\n const prior = readState.get(absKey)\n if (!prior)\n return `Edit error: ${target} has not been read in this session. Call read_file first so the edit applies against the current contents.`\n if (prior.contentHash !== hashContent(original))\n return `Edit error: ${target} has changed on disk since the last read. Re-read the file before editing.`\n }\n }\n\n // Resolve `old_string` against the file content with the recovery\n // cascade in `resolveOldString` (exact → quotes → desanitize → combined\n // → line-number-strip → strip+quotes). On a non-exact hit, `match.actual`\n // is the file-side substring (preserves the file's typography) and the\n // replacement is re-styled below to match.\n const match = resolveOldString(original, find)\n\n if (!match) {\n const preview = nearestMatchPreview(original, find)\n return preview\n ? `Edit error: old_string not found in ${target}. Closest match in the file: ${preview}`\n : `Edit error: old_string not found in ${target}.`\n }\n\n const { actual, occurrences, via } = match\n\n if (occurrences > 1 && !replaceAll)\n return `Edit error: old_string appears ${occurrences} times in ${target}. Pass replace_all=true or expand old_string for uniqueness.`\n\n const styledReplacement = styleReplacementForVia(replacement, via, actual)\n\n const updated = replaceAll\n ? original.split(actual).join(styledReplacement)\n : original.replace(actual, styledReplacement)\n\n if (updated === original)\n return `Edit error: replacement produced no change in ${target}.`\n\n await ctx.execution.writeFile(ctx.handle, target, updated)\n\n // Keep read-state coherent — after a successful edit, the model\n // knows the post-edit content (it produced new_string), so we\n // record the new hash against the same slice. This lets a chain\n // of edits proceed without re-reading between each one even with\n // `requireReadBeforeEdit` on.\n const readState = resolveReadStateMap(ctx)\n if (readState) {\n const absKey = readStateKey(ctx.handle.cwd, target)\n const prior = readState.get(absKey)\n if (prior)\n readState.set(absKey, { ...prior, contentHash: hashContent(updated), mtimeMs: Date.now() })\n }\n\n return `Edited ${target}: replaced ${occurrences} occurrence${occurrences === 1 ? '' : 's'}.`\n },\n}\n\n/**\n * Find the line that shares the longest common prefix with the needle's first\n * line. Cheap heuristic — better than nothing for the common \"model has a typo\"\n * case (off-by-one indent, trailing whitespace, escape mismatch).\n *\n * Returns a \"line N: <preview>\" snippet or null when no line shares a useful\n * prefix. Strips line-number prefixes from the needle first so a model that\n * pasted a numbered `read_file` chunk verbatim still gets a useful diagnostic.\n */\nfunction nearestMatchPreview(haystack: string, needle: string): string | null {\n // If the model included `<N>\\t` (or `<N>|` / `<N>→`) prefixes from a\n // numbered read, strip them before scoring — otherwise the prefix\n // dominates and we never match real file lines.\n const normalizedNeedle = stripLineNumberPrefixes(needle)\n const needleFirstLine = normalizedNeedle.split('\\n')[0]\n if (needleFirstLine.length < 3)\n return null\n\n const lines = haystack.split('\\n')\n let bestScore = 0\n let bestIdx = -1\n for (let i = 0; i < lines.length; i++) {\n const score = sharedPrefixLength(lines[i], needleFirstLine)\n if (score > bestScore) {\n bestScore = score\n bestIdx = i\n }\n }\n if (bestIdx < 0 || bestScore < Math.min(8, Math.floor(needleFirstLine.length / 2)))\n return null\n\n const snippet = lines[bestIdx].slice(0, 80)\n return `line ${bestIdx + 1}: ${JSON.stringify(snippet)}`\n}\n\nfunction sharedPrefixLength(a: string, b: string): number {\n const max = Math.min(a.length, b.length)\n let i = 0\n while (i < max && a.charCodeAt(i) === b.charCodeAt(i))\n i++\n return i\n}\n","import type { ToolContext, ToolDef } from './types'\nimport { stat } from 'node:fs/promises'\nimport { resolve } from 'node:path'\nimport { errorMessage } from '../errors'\n\n/**\n * Glob-pattern file matching.\n *\n * Uses Bun's native `Bun.Glob` engine when running in the in-process execution\n * context. For non-process contexts (docker, sandbox), falls back to running\n * the pattern through a shell `find` invocation so the match is executed\n * wherever the context lives.\n *\n * Results are capped at 1000 entries to keep model input bounded.\n *\n * By default each row carries `<path>\\t<size>\\t<mtime>` metadata so the\n * model can rank \"what changed recently\" without a follow-up `read_file`.\n * Pass `metadata: false` to fall back to plain newline-separated paths.\n * Metadata is best-effort: in-process contexts use `node:fs/promises stat`\n * (parallelized); non-process contexts fall through to the path-only shape\n * since shelling out per file would dominate the call latency.\n */\nconst DEFAULT_LIMIT = 1000\n\n// Only allow characters that are meaningful in glob patterns plus path/name chars.\n// Anything else (quotes, backticks, $, ;, |, <, >, spaces-in-weird-places, etc.) is\n// rejected so the shell fallback can't be hijacked via a crafted `pattern`.\nconst SAFE_GLOB_PATTERN_RE = /^[\\w./*?[\\]{}!,^@+-]+$/\n\nasync function globInProcess(pattern: string, cwd: string, limit: number): Promise<string[]> {\n // Bun.Glob is available in Bun runtime; the agent targets Bun so this is safe.\n const glob = new Bun.Glob(pattern)\n const results: string[] = []\n for await (const file of glob.scan({ cwd })) {\n results.push(file)\n if (results.length >= limit)\n break\n }\n return results.sort()\n}\n\nasync function globViaShell(pattern: string, ctx: ToolContext, limit: number): Promise<string[]> {\n // Reject any characters that could break out of the single-quoted arg. With the\n // allowlist above, pattern is guaranteed shell-safe when single-quoted.\n if (!SAFE_GLOB_PATTERN_RE.test(pattern))\n throw new Error('Glob pattern contains unsupported characters (shell fallback only allows path/glob metacharacters)')\n\n // `find -path`'s `*` matches any character — including `/` — so `**` and `*` are\n // effectively equivalent. Using the user pattern verbatim keeps semantics close to\n // Bun.Glob for recursive matches. `find -name` (no slash in pattern) matches basenames.\n const useBasename = !pattern.includes('/')\n const finder = useBasename\n ? `find . -type f -name '${pattern}'`\n : `find . -type f -path './${pattern}'`\n const searchCmd = `${finder} 2>/dev/null | sed 's|^./||' | sort | head -n ${limit}`\n const result = await ctx.execution.exec(ctx.handle, searchCmd)\n if (result.exitCode !== 0 && !result.stdout)\n return []\n return result.stdout.split('\\n').filter(line => line.length > 0)\n}\n\nexport const glob: ToolDef = {\n // Filesystem listing only — safe to fan out alongside other reads.\n isConcurrencySafe: true,\n spec: {\n name: 'glob',\n description: 'Match files by glob pattern (supports **, *, ?). Relative to the execution context cwd. By default each row is `<path>\\\\t<size-bytes>\\\\t<mtime-iso>`; set `metadata: false` for a plain newline-separated list of paths. Always sorted.',\n inputSchema: {\n type: 'object',\n properties: {\n pattern: {\n type: 'string',\n description: 'Glob pattern (e.g. \"src/**/*.ts\", \"*.md\", \"test/**/fixtures/*\").',\n },\n limit: {\n type: 'number',\n description: `Maximum number of matches to return. Default: ${DEFAULT_LIMIT}.`,\n },\n metadata: {\n type: 'boolean',\n description: 'Append size (bytes) and mtime (ISO) per row, tab-separated. Default: true. In-process only — non-process execution contexts always return paths.',\n },\n },\n required: ['pattern'],\n },\n },\n async execute({ pattern, limit, metadata }, ctx: ToolContext) {\n const pat = pattern as string\n const max = typeof limit === 'number' && limit > 0 ? limit : DEFAULT_LIMIT\n const wantMetadata = metadata !== false\n\n try {\n const entries = ctx.execution.type === 'process'\n ? await globInProcess(pat, ctx.handle.cwd, max)\n : await globViaShell(pat, ctx, max)\n if (entries.length === 0)\n return '(no matches)'\n\n // Metadata only makes sense in-process — shelling out per file would\n // dominate latency for large match sets, and `find -printf` is\n // GNU-only. Non-process contexts return paths only.\n if (!wantMetadata || ctx.execution.type !== 'process')\n return entries.join('\\n')\n\n const rows = await Promise.all(entries.map(async (rel) => {\n try {\n const s = await stat(resolve(ctx.handle.cwd, rel))\n return `${rel}\\t${s.size}\\t${new Date(s.mtimeMs).toISOString()}`\n }\n catch {\n // Stat failure (race with deletion, perms) — emit path with\n // empty columns so the row count still matches.\n return `${rel}\\t\\t`\n }\n }))\n return rows.join('\\n')\n }\n catch (err) {\n return `Glob error: ${errorMessage(err)}`\n }\n },\n}\n","import type { ToolContext, ToolDef } from './types'\nimport { errorMessage } from '../errors'\nimport { shellQuote } from './shell-quote'\n\n/**\n * Search file contents by regex.\n *\n * Wraps ripgrep (`rg`) when available, falls back to an in-process Bun.Glob +\n * regex implementation when running in the in-process execution context. For\n * non-process contexts without `rg`, returns a clear hint rather than silently\n * doing nothing.\n *\n * The tool surface mirrors Claude Code's `Grep` so models authored against the\n * Anthropic tool surface need no relearning. Output modes:\n * - `files_with_matches` (default) — newline-separated paths.\n * - `content` — `path:line:match` (line numbers on by default).\n * - `count` — `path:N` per matching file.\n *\n * Results are capped via `head_limit` (default 250) to keep model input\n * bounded; `offset` lets the caller page through.\n */\n\nconst DEFAULT_HEAD_LIMIT = 250\nconst DEFAULT_OUTPUT_MODE: OutputMode = 'files_with_matches'\n\ntype OutputMode = 'content' | 'files_with_matches' | 'count'\n\ninterface GrepInput {\n 'pattern': string\n 'path'?: string\n 'glob'?: string\n 'type'?: string\n 'output_mode'?: OutputMode\n '-i'?: boolean\n '-n'?: boolean\n '-A'?: number\n '-B'?: number\n '-C'?: number\n 'multiline'?: boolean\n 'head_limit'?: number\n 'offset'?: number\n}\n\nexport const grep: ToolDef = {\n // Searches files without touching them — concurrency-safe.\n isConcurrencySafe: true,\n spec: {\n name: 'grep',\n description: 'Search file contents by regex. Returns matching paths (default), match content, or per-file counts. Backed by ripgrep when available with a Bun.Glob fallback for in-process runs.',\n inputSchema: {\n type: 'object',\n properties: {\n 'pattern': { type: 'string', description: 'Regex (PCRE-flavored via ripgrep, JS regex via fallback).' },\n 'path': { type: 'string', description: 'File or directory to search. Default: \".\".' },\n 'glob': { type: 'string', description: 'Restrict to files matching this glob, e.g. \"**/*.ts\".' },\n 'type': { type: 'string', description: 'rg file type filter, e.g. \"ts\", \"py\", \"rust\". Ignored by the fallback.' },\n 'output_mode': { type: 'string', enum: ['content', 'files_with_matches', 'count'], description: 'Default: \"files_with_matches\".' },\n '-i': { type: 'boolean', description: 'Case-insensitive match.' },\n '-n': { type: 'boolean', description: 'Show line numbers (content mode). Default: true.' },\n '-A': { type: 'integer', description: 'Lines of trailing context (content mode).' },\n '-B': { type: 'integer', description: 'Lines of leading context (content mode).' },\n '-C': { type: 'integer', description: 'Lines of surrounding context (content mode). Overridden by -A/-B if set.' },\n 'multiline': { type: 'boolean', description: 'Allow patterns to match across line boundaries.' },\n 'head_limit': { type: 'integer', description: 'Cap output entries. Default: 250. Set 0 for unlimited.' },\n 'offset': { type: 'integer', description: 'Skip first N entries. Default: 0.' },\n },\n required: ['pattern'],\n },\n },\n async execute(rawInput, ctx: ToolContext) {\n const input = rawInput as unknown as GrepInput\n\n const useRg = await isRipgrepAvailable(ctx)\n if (useRg)\n return runViaRipgrep(input, ctx)\n\n if (ctx.execution.type === 'process')\n return runInProcess(input, ctx)\n\n return 'grep error: ripgrep is not available in the execution context. Install `rg` or use the `shell` tool with grep/awk.'\n },\n}\n\n// ---------------------------------------------------------------------------\n// ripgrep path\n// ---------------------------------------------------------------------------\n\n/**\n * Probe ripgrep availability **per call**. The probe is intentionally not\n * cached: caching at module scope would leak across execution contexts (an\n * orchestrator running an in-process agent and a docker agent in the same\n * Node process must be able to differ on whether `rg` exists), and caching\n * per handle adds bookkeeping for negligible savings — `rg --version` is\n * ~5 ms, and grep is invoked at most a few times per turn.\n */\nasync function isRipgrepAvailable(ctx: ToolContext): Promise<boolean> {\n const result = await ctx.execution.exec(ctx.handle, 'rg --version')\n return result.exitCode === 0\n}\n\nasync function runViaRipgrep(input: GrepInput, ctx: ToolContext): Promise<string> {\n const args = ['rg']\n const mode = (input.output_mode ?? DEFAULT_OUTPUT_MODE) as OutputMode\n\n if (mode === 'files_with_matches')\n args.push('--files-with-matches')\n else if (mode === 'count')\n args.push('--count')\n else\n args.push((input['-n'] ?? true) ? '--line-number' : '--no-line-number')\n\n if (input['-i'])\n args.push('-i')\n\n if (mode === 'content') {\n if (typeof input['-A'] === 'number')\n args.push('-A', String(input['-A']))\n if (typeof input['-B'] === 'number')\n args.push('-B', String(input['-B']))\n if (typeof input['-C'] === 'number' && typeof input['-A'] !== 'number' && typeof input['-B'] !== 'number')\n args.push('-C', String(input['-C']))\n }\n\n if (input.multiline)\n args.push('--multiline', '--multiline-dotall')\n\n if (input.glob)\n args.push('--glob', input.glob)\n\n if (input.type)\n args.push('--type', input.type)\n\n args.push('--', input.pattern)\n\n // Always pass a path — rg reads from stdin when invoked from a non-TTY child\n // process without a path argument, which hangs forever waiting for input.\n args.push(input.path ?? '.')\n\n const command = args.map(shellQuote).join(' ')\n const result = await ctx.execution.exec(ctx.handle, command)\n\n // ripgrep exits 1 when there are no matches — that's not an error here.\n if (result.exitCode !== 0 && result.exitCode !== 1) {\n return `grep error: ${result.stderr.trim() || `rg exited with code ${result.exitCode}`}`\n }\n\n return formatPaginated(result.stdout, input)\n}\n\n// ---------------------------------------------------------------------------\n// In-process fallback (Bun.Glob + readFile + regex)\n// ---------------------------------------------------------------------------\n\nasync function runInProcess(input: GrepInput, ctx: ToolContext): Promise<string> {\n const mode = (input.output_mode ?? DEFAULT_OUTPUT_MODE) as OutputMode\n const flags = `${input['-i'] ? 'i' : ''}${input.multiline ? 's' : ''}${mode !== 'content' ? '' : 'g'}`\n\n let regex: RegExp\n try {\n regex = new RegExp(input.pattern, flags || undefined)\n }\n catch (err) {\n return `grep error: invalid regex: ${errorMessage(err)}`\n }\n\n const files = await enumerateFiles(input, ctx)\n const showLineNumbers = input['-n'] ?? true\n const before = (input['-B'] ?? input['-C'] ?? 0)\n const after = (input['-A'] ?? input['-C'] ?? 0)\n\n const lines: string[] = []\n for (const path of files) {\n let content: string\n try {\n content = await ctx.execution.readFile(ctx.handle, path)\n }\n catch {\n continue\n }\n if (input.multiline) {\n // Multiline mode: scan the whole buffer with /g semantics.\n const allMatches = [...content.matchAll(new RegExp(regex.source, `${flags.replace(/g/, '')}g`))]\n if (allMatches.length === 0)\n continue\n if (mode === 'files_with_matches') {\n lines.push(path)\n continue\n }\n if (mode === 'count') {\n lines.push(`${path}:${allMatches.length}`)\n continue\n }\n // content mode — emit each match's surrounding line context.\n for (const m of allMatches) {\n const lineStart = content.lastIndexOf('\\n', m.index! - 1) + 1\n const lineEnd = content.indexOf('\\n', m.index!)\n const snippet = content.slice(lineStart, lineEnd === -1 ? undefined : lineEnd)\n const lineNo = content.slice(0, m.index!).split('\\n').length\n lines.push(formatContentLine(path, lineNo, snippet, showLineNumbers))\n }\n continue\n }\n\n const fileLines = content.split('\\n')\n const matched: number[] = []\n for (let i = 0; i < fileLines.length; i++) {\n regex.lastIndex = 0\n if (regex.test(fileLines[i]))\n matched.push(i)\n }\n if (matched.length === 0)\n continue\n\n if (mode === 'files_with_matches') {\n lines.push(path)\n continue\n }\n if (mode === 'count') {\n lines.push(`${path}:${matched.length}`)\n continue\n }\n\n const includeLineNos = new Set<number>()\n for (const m of matched) {\n for (let i = Math.max(0, m - before); i <= Math.min(fileLines.length - 1, m + after); i++)\n includeLineNos.add(i)\n }\n const sorted = [...includeLineNos].sort((a, b) => a - b)\n let prev = -2\n for (const lineNo of sorted) {\n if (lineNo > prev + 1 && lines.length > 0)\n lines.push('--')\n const snippet = fileLines[lineNo]\n lines.push(formatContentLine(path, lineNo + 1, snippet, showLineNumbers))\n prev = lineNo\n }\n }\n\n return formatPaginated(lines.join('\\n'), input)\n}\n\nfunction formatContentLine(path: string, lineNo: number, snippet: string, showLineNumbers: boolean): string {\n return showLineNumbers ? `${path}:${lineNo}:${snippet}` : `${path}:${snippet}`\n}\n\nasync function enumerateFiles(input: GrepInput, ctx: ToolContext): Promise<string[]> {\n const cwd = ctx.handle.cwd\n const root = input.path ?? '.'\n\n // If `path` is a single file, just return it.\n // Cheap heuristic: contains a dot in the basename and no glob wildcards.\n if (input.path && !input.path.includes('*') && !input.path.includes('?')) {\n try {\n const stat = await ctx.execution.exec(ctx.handle, `test -f ${shellQuote(input.path)} && echo file || echo dir`)\n if (stat.stdout.trim() === 'file')\n return [input.path]\n }\n catch { /* fall through to glob enumeration */ }\n }\n\n const pattern = input.glob ?? '**/*'\n const glob = new Bun.Glob(pattern)\n const out: string[] = []\n const scanRoot = root === '.' ? cwd : `${cwd.replace(/\\/$/, '')}/${root.replace(/^\\.\\//, '')}`\n for await (const file of glob.scan({ cwd: scanRoot, onlyFiles: true })) {\n out.push(root === '.' ? file : `${root.replace(/\\/$/, '')}/${file}`)\n }\n return out.sort()\n}\n\nfunction formatPaginated(text: string, input: GrepInput): string {\n const headLimit = typeof input.head_limit === 'number' && input.head_limit >= 0\n ? input.head_limit\n : DEFAULT_HEAD_LIMIT\n const offset = typeof input.offset === 'number' && input.offset > 0\n ? Math.floor(input.offset)\n : 0\n\n if (!text.trim())\n return '(no matches)'\n\n const lines = text.split('\\n').filter(l => l.length > 0)\n const total = lines.length\n\n const sliced = headLimit === 0\n ? lines.slice(offset)\n : lines.slice(offset, offset + headLimit)\n\n if (sliced.length === 0)\n return '(no matches in this slice)'\n\n const truncatedHead = offset > 0\n const truncatedTail = headLimit > 0 && offset + headLimit < total\n\n let out = sliced.join('\\n')\n if (truncatedHead)\n out = `…(${offset} earlier matches skipped)…\\n${out}`\n if (truncatedTail)\n out = `${out}\\n…(${total - offset - headLimit} more matches; re-run with offset=${offset + headLimit} or larger head_limit)`\n return out\n}\n","/**\n * Interaction tool — lets the agent request structured input from the outside world.\n *\n * Not included in any preset by default. Add it explicitly:\n *\n * import { createInteractionTool } from 'zidane'\n *\n * const askUser = createInteractionTool({\n * schema: { type: 'object', properties: { question: { type: 'string' } }, required: ['question'] },\n * onRequest: async (payload) => {\n * const answer = await promptUser(payload.question)\n * return { answer }\n * },\n * })\n *\n * const preset = definePreset({ name: 'interactive', tools: { ...basicTools, ask_user: askUser } })\n */\n\nimport type { ToolContext, ToolDef } from './types'\n\nexport interface InteractionToolOptions {\n /** JSON Schema for the request payload the model sends */\n schema: Record<string, unknown>\n /** Tool name (default: 'interaction') */\n name?: string\n /** Tool description shown to the model */\n description?: string\n /** Called when the model invokes this tool. Receives the validated payload and tool context, returns data for the model. */\n onRequest: (payload: Record<string, unknown>, ctx: ToolContext) => Promise<Record<string, unknown> | string>\n}\n\n/**\n * Create an interaction tool that lets the agent request structured input.\n *\n * The model calls this tool with a payload matching the schema.\n * `onRequest` is called with the payload and should return the response\n * (string or object) that gets sent back to the model as the tool result.\n */\nexport function createInteractionTool(options: InteractionToolOptions): ToolDef {\n const name = options.name ?? 'interaction'\n const description = options.description ?? 'Request structured input from the user or external system.'\n\n return {\n spec: {\n name,\n description,\n inputSchema: options.schema,\n },\n async execute(input, ctx) {\n const result = await options.onRequest(input, ctx)\n\n return typeof result === 'string' ? result : JSON.stringify(result)\n },\n }\n}\n","import type { ToolContext, ToolDef } from './types'\n\nexport const listFiles: ToolDef = {\n // Directory listing only — safe to parallelize alongside other reads.\n isConcurrencySafe: true,\n spec: {\n name: 'list_files',\n description: 'List files and directories at the given path (relative to project root).',\n inputSchema: {\n type: 'object',\n properties: {\n path: { type: 'string', description: 'Relative directory path (default: \".\")' },\n },\n required: [],\n },\n },\n async execute({ path }, ctx: ToolContext) {\n try {\n const entries = await ctx.execution.listFiles(ctx.handle, (path as string) || '.')\n return entries.join('\\n') || '(empty directory)'\n }\n catch {\n return `Directory not found: ${path}`\n }\n },\n}\n","import type { ToolContext, ToolDef } from './types'\nimport { resolveOldString, styleReplacementForVia } from './edit-utils'\nimport { suggestionFor } from './path-suggest'\nimport { hashContent, readStateKey, resolveReadStateMap } from './read-state'\n\n/**\n * Multi-edit on a single file — best-effort, per-hunk semantics.\n *\n * Each step replaces `old_string` with `new_string` against the file\n * contents *as left by the previous applied edit*. A per-step failure\n * (`old_string` not found, ambiguous match without `replace_all`,\n * identical strings, malformed input) is recorded against THAT step\n * only — the remaining steps still run. The file is written iff at\n * least one step applied. The result text reports per-hunk outcomes\n * inline and (when any step failed) carries an\n * `<edit-outcomes>…</edit-outcomes>` annotation block so the chat layer\n * + replay can paint per-hunk badges via `parseEditOutcomesFromResult`.\n *\n * Per-hunk approval is a TUI/host concern. When the user partially\n * denies a `multi_edit` from the file-edit modal, the gate handler\n * rebinds `ctx.input.edits` to the approved subset before this tool\n * runs — so the body sees a smaller all-approved batch and reports\n * outcomes keyed against THAT subset. The host's `tool:transform`\n * hook merges this subset-keyed result with the approval-side denied\n * entries (1:1 with the model's ORIGINAL `edits` list) before the\n * canonical `<edit-outcomes>` block lands on the wire — see\n * `mergeApprovalAndBodyOutcomes` in `chat/edit-approval.ts`.\n *\n * Wire / replay format (see `parseEditOutcomesFromResult`):\n *\n * Edited <path>: applied N of M edits (R replacements).\n * edit #2 failed: old_string not found in <path>.\n *\n * <edit-outcomes>\n * #1 applied\n * #2 failed: old_string not found in <path>\n * #3 applied\n * </edit-outcomes>\n *\n * The annotation is omitted entirely on the all-applied path — the\n * renderer's `isEditErrorResult` check then suppresses the paired\n * `tool-result` so the diff stands alone (existing behavior).\n */\n\ninterface EditStep {\n old_string: string\n new_string: string\n replace_all?: boolean\n}\n\ninterface StepOutcome {\n kind: 'applied' | 'failed'\n reason?: string\n}\n\n/**\n * Inline annotation builder — kept local to avoid importing from\n * `chat/edit-approval.ts` (that's a renderer-side module; tools live\n * one layer below). Line shape matches `parseEditOutcomesFromResult`'s\n * regex so the round-trip is lossless.\n *\n * Newlines in `reason` are folded to spaces because the parser is line-\n * scoped (`body.split('\\n')`); a multi-line reason would split into a\n * \"trailing prose\" line and trip the malformed-block guard, losing every\n * outcome below it. Static reasons in this file are single-line; the\n * sanitize is a guard against a pathological `target` (file path\n * containing a newline) leaking into `old_string not found in <target>`.\n */\nfunction annotationFor(outcomes: readonly StepOutcome[]): string {\n const lines = ['<edit-outcomes>']\n for (let i = 0; i < outcomes.length; i++) {\n const o = outcomes[i]\n const reason = o.reason ? `: ${o.reason.replace(/\\r?\\n/g, ' ')}` : ''\n lines.push(`#${i + 1} ${o.kind}${reason}`)\n }\n lines.push('</edit-outcomes>')\n return lines.join('\\n')\n}\n\nexport const multiEdit: ToolDef = {\n spec: {\n name: 'multi_edit',\n description: 'Apply a sequential list of edits to a file. Each edit operates on the result of the previous APPLIED edit. Prefer this over multiple `edit` calls when several non-overlapping changes are needed in the same file. Edits run **best-effort**: a per-step failure (`old_string` not found, ambiguous match without `replace_all`, identical strings) is reported in the result but does NOT block the remaining steps. The file is written iff at least one step applied. The result lists per-hunk outcomes (`applied` / `failed`) so the model can re-issue just the failures without resending the whole batch. Each step tolerates `read_file` line-number prefixes (`<N>\\\\t…`, `<N>|…`, or `<N>→…`) in `old_string` / `new_string`.',\n inputSchema: {\n type: 'object',\n properties: {\n path: { type: 'string', description: 'Relative file path.' },\n edits: {\n type: 'array',\n description: 'List of edits applied in order; each operates on the previous applied edit\\'s output.',\n items: {\n type: 'object',\n properties: {\n old_string: { type: 'string' },\n new_string: { type: 'string' },\n replace_all: { type: 'boolean' },\n },\n required: ['old_string', 'new_string'],\n },\n },\n },\n required: ['path', 'edits'],\n },\n },\n async execute({ path, edits }, ctx: ToolContext) {\n const target = path as string\n const steps = edits as EditStep[]\n\n if (!Array.isArray(steps) || steps.length === 0)\n return `multi_edit error: edits must be a non-empty array.`\n\n let current: string\n try {\n current = await ctx.execution.readFile(ctx.handle, target)\n }\n catch {\n const hint = await suggestionFor(ctx.execution, ctx.handle, target)\n return `multi_edit error: file not found: ${target}.${hint}`\n }\n\n // Read-before-edit guard — see the matching block in `src/tools/\n // edit.ts` for the rationale and the no-read-state-map fall-through.\n if (ctx.behavior?.requireReadBeforeEdit) {\n const readState = resolveReadStateMap(ctx)\n if (readState) {\n const absKey = readStateKey(ctx.handle.cwd, target)\n const prior = readState.get(absKey)\n if (!prior)\n return `multi_edit error: ${target} has not been read in this session. Call read_file first so the edits apply against the current contents.`\n if (prior.contentHash !== hashContent(current))\n return `multi_edit error: ${target} has changed on disk since the last read. Re-read the file before editing.`\n }\n }\n\n const outcomes: StepOutcome[] = []\n let totalReplacements = 0\n\n for (let i = 0; i < steps.length; i++) {\n const step = steps[i]\n const find = step.old_string\n const replacement = step.new_string\n const replaceAll = step.replace_all === true\n\n if (typeof find !== 'string' || typeof replacement !== 'string') {\n outcomes.push({ kind: 'failed', reason: 'missing old_string or new_string' })\n continue\n }\n\n if (find.length === 0) {\n outcomes.push({ kind: 'failed', reason: 'empty old_string (use write_file to fully replace a file)' })\n continue\n }\n\n if (find === replacement) {\n outcomes.push({ kind: 'failed', reason: 'old_string and new_string are identical' })\n continue\n }\n\n const match = resolveOldString(current, find)\n if (!match) {\n outcomes.push({ kind: 'failed', reason: `old_string not found in ${target}` })\n continue\n }\n\n const { actual, occurrences, via } = match\n if (occurrences > 1 && !replaceAll) {\n outcomes.push({\n kind: 'failed',\n reason: `old_string appears ${occurrences} times — pass replace_all=true on this edit or expand old_string for uniqueness`,\n })\n continue\n }\n\n const styledReplacement = styleReplacementForVia(replacement, via, actual)\n current = replaceAll\n ? current.split(actual).join(styledReplacement)\n : current.replace(actual, styledReplacement)\n totalReplacements += occurrences\n outcomes.push({ kind: 'applied' })\n }\n\n const appliedCount = outcomes.reduce((n, o) => o.kind === 'applied' ? n + 1 : n, 0)\n const failedCount = outcomes.length - appliedCount\n\n // Skip the write when nothing changed. Preserves mtime so a partner\n // process watching the file doesn't see a phantom touch on a fully-\n // failed batch.\n if (appliedCount > 0) {\n await ctx.execution.writeFile(ctx.handle, target, current)\n\n // Bring the tracked hash forward so a follow-up edit on the same\n // file doesn't trip the drift branch on freshly-rewritten bytes.\n const readState = resolveReadStateMap(ctx)\n if (readState) {\n const absKey = readStateKey(ctx.handle.cwd, target)\n const prior = readState.get(absKey)\n if (prior)\n readState.set(absKey, { ...prior, contentHash: hashContent(current), mtimeMs: Date.now() })\n }\n }\n\n const n = steps.length\n\n // Header shape — three branches, each chosen for renderer + model\n // legibility:\n //\n // - All applied → legacy \"Edited X: applied N edits (R replacements).\"\n // so the renderer's `isEditErrorResult` keeps suppressing the\n // paired tool-result (the diff stands alone on a clean success).\n // - Mixed → \"Edited X: applied N of M edits (R replacements).\"\n // plus per-failure lines + `<edit-outcomes>` block.\n // - All failed → \"multi_edit error: no edits applied to X (M attempted).\"\n // keeps the legacy \"multi_edit error:\" prefix so anything that\n // pattern-matched on it (renderer visibility branch, status\n // ribbons, log filters) keeps working.\n let header: string\n if (appliedCount === n) {\n header = `Edited ${target}: applied ${n} edit${n === 1 ? '' : 's'} (${totalReplacements} replacement${totalReplacements === 1 ? '' : 's'}).`\n }\n else if (appliedCount > 0) {\n header = `Edited ${target}: applied ${appliedCount} of ${n} edits (${totalReplacements} replacement${totalReplacements === 1 ? '' : 's'}).`\n }\n else {\n header = `multi_edit error: no edits applied to ${target} (${n} attempted).`\n }\n\n const failureLines: string[] = []\n for (let i = 0; i < outcomes.length; i++) {\n const o = outcomes[i]\n if (o.kind === 'failed')\n failureLines.push(`edit #${i + 1} failed: ${o.reason}`)\n }\n\n // Annotation is the body's side-channel for replay + chat-layer\n // merge. Omit on the all-applied happy path so the renderer can\n // continue to hide the paired tool-result; emit whenever any step\n // failed so the chat layer + replay can paint per-hunk badges.\n const parts = [header]\n if (failureLines.length > 0)\n parts.push(failureLines.join('\\n'))\n if (failedCount > 0)\n parts.push(annotationFor(outcomes))\n return parts.join('\\n\\n')\n },\n}\n","/**\n * Binary-aware file read for the `read_file` tool.\n *\n * Dispatches to `ExecutionContext.readFileBinary` when implemented; otherwise\n * shells out via `base64 < path` so docker / sandbox contexts don't have to\n * implement a custom primitive. The shell fallback is portable (busybox /\n * coreutils / macOS all ship `base64`) and only fires when the native path\n * is unavailable.\n *\n * Returns a base64 string ready to drop into a `ToolResultContent` image\n * block. Raises on read failure — caller is responsible for catching and\n * formatting the error message.\n */\n\nimport type { ExecutionContext, ExecutionHandle } from '../contexts'\nimport { Buffer } from 'node:buffer'\nimport { alwaysQuote } from './shell-quote'\n\n/**\n * Best-effort guess at IANA media type from a file extension. Covers the\n * extensions Zidane's `read_file` actually dispatches to image blocks\n * (png/jpg/jpeg/gif/webp). Other extensions return `undefined` and the\n * caller short-circuits the binary route.\n */\nexport function imageMediaTypeFor(path: string): string | undefined {\n const dot = path.lastIndexOf('.')\n if (dot === -1)\n return undefined\n const ext = path.slice(dot + 1).toLowerCase()\n switch (ext) {\n case 'png':\n return 'image/png'\n case 'jpg':\n case 'jpeg':\n return 'image/jpeg'\n case 'gif':\n return 'image/gif'\n case 'webp':\n return 'image/webp'\n default:\n return undefined\n }\n}\n\n/**\n * Read a file as base64. Prefers `ExecutionContext.readFileBinary` (zero\n * subprocess overhead in-process) and falls back to `base64 < path` via\n * the shell seam — works on docker / sandbox without an interface change.\n *\n * Returns `{ base64, byteLength }`. `byteLength` is the *decoded* byte count\n * so callers can size-budget against the original file, not the inflated\n * base64 representation (which is ~4/3× larger).\n */\nexport async function readFileAsBase64(\n execution: ExecutionContext,\n handle: ExecutionHandle,\n path: string,\n): Promise<{ base64: string, byteLength: number }> {\n if (execution.readFileBinary) {\n const bytes = await execution.readFileBinary(handle, path)\n const b64 = Buffer.from(bytes.buffer, bytes.byteOffset, bytes.byteLength).toString('base64')\n return { base64: b64, byteLength: bytes.byteLength }\n }\n\n // Shell fallback. Use `base64 -i` on macOS-style implementations; the\n // GNU/coreutils form just takes the path positionally. Try both.\n // `2>/dev/null` swallows the macOS warning when the GNU form is used on\n // a system without `-i`.\n const cmd = `base64 < ${alwaysQuote(path)}`\n const result = await execution.exec(handle, cmd)\n if (result.exitCode !== 0)\n throw new Error(`base64 read failed: ${result.stderr || `exit ${result.exitCode}`}`)\n // `base64` may emit line-wrapped output (76-col default); strip whitespace.\n const b64 = result.stdout.replace(/\\s+/g, '')\n return { base64: b64, byteLength: decodedBase64ByteLength(b64) }\n}\n\n/**\n * Decoded byte length of a (whitespace-stripped) base64 string. Accounts for\n * `=` padding so the value matches the original file size to the byte —\n * `Math.floor(len * 3 / 4)` over-reports by 1–2 bytes on padded payloads.\n */\nfunction decodedBase64ByteLength(b64: string): number {\n if (b64.length === 0)\n return 0\n let pad = 0\n if (b64.endsWith('=='))\n pad = 2\n else if (b64.endsWith('='))\n pad = 1\n return Math.max(0, (b64.length * 3) / 4 - pad)\n}\n","import type { ToolResultContent } from '../types'\nimport type { ToolContext, ToolDef } from './types'\nimport { Buffer } from 'node:buffer'\nimport { errorMessage } from '../errors'\nimport { looksBinary } from './binary-detect'\nimport { imageMediaTypeFor, readFileAsBase64 } from './binary-read'\nimport { suggestionFor } from './path-suggest'\nimport { hashContent, readStateKey, resolveReadStateMap } from './read-state'\n\n/**\n * Read a file with line-based offset/limit and a hard byte cap.\n *\n * Defaults are tuned for source code: 2000 lines / 256 KiB. A typical source\n * file, lockfile, or large config fits in one read; logs and very large\n * fixtures get truncated with a footer that documents how to fetch the\n * remainder.\n *\n * Binary files are detected on the leading bytes — if the buffer contains a\n * NUL or has an unreasonable proportion of non-printable bytes, we skip text\n * decoding and return a marker so the model doesn't drown in mojibake.\n */\n\nconst DEFAULT_LINE_LIMIT = 2000\nconst DEFAULT_BYTE_CAP = 262_144\n\n/**\n * Hard upper bound on raw image bytes we'll inline as a base64 image block.\n * Above this, we return a marker instead — the model won't get useful\n * information from a 10 MB+ screenshot rendered as one tool result, and\n * the wire bill gets ugly. Override via the `maxBytes` parameter on the\n * tool call.\n */\nconst DEFAULT_IMAGE_BYTE_CAP = 5 * 1024 * 1024\n\nexport const readFile: ToolDef = {\n // Pure read — fans out in parallel with other concurrency-safe siblings\n // up to `behavior.maxConcurrentTools`.\n isConcurrencySafe: true,\n spec: {\n name: 'read_file',\n description: 'Read a file by path. Returns lines [offset..offset+limit). Default offset=1, limit=2000. Each line is prefixed with its 1-indexed line number followed by a tab (e.g. `42\\\\tconst foo = bar`); the prefix is metadata, not part of the file. Mirrors Claude Code\\'s `cat -n`-style compact output for token efficiency. A trailing footer explains how to read the rest when truncated. Binary files return a short marker rather than mojibake.',\n inputSchema: {\n type: 'object',\n properties: {\n path: { type: 'string', description: 'Relative file path.' },\n offset: { type: 'integer', description: '1-indexed line number to start from. Default: 1.' },\n limit: { type: 'integer', description: 'Max lines to return. Default: 2000. Set 0 for unlimited.' },\n maxBytes: { type: 'integer', description: 'Hard byte cap on file content read, regardless of line count. Default: 262144. Set 0 for unlimited. The rendered output may be slightly larger than this cap when `lineNumbers` is on (each line carries a `<N>\\\\t` prefix).' },\n lineNumbers: { type: 'boolean', description: 'Prefix each line with its 1-indexed line number. Default: true. Override the agent-wide `behavior.readLineNumbers` for this call.' },\n },\n required: ['path'],\n },\n },\n async execute({ path, offset, limit, maxBytes, lineNumbers }, ctx: ToolContext) {\n // Image dispatch — vision-aware models can answer questions directly\n // against the image bytes; non-vision models get the marker substitution\n // applied later in the loop (`stripImagesForNonVision`). Skips the line-\n // based read entirely. Gated on extension only — content-sniffing would\n // double the IO for a marginal accuracy win.\n const imgMedia = imageMediaTypeFor(path as string)\n if (imgMedia) {\n const sizeCap = maxBytes !== undefined ? normalizeInteger(maxBytes, DEFAULT_IMAGE_BYTE_CAP) : DEFAULT_IMAGE_BYTE_CAP\n try {\n const { base64, byteLength } = await readFileAsBase64(ctx.execution, ctx.handle, path as string)\n if (sizeCap > 0 && byteLength > sizeCap) {\n return `[image too large to inline: ${path}, ${byteLength} bytes (cap ${sizeCap}). Raise maxBytes, or use shell to inspect.]`\n }\n const content: ToolResultContent[] = [\n { type: 'text', text: `Image: ${path} (${byteLength} bytes, ${imgMedia})` },\n { type: 'image', mediaType: imgMedia, data: base64 },\n ]\n return content\n }\n catch (err) {\n const hint = await suggestionFor(ctx.execution, ctx.handle, path as string)\n return `Image read failed: ${path} — ${errorMessage(err)}.${hint}`\n }\n }\n\n let raw: string\n try {\n raw = await ctx.execution.readFile(ctx.handle, path as string)\n }\n catch {\n const hint = await suggestionFor(ctx.execution, ctx.handle, path as string)\n return `File not found: ${path}.${hint}`\n }\n\n const totalBytes = Buffer.byteLength(raw)\n\n // Read-state tracking + per-session dedup. Tracking populates the\n // map so `requireReadBeforeEdit` can confirm the model has seen\n // the file; dedup decides whether identical re-reads should\n // short-circuit to a \"still current\" stub. The two are\n // INDEPENDENT — turning off dedup must NOT also break the gate.\n //\n // Tracking runs whenever either feature is enabled (or by default,\n // since dedup defaults on); dedup short-circuit only fires when\n // `dedupReads` is explicitly on. Costs nothing when no session is\n // bound (direct/standalone tool invocations).\n //\n // The hash is taken on the *raw* file before any truncation so a\n // future read with a wider slice still invalidates the prior\n // stub-eligible entry — re-emitting the freshly-included bytes.\n const dedupEnabled = ctx.behavior?.dedupReads !== false\n const gateEnabled = ctx.behavior?.requireReadBeforeEdit === true\n const trackingEnabled = dedupEnabled || gateEnabled\n const readState = trackingEnabled ? resolveReadStateMap(ctx) : undefined\n const absKey = readStateKey(ctx.handle.cwd, path as string)\n const offsetForKey = normalizeInteger(offset, 1)\n const limitForKey = normalizeInteger(limit, DEFAULT_LINE_LIMIT)\n const maxBytesForKey = normalizeInteger(maxBytes, DEFAULT_BYTE_CAP)\n const showLineNumbers = typeof lineNumbers === 'boolean'\n ? lineNumbers\n : (ctx.behavior?.readLineNumbers ?? true)\n const currentHash = readState ? hashContent(raw) : ''\n if (dedupEnabled && readState) {\n const prior = readState.get(absKey)\n if (\n prior\n && prior.contentHash === currentHash\n && prior.offset === offsetForKey\n && prior.limit === limitForKey\n && prior.maxBytes === maxBytesForKey\n // Toggling `lineNumbers` between reads must miss dedup: the stub\n // claims \"the prior result is still current\", but the prior\n // output was a different shape — the model would draw bogus\n // conclusions if we replayed it.\n && prior.lineNumbers === showLineNumbers\n ) {\n return `File ${path} unchanged since the previous read in this session — the prior result is still current.`\n }\n }\n\n // Cheap binary detection on a leading sample. ExecutionContext.readFile\n // returns UTF-8-decoded text already — but if the underlying file was\n // binary, the decode replaces invalid sequences with U+FFFD. Use a NUL\n // byte and replacement-char ratio in the sample as a heuristic.\n if (looksBinary(raw)) {\n return `[binary file: ${path}, ${totalBytes} bytes; use shell with hexdump | xxd | od to inspect]`\n }\n\n const offsetN = offsetForKey\n const limitN = limitForKey\n const maxBytesN = maxBytesForKey\n\n const lines = raw.split('\\n')\n const totalLines = lines.length\n\n // 1-indexed offset → 0-indexed slice start.\n const startIdx = Math.max(0, offsetN - 1)\n const endIdx = limitN > 0 ? Math.min(totalLines, startIdx + limitN) : totalLines\n let slice = lines.slice(startIdx, endIdx)\n\n // Apply byte cap at the line boundary. Walk lines, accumulating bytes; cut\n // just below the cap. The \"truncatedSlice.length > 0\" guard ensures we\n // always emit at least one line — files with a single oversized line would\n // otherwise return empty. The single-line overflow gets handled by the\n // hard-cap pass below.\n let bytesCut = false\n if (maxBytesN > 0) {\n const truncatedSlice: string[] = []\n let bytesUsed = 0\n for (const line of slice) {\n const lineBytes = Buffer.byteLength(line) + 1 // +1 for the '\\n' rejoin\n if (bytesUsed + lineBytes > maxBytesN && truncatedSlice.length > 0) {\n bytesCut = true\n break\n }\n truncatedSlice.push(line)\n bytesUsed += lineBytes\n if (bytesUsed >= maxBytesN) {\n // Budget consumed — keep this line, stop here.\n break\n }\n }\n if (truncatedSlice.length < slice.length)\n bytesCut = true\n slice = truncatedSlice\n }\n\n // Hard-cap pass: when the line-boundary loop had to admit an oversized line\n // (single huge line, or first line ≥ maxBytes), the body still exceeds the\n // budget. Truncate the last line at a UTF-8-safe boundary and flag it as a\n // mid-line cut so the footer can warn the reader that offset+1 won't pick\n // up where this read left off.\n let midLineCut = false\n if (maxBytesN > 0 && slice.length > 0) {\n const bodyBytes = Buffer.byteLength(slice.join('\\n'))\n if (bodyBytes > maxBytesN) {\n const lastIdx = slice.length - 1\n const lastLine = slice[lastIdx]\n const otherBytes = lastIdx > 0\n ? Buffer.byteLength(slice.slice(0, lastIdx).join('\\n')) + 1 // +1 for the joining '\\n'\n : 0\n const budgetForLast = Math.max(0, maxBytesN - otherBytes)\n // Estimate cut at char count = byte budget (correct for ASCII), then\n // walk back if the resulting prefix still overflows because of multi-\n // byte codepoints. Worst case ~3 iterations (max UTF-8 width is 4).\n let cut = Math.min(lastLine.length, budgetForLast)\n while (cut > 0 && Buffer.byteLength(lastLine.slice(0, cut)) > budgetForLast)\n cut--\n slice[lastIdx] = lastLine.slice(0, cut)\n midLineCut = true\n bytesCut = true\n }\n }\n\n const linesReturned = slice.length\n const lastLineRead = startIdx + linesReturned\n\n // Line numbers default on. Format: `<line-no>\\t<content>` — bare digit,\n // tab separator, no padding. Matches Claude Code's compact `cat -n`\n // output for token efficiency (~5 chars saved per line vs. padded\n // forms). Models treat the prefix as metadata; `edit` strips it from\n // `old_string` when the model pastes a numbered chunk back, and also\n // accepts `<N>|<content>` / `<N>→<content>` for cross-stack tolerance.\n const body = showLineNumbers\n ? slice.map((line, i) => `${startIdx + i + 1}\\t${line}`).join('\\n')\n : slice.join('\\n')\n\n if (readState) {\n readState.set(absKey, {\n contentHash: currentHash,\n offset: offsetN,\n limit: limitN,\n maxBytes: maxBytesN,\n lineNumbers: showLineNumbers,\n mtimeMs: Date.now(),\n })\n }\n\n const linesTruncated = endIdx < totalLines || bytesCut\n if (!linesTruncated && offsetN === 1)\n return body\n\n if (!linesTruncated) {\n // Reading from a non-1 offset; tell the model where the read started.\n return `${body}\\n\\n…read lines ${offsetN}-${lastLineRead} of ${totalLines}.`\n }\n\n if (midLineCut) {\n // Line N was only partially read, so offset=N+1 would skip the unread\n // portion — we don't suggest it. Raising maxBytes is the only lossless\n // way forward. Avoid suggesting shell+sed/awk slicing here: that primes\n // a debugging anti-pattern where the model loops on narrow shell probes\n // instead of using paginated read_file calls.\n return `${body}\\n\\n…truncated mid-line at line ${lastLineRead} (byte cap ${maxBytesN} reached). File has ${totalLines} lines, ${totalBytes} bytes total. Raise maxBytes to read the full line.`\n }\n\n const reason = bytesCut ? `byte cap (${maxBytesN}) reached` : `line limit (${limitN}) reached`\n return `${body}\\n\\n…truncated at line ${lastLineRead} (${reason}). File has ${totalLines} lines, ${totalBytes} bytes total — re-read with offset=${lastLineRead + 1} to continue.`\n },\n}\n\nfunction normalizeInteger(value: unknown, fallback: number): number {\n if (typeof value !== 'number' || !Number.isFinite(value))\n return fallback\n // Validation auto-coerces strings to numbers, so by the time we get here\n // a non-finite value means the caller passed something garbled.\n if (value < 0)\n return fallback\n return Math.floor(value)\n}\n","/**\n * `shell_kill` — terminate a running background task by id.\n *\n * Companion to the `shell` tool's `run_in_background: true` mode. The\n * model receives a `task_id` when it spawns a background task; this\n * tool routes a kill request through `ExecutionContext.killBackground`,\n * which SIGTERMs the whole process group (the foreground `exec`\n * implementation already documents the kill-tree rationale).\n *\n * The successful kill ALSO suppresses the would-be `<task-notification>`\n * on the next turn — the agent's `tool:after` listener (see\n * `src/agent.ts`'s notification-suppression block) deletes the pending\n * entry by `task_id`. Without that, the model would get the kill result\n * inline AND a redundant notification on its next prompt.\n */\n\nimport type { ToolContext, ToolDef } from './types'\nimport { formatDuration, formatTaskStatus, previewLine } from '../chat/format'\n\nexport const shellKill: ToolDef = {\n // Concurrency-safe — pure SIGTERM dispatch, no shared mutable state\n // beyond the per-context registry which is atomic.\n isConcurrencySafe: true,\n spec: {\n name: 'shell_kill',\n description: [\n 'Terminate a running background task started by `shell({ run_in_background: true })`.',\n 'Sends SIGTERM to the whole process group so the shell wrapper AND its child commands die together.',\n 'Returns the final exit info (status, exit code, output path) or a \"no such task\" message when the id is unknown / already cleaned up.',\n 'Idempotent — calling on a task that has already terminated returns the cached exit info without re-killing.',\n ].join('\\n'),\n inputSchema: {\n type: 'object',\n properties: {\n task_id: { type: 'string', description: 'The task id returned by a prior `shell({ run_in_background: true })` call.' },\n },\n required: ['task_id'],\n additionalProperties: false,\n },\n },\n async execute(input, ctx: ToolContext): Promise<string> {\n const taskId = input.task_id as string\n\n if (!ctx.execution.killBackground) {\n return `shell_kill error: the active execution context (${ctx.execution.type}) does not support background tasks.`\n }\n\n const info = await ctx.execution.killBackground(ctx.handle, taskId)\n if (!info) {\n return `shell_kill: no such task \"${taskId}\". It may have already exited and been cleaned up, or it was never started in this session.`\n }\n\n return [\n `Killed ${info.taskId} — ${formatTaskStatus(info)} after ${formatDuration(info.durationMs)}.`,\n ` command: ${previewLine(info.command, 60)}`,\n ` output: ${info.outputPath}`,\n ].join('\\n')\n },\n}\n","/**\n * Spawn tool — create sub-agents from a parent agent.\n *\n * A configurable factory that reads the parent's preset-y fields from ToolContext.\n *\n * Usage:\n * ```ts\n * import { createSpawnTool } from 'zidane'\n * import { definePreset, basicTools } from 'zidane/presets'\n *\n * const preset = definePreset({\n * name: 'orchestrator',\n * tools: { ...basicTools, spawn: createSpawnTool({ maxConcurrent: 5 }) },\n * })\n * ```\n *\n * Each `createSpawnTool()` call returns a fresh instance with its own\n * concurrency counter, depth cap, and child-stats accumulator — never\n * share an instance across unrelated parent agents.\n *\n * Key guarantees:\n * - **Depth-capped** to `maxDepth` (default 3) to prevent infinite recursion.\n * - **Concurrency slot is reserved synchronously** before any `await`, so a\n * parent running tools in parallel cannot exceed `maxConcurrent`.\n * - **Pre-aborted signals short-circuit** without paying agent-spawn cost.\n * - **Abort / timeout / error are surfaced distinctly** in the returned text\n * and via `ChildRunStats.status` on `spawn:complete`.\n * - **`agent.destroy()` errors never mask the original run error** (captured\n * and emitted via `spawn:error` but the primary error wins).\n * - **Child hooks bubble** to the parent as `child:*` events when\n * `forwardHooks` is set (default `true`).\n * - **Persistence** via `persist: true` — child runs get appended to the\n * parent's session with `parentRunId` wired, so the run tree is\n * reconstructible from a stored `SessionData`.\n */\n\nimport type { Hookable } from 'hookable'\nimport type { AgentHooks } from '../agent'\nimport type { Preset } from '../presets'\nimport type { Session } from '../session'\nimport type { AgentStats, ChildRunStats } from '../types'\nimport type { ToolContext, ToolDef } from './types'\nimport { createAgent } from '../agent'\nimport { flattenTurns, formatTokenUsage } from '../stats'\nimport { resolveReadStateMap } from './read-state'\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport interface ChildAgent {\n id: string\n task: string\n startedAt: number\n /** Subagent depth — 1 for a direct child of a top-level agent. */\n depth: number\n}\n\nexport interface SpawnToolState {\n /** Currently running children. */\n readonly children: ReadonlyMap<string, ChildAgent>\n /**\n * Cumulative stats across every completed direct child of this spawn-tool\n * instance (returns a copy). Each child's contribution is the cumulative\n * `AgentStats` returned by its `agent.run()` — so\n * `totalIn`/`totalOut`/`totalCacheRead`/`totalCacheCreation` cover the\n * entire subtree (children + grandchildren + …), while `turns` and\n * `elapsed` stay parent-loop-only per child and are summed across direct\n * children. `elapsed` over-counts when children ran in parallel.\n *\n * Lives across multiple parent runs that share this instance.\n */\n readonly totalChildStats: Readonly<AgentStats>\n}\n\n// ---------------------------------------------------------------------------\n// Events to bubble from child → parent. Keep these lists small: the goal is\n// to give the parent a usable live-tail (and a place to gate child calls)\n// without drowning it in telemetry.\n//\n// Two kinds:\n// - {@link BUBBLED_EVENTS} — observational. Forwarded as a spread copy so\n// the parent's listeners can't accidentally mutate the child's ctx.\n// - {@link BUBBLED_GATE_EVENTS} — gates. Forwarded with the SAME ctx\n// reference so parent listeners can set `block` / `reason` / `result`\n// and have those mutations land on the gate the child's loop is\n// awaiting on.\n// ---------------------------------------------------------------------------\n\nconst BUBBLED_EVENTS = [\n 'stream:text',\n 'stream:thinking',\n 'stream:end',\n 'stream:error',\n 'tool:dispatched',\n 'tool:before',\n 'tool:after',\n 'tool:error',\n 'tool:cancelled',\n 'background:start',\n 'background:exit',\n 'background:reassign',\n 'turn:after',\n] as const\n\n// Same-ref bubble — parent listener mutations propagate back into the\n// child's loop. Gate hooks (`block` / `result`) and `tool:transform`\n// (`result` rewrite) need this so a parent's per-edit annotation /\n// approval lands on the child's wire-level tool result.\nconst BUBBLED_MUTABLE_EVENTS = [\n 'tool:gate',\n 'mcp:tool:gate',\n 'tool:transform',\n] as const\n\ntype BubbledEvent = typeof BUBBLED_EVENTS[number]\ntype BubbledMutableEvent = typeof BUBBLED_MUTABLE_EVENTS[number]\n\n// Mapping from a bubbled event to its corresponding `child:*` event. Kept as\n// an explicit table (rather than a computed `child:${evt}` template literal)\n// so AgentHooks can type each entry independently.\nconst CHILD_EVENT_NAME: Record<BubbledEvent, keyof AgentHooks> = {\n 'stream:text': 'child:stream:text',\n 'stream:thinking': 'child:stream:thinking',\n 'stream:end': 'child:stream:end',\n 'stream:error': 'child:stream:error',\n 'tool:dispatched': 'child:tool:dispatched',\n 'tool:before': 'child:tool:before',\n 'tool:after': 'child:tool:after',\n 'tool:error': 'child:tool:error',\n 'tool:cancelled': 'child:tool:cancelled',\n 'background:start': 'child:background:start',\n 'background:exit': 'child:background:exit',\n 'background:reassign': 'child:background:reassign',\n 'turn:after': 'child:turn:after',\n}\n\nconst CHILD_MUTABLE_EVENT_NAME: Record<BubbledMutableEvent, keyof AgentHooks> = {\n 'tool:gate': 'child:tool:gate',\n 'mcp:tool:gate': 'child:mcp:tool:gate',\n 'tool:transform': 'child:tool:transform',\n}\n\n// ---------------------------------------------------------------------------\n// Session-scoped child label counter\n//\n// The `child-N` label that surfaces in transcripts (and in the spawn tool's\n// return string) is allocated per-session — *seeded* from the persisted\n// run tree — rather than from a closure-local counter on the spawn-tool\n// instance. The reason: closure counters don't survive process restarts,\n// so a fresh CLI launch that reopens a session with two prior children\n// (`child-1`, `child-2`) would start its next spawn at `child-1` and\n// collide with the persisted labels. Anchoring on `session.runs.filter(\n// depth > 0).length` makes the live counter pick up exactly where the\n// reloaded transcript's labels left off — matching `eventsFromTurns`'s\n// chronological numbering on the read side.\n//\n// The WeakMap caches the counter for the lifetime of the `Session` object\n// after the first call so the read of `session.runs.length` only happens\n// once per session, even though spawns mutate `session.runs` mid-run.\n//\n// Sessionless agents (no `ctx.session`) fall back to `localCounter` — the\n// closure is the best we can do without a shared anchor.\n// ---------------------------------------------------------------------------\n\nconst sessionChildCounters = new WeakMap<Session, number>()\n\nfunction reserveChildLabel(session: Session): string {\n let counter = sessionChildCounters.get(session)\n if (counter === undefined)\n counter = session.runs.filter(r => (r.depth ?? 0) > 0).length\n counter += 1\n sessionChildCounters.set(session, counter)\n return `child-${counter}`\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nfunction extractText(message: unknown): string {\n if (!message || typeof message !== 'object')\n return ''\n\n const msg = message as Record<string, unknown>\n\n if (typeof msg.content === 'string')\n return msg.content\n\n if (Array.isArray(msg.content)) {\n return msg.content\n .filter((block): block is Record<string, unknown> => !!block && typeof block === 'object' && (block as Record<string, unknown>).type === 'text')\n .map(block => block.text)\n .join('\\n')\n }\n\n return ''\n}\n\n/**\n * Race `task` (an already-running child `agent.run()` promise) against a\n * timer. Does NOT race against the parent abort signal — the child agent\n * already observes the same signal internally and handles its own aborted\n * bookkeeping, so racing here would detach the spawn from the child's\n * session-persisting finally block.\n *\n * On timeout: rejects with `SpawnTimeoutError`; caller is expected to call\n * `agent.abort()` and subsequently `await` the original `task` so the\n * child's session state (runs, turns, status) gets flushed before the\n * parent moves on.\n */\nasync function raceWithTimeout<T>(\n task: Promise<T>,\n timeoutMs: number | undefined,\n): Promise<T> {\n if (!timeoutMs || timeoutMs <= 0)\n return task\n\n let timer: ReturnType<typeof setTimeout> | undefined\n try {\n return await new Promise<T>((resolve, reject) => {\n timer = setTimeout(() => reject(new SpawnTimeoutError(timeoutMs)), timeoutMs)\n task.then(resolve, reject)\n })\n }\n finally {\n if (timer)\n clearTimeout(timer)\n }\n}\n\nclass SpawnTimeoutError extends Error {\n readonly timeoutMs: number\n constructor(timeoutMs: number) {\n super(`Child agent timed out after ${timeoutMs}ms`)\n this.name = 'SpawnTimeoutError'\n this.timeoutMs = timeoutMs\n }\n}\n\n/**\n * Wire child's hooks to bubble into `parentHooks` as `child:*` events.\n *\n * Three kinds of forwarding:\n *\n * 1. **Originating observational events** (`stream:text`, `tool:before`, …)\n * → rewrite to the matching `child:*` event, inject `{ childId, depth }`,\n * fire on the parent's hook bus as a **spread copy** (so parent listeners\n * can't accidentally mutate the child's ctx).\n * 2. **Originating gate events** (`tool:gate`, `mcp:tool:gate`) → forward\n * the **same ctx reference**, augmented with `childId` / `depth`, so a\n * parent listener writing `ctx.block = true` lands on the gate the\n * child's loop is awaiting on. The bubble itself `await`s the parent's\n * callHook so any async approval (e.g. a TUI picker) completes before\n * the child's loop sees the decision.\n * 3. **Re-bubbled `child:*` events** from a grandchild already carry the\n * originating `childId` + `depth`. Forward verbatim so a top-level\n * listener sees true ancestry, not the immediate parent's.\n *\n * Returns a function that unregisters every listener registered here.\n * Called before `agent.run()` starts, torn down in a finally block — so\n * nothing leaks even if the child throws mid-run.\n */\n/**\n * Surface a thrown observational-bubble listener without killing the\n * process. Observational events (`child:stream:text`, `child:tool:after`,\n * `child:turn:after`, …) are fire-and-forget — a rejected promise here\n * has no caller. Without this handler, a buggy parent listener bubbles\n * up to an unhandled-rejection, and Node/Bun under\n * `--unhandled-rejections=strict` terminates the host.\n *\n * Gated on `ZIDANE_DEBUG` so production logs stay quiet; debug builds\n * still get the tagged line.\n */\nfunction swallowBubbleError(eventName: string, err: unknown): void {\n if (!process.env.ZIDANE_DEBUG)\n return\n const message = err instanceof Error ? (err.stack ?? err.message) : String(err)\n process.stderr.write(`[zidane/spawn] parent listener for \"${eventName}\" rejected: ${message}\\n`)\n}\n\nfunction bubbleHooks(\n childHooks: Hookable<AgentHooks>,\n parentHooks: Hookable<AgentHooks>,\n childId: string,\n depth: number,\n): () => void {\n const unregisters: Array<() => void> = []\n\n // Cast the callHook surface once — it accepts any (name, ctx) at runtime\n // but the typed overload is too narrow to satisfy across the map.\n const fire = parentHooks.callHook as unknown as (\n name: keyof AgentHooks,\n ctx: Record<string, unknown>,\n ) => Promise<unknown>\n\n // Observational events — fire-and-forget so a slow parent listener never\n // backpressures child emission (the child has already moved on by then).\n // `Promise.resolve(...).catch(swallowBubbleError)` instead of `void` so a\n // rejected listener surfaces as a tagged stderr line rather than an\n // unhandled rejection — which would terminate the process under\n // `--unhandled-rejections=strict`. `Promise.resolve` because hookable's\n // `callHook` returns `undefined` when no listeners are registered (rather\n // than a resolved promise), and we still want a uniform `.catch` surface.\n for (const evt of BUBBLED_EVENTS) {\n const parentEvt = CHILD_EVENT_NAME[evt]\n const unregister = childHooks.hook(evt, (ctx: object) => {\n Promise.resolve(fire(parentEvt, { ...(ctx as Record<string, unknown>), childId, depth }))\n .catch(err => swallowBubbleError(parentEvt, err))\n })\n unregisters.push(unregister)\n }\n\n // Same-ref bubble — `await` so the parent's decision lands before the\n // child's loop reads back the mutable slot (`ctx.block` / `ctx.result`\n // for gates, `ctx.result` for `tool:transform`). The bubble only adds\n // `childId` + `depth` for routing; the ctx reference itself is the\n // one the child's loop is awaiting on.\n const tagOnCtx = (ctx: Record<string, unknown>) => {\n ctx.childId = childId\n ctx.depth = depth\n }\n for (const evt of BUBBLED_MUTABLE_EVENTS) {\n const parentEvt = CHILD_MUTABLE_EVENT_NAME[evt]\n const unregister = childHooks.hook(evt, async (ctx: object) => {\n tagOnCtx(ctx as Record<string, unknown>)\n await fire(parentEvt, ctx as Record<string, unknown>)\n })\n unregisters.push(unregister)\n }\n\n // Chain bubbling: forward already-tagged `child:*` events from a grandchild\n // through to the parent, preserving the originating spawn's `childId` +\n // `depth`. Without this, only direct children would surface.\n const chainHook = childHooks.hook as unknown as (\n name: keyof AgentHooks,\n handler: (ctx: object) => void | Promise<void>,\n ) => () => void\n for (const evt of BUBBLED_EVENTS) {\n const parentEvt = CHILD_EVENT_NAME[evt]\n unregisters.push(chainHook(parentEvt, (ctx) => {\n Promise.resolve(fire(parentEvt, ctx as Record<string, unknown>))\n .catch(err => swallowBubbleError(parentEvt, err))\n }))\n }\n for (const evt of BUBBLED_MUTABLE_EVENTS) {\n const parentEvt = CHILD_MUTABLE_EVENT_NAME[evt]\n unregisters.push(chainHook(parentEvt, async (ctx) => {\n // Same-ref forwards need the await chain to remain serial up to\n // the top-level listener — otherwise the deepest child's loop\n // resumes before the root's decision / result mutation lands.\n await fire(parentEvt, ctx as Record<string, unknown>)\n }))\n }\n\n return () => {\n for (const u of unregisters) u()\n }\n}\n\n// ---------------------------------------------------------------------------\n// Factory\n// ---------------------------------------------------------------------------\n\nexport interface SpawnToolOptions {\n /** Maximum concurrent sub-agents (default: 3). */\n maxConcurrent?: number\n /**\n * Maximum subagent depth. 0 disables spawning entirely; 1 allows top-level\n * spawns but forbids grandchildren; 3 (default) allows three levels of\n * recursion — enough for most orchestration patterns, a sharp ceiling\n * against runaway loops.\n */\n maxDepth?: number\n /** Child model override. */\n model?: string\n /** Child system prompt override. Per-spawn `input.system` takes precedence. */\n system?: string\n /** Child thinking level. */\n thinking?: 'off' | 'minimal' | 'low' | 'medium' | 'high'\n /** Preset override for children. Shallow-merged over the parent's preset (parent fields still win for anything left unset). */\n preset?: Preset\n /**\n * Per-child timeout, in milliseconds. When the child exceeds it the spawn\n * tool returns a timeout marker, fires `spawn:error`, and destroys the\n * child agent. Default: none.\n */\n timeoutMs?: number\n /**\n * When `true` and the parent has a session, the child reuses the parent's\n * session — child turns are appended with the child's own `runId`, and the\n * resulting `SessionRun` carries `parentRunId` so the tree is\n * reconstructible. Default: `false` (child is in-memory only).\n *\n * **Read-state isolation.** Sharing the session also shares the\n * `read_file` / `requireReadBeforeEdit` tracking map (it's keyed\n * by `Session`). With `persist: false` the child gets no session,\n * so reads inside the subagent populate nothing the parent can see —\n * a follow-up `edit` / `multi_edit` in the parent will trip the\n * gate with `\"has not been read\"` even though the model just\n * read the file in the child. Use {@link shareReadState} when\n * you want the parent's gate to honor the child's reads WITHOUT\n * also persisting child turns to the parent's session.\n */\n persist?: boolean\n /**\n * Forward the parent's read-state map to the child agent so the\n * `requireReadBeforeEdit` gate and `dedupReads` cache see reads\n * across the parent/child boundary. Orthogonal to {@link persist} —\n * use this when you want shared read tracking without sharing the\n * session's turn history. Default: `false`.\n *\n * Has no effect when the parent has no read-state to share (no\n * session and no explicit `readState` on the parent agent's\n * options). Implementation: passes the parent's resolved\n * `ReadStateMap` to the child via `AgentOptions.readState`, which\n * tools resolve via `ctx.readState ?? getReadState(ctx.session)`.\n */\n shareReadState?: boolean\n /**\n * Forward a curated subset of child hook events (`stream:*`, `tool:*`,\n * `turn:after`) onto the parent's hook bus as `child:*` events. Default:\n * `true`. Grandchildren bubble through their child transparently.\n */\n forwardHooks?: boolean\n /** Called when a child agent starts. */\n onSpawn?: (child: ChildAgent) => void\n /** Called when a child agent completes (success, abort, timeout, or error). */\n onComplete?: (child: ChildAgent, stats: AgentStats, status: NonNullable<ChildRunStats['status']>) => void\n}\n\n/**\n * Create a configured spawn tool.\n *\n * State (`children`, `totalChildStats`, counters, active count) is scoped to\n * the returned instance. Multiple parent agents using the same instance will\n * share counters + stats + concurrency slots — call `createSpawnTool()` per\n * agent (or use the stateless default `spawn`) to keep them isolated.\n */\nexport function createSpawnTool(options: SpawnToolOptions = {}): ToolDef & SpawnToolState {\n const localChildren = new Map<string, ChildAgent>()\n let localCounter = 0\n let localActiveCount = 0\n const maxConcurrent = options.maxConcurrent ?? 3\n const maxDepth = options.maxDepth ?? 3\n const forwardHooks = options.forwardHooks ?? true\n\n const localStats: AgentStats = {\n totalIn: 0,\n totalOut: 0,\n totalCacheRead: 0,\n totalCacheCreation: 0,\n turns: 0,\n elapsed: 0,\n }\n\n return {\n get children() { return localChildren },\n get totalChildStats() { return { ...localStats } },\n\n // Sub-agents fan out by design — multiple spawn calls in a single\n // batch run in parallel (each child has its own context window,\n // its own retry budget, and its own session linkage). The internal\n // `maxConcurrent` here is the spawn-tool-level cap on simultaneous\n // children; `behavior.maxConcurrentTools` is the loop-level cap on\n // all concurrent tools regardless of kind.\n isConcurrencySafe: true,\n\n spec: {\n name: 'spawn',\n description: 'Spawn a sub-agent for a self-contained task that benefits from isolation (separate context window, separate retries) — for example, a deep research dive or a long codegen pass on a specific file. The sub-agent runs independently with its own tool access and returns its final response. Do NOT spawn for sequential steps you could do yourself.',\n inputSchema: {\n type: 'object',\n properties: {\n task: {\n type: 'string',\n description: 'The task prompt for the sub-agent. Be specific about what you want it to accomplish.',\n },\n system: {\n type: 'string',\n description: 'Optional system prompt override for this specific sub-agent.',\n },\n },\n required: ['task'],\n },\n },\n\n async execute(input: Record<string, unknown>, ctx: ToolContext): Promise<string> {\n const task = input.task as string\n const systemOverride = input.system as string | undefined\n const parentDepth = ctx.depth ?? 0\n const childDepth = parentDepth + 1\n\n // Reject before any await so the parent-level LLM sees the failure\n // immediately, and the concurrency slot below is reserved atomically\n // with the cap check.\n if (childDepth > maxDepth) {\n return `Cannot spawn: maxDepth=${maxDepth} reached (parent depth=${parentDepth}). Deepen the cap with createSpawnTool({ maxDepth }).`\n }\n\n if (localActiveCount >= maxConcurrent) {\n return `Cannot spawn: ${localActiveCount}/${maxConcurrent} sub-agents already running. Wait for one to complete.`\n }\n\n // Signal short-circuit: a pre-aborted parent means the child would\n // immediately no-op on its first `signal.aborted` check. Skip the\n // agent/handle/session setup cost entirely.\n if (ctx.signal.aborted) {\n return `[sub-agent pre-aborted] Parent signal was already aborted — skipped \"${task.slice(0, 80)}\"`\n }\n\n // Reserve the slot + id SYNCHRONOUSLY — this block must not contain\n // any `await`, so (a) two parallel spawns can't both pass the cap\n // check, and (b) the session-scoped child counter (see\n // `reserveChildLabel`) sees a consistent `session.runs` snapshot\n // between concurrent spawns sharing this session.\n const id = ctx.session\n ? reserveChildLabel(ctx.session)\n : `child-${++localCounter}`\n localActiveCount++\n const child: ChildAgent = { id, task, startedAt: Date.now(), depth: childDepth }\n localChildren.set(id, child)\n\n let destroyError: Error | undefined\n let childRunStatus: NonNullable<ChildRunStats['status']> = 'completed'\n let finalStats: AgentStats | undefined\n let result = ''\n\n // Bubble child hook events to parent if requested. Wired BEFORE\n // `agent.run()` so even the very first `turn:before` is observable.\n let unbubble: (() => void) | undefined\n\n try {\n const parentPreset: Preset = {\n ...(ctx.name !== undefined ? { name: ctx.name } : {}),\n ...(ctx.system !== undefined ? { system: ctx.system } : {}),\n tools: ctx.tools,\n ...(ctx.toolAliases !== undefined ? { toolAliases: ctx.toolAliases } : {}),\n ...(ctx.mcpServers !== undefined ? { mcpServers: ctx.mcpServers } : {}),\n ...(ctx.skills !== undefined ? { skills: ctx.skills } : {}),\n ...(ctx.behavior !== undefined ? { behavior: ctx.behavior } : {}),\n }\n // Resolve the parent's read-state map for cross-context\n // sharing. `shareReadState: true` forwards it to the child via\n // `AgentOptions.readState`, which tools resolve through\n // `resolveReadStateMap(ctx)`. Skips when the parent has\n // nothing to share (no session, no explicit map). Orthogonal\n // to `persist` — turn-history and read-state are independent.\n // Reading via `resolveReadStateMap` here ensures we forward\n // the same map a grandparent might already have forwarded\n // into us (chained `shareReadState` across the tree).\n const sharedReadState = options.shareReadState\n ? resolveReadStateMap(ctx)\n : undefined\n const agent = createAgent({\n ...parentPreset,\n ...options.preset,\n provider: ctx.provider,\n execution: ctx.execution,\n // Share the parent's session on opt-in. Child turns get appended to\n // the same session.turns stream with the child's runId; the child\n // run itself is tagged with parentRunId below, via AgentRunOptions.\n ...(options.persist && ctx.session ? { session: ctx.session } : {}),\n ...(sharedReadState ? { readState: sharedReadState } : {}),\n })\n\n if (forwardHooks) {\n // Enrich `tool:before` ctx with `priorContent` for the three\n // edit tools BEFORE bubbleHooks fires the parent's\n // `child:tool:before`. Hookable runs listeners serially in\n // registration order, so registering this enricher first\n // guarantees the bubble's ctx-spread captures the field.\n // Hosts that listen on `child:tool:before` (the TUI's\n // edit-diff feature) read `priorContent` to:\n // - render a true `old → new` diff for `write_file` instead\n // of an all-add view, and\n // - paint real-file line numbers in the gutter / compact\n // summary for all three edit tools (via the renderer's\n // `buildContextualDiff` path).\n //\n // Note: `tool:before` is awaited by the child's loop, so this\n // pre-read blocks the child's tool execution by the read\n // latency. Acceptable — edit calls are cheap relative to the\n // model round-trip, and the read is skipped entirely for\n // non-edit tools.\n const unregisterEnricher = agent.hooks.hook('tool:before', async (toolCtx) => {\n if (toolCtx.name !== 'write_file' && toolCtx.name !== 'edit' && toolCtx.name !== 'multi_edit')\n return\n if (!agent.handle)\n return\n const inputPath = toolCtx.input?.path\n if (typeof inputPath !== 'string')\n return\n try {\n toolCtx.priorContent = await agent.execution.readFile(agent.handle, inputPath)\n }\n catch {\n // File doesn't exist (fresh create for `write_file`, or\n // a model-side mistake for `edit` / `multi_edit`) — leave\n // priorContent unset; downstream renderers treat the\n // absence as \"no real-file positions available\" and fall\n // back to synthetic line numbers.\n }\n })\n const unbubbleInner = bubbleHooks(agent.hooks, ctx.hooks, id, childDepth)\n unbubble = () => {\n unregisterEnricher()\n unbubbleInner()\n }\n }\n\n options.onSpawn?.(child)\n // Mutable tracingContext carrier — empty by default; parent tracer\n // listeners on `spawn:before` write a W3C `traceparent` (plus\n // optional `tracestate`) into it. We forward whatever it contains\n // to the child's `agent.run()` so the child tracer can re-parent\n // its root span. Empty / absent for parents that don't run a\n // tracer (no-op, no key gets set, child sees `undefined`).\n const spawnHookCtx: { id: string, task: string, depth: number, tracingContext: Record<string, string> } = {\n id,\n task,\n depth: childDepth,\n tracingContext: {},\n }\n await ctx.hooks.callHook('spawn:before', spawnHookCtx)\n\n // `agent.run()` is started here and we hold onto its promise so we\n // can always `await` it before destroying. Even on a timeout we\n // re-await the run — that lets the child's internal `finally`\n // (session.abortRun, finalizeSession, hooks) flush its state to the\n // store before the parent hands the outcome back to the LLM.\n const propagatedTracing = Object.keys(spawnHookCtx.tracingContext).length > 0\n ? Object.freeze({ ...spawnHookCtx.tracingContext })\n : undefined\n const runPromise = agent.run({\n prompt: task,\n model: options.model,\n system: systemOverride ?? options.system,\n thinking: options.thinking,\n signal: ctx.signal,\n depth: childDepth,\n ...(options.persist && ctx.runId ? { parentRunId: ctx.runId } : {}),\n ...(propagatedTracing ? { tracingContext: propagatedTracing } : {}),\n })\n\n try {\n finalStats = await raceWithTimeout(runPromise, options.timeoutMs)\n\n // Tree-wide turn count keeps the model-facing line internally\n // consistent with the cumulative token numbers — otherwise a\n // 2-turn child that spawned a 50-turn grandchild would print\n // \"2 turns ... 5000 tokens\" and look broken. Equals\n // `finalStats.turns` when the child has no descendants of its own.\n const treeTurns = flattenTurns(finalStats).length\n // `formatTokenUsage` reports the effective input (including\n // cached reads / cache creations) with the cached portion broken\n // out — see the helper's docstring.\n const usage = formatTokenUsage(finalStats)\n if (ctx.signal.aborted) {\n childRunStatus = 'aborted'\n result = [\n `[sub-agent ${id}] Aborted after ${treeTurns} turns (${finalStats.elapsed}ms)`,\n `Tokens: ${usage}`,\n ].join('\\n')\n }\n else {\n const response = extractText(agent.turns.at(-1))\n result = [\n `[sub-agent ${id}] Completed in ${treeTurns} turns (${finalStats.elapsed}ms)`,\n `Tokens: ${usage}`,\n '',\n response || '(no text response)',\n ].join('\\n')\n }\n }\n catch (err) {\n if (err instanceof SpawnTimeoutError) {\n childRunStatus = 'timeout'\n agent.abort() // triggers child's internal abort path\n // Await the run so its finally block persists aborted state\n // before we move on. Swallow any error from the awaited promise\n // — we've already classified the outcome as 'timeout'.\n try {\n finalStats = await runPromise\n }\n catch {\n finalStats = {\n totalIn: 0,\n totalOut: 0,\n totalCacheRead: 0,\n totalCacheCreation: 0,\n turns: 0,\n elapsed: err.timeoutMs,\n }\n }\n result = `[sub-agent ${id}] Timed out after ${err.timeoutMs}ms`\n }\n else {\n const error = err instanceof Error ? err : new Error(String(err))\n childRunStatus = 'error'\n finalStats = {\n totalIn: 0,\n totalOut: 0,\n totalCacheRead: 0,\n totalCacheCreation: 0,\n turns: 0,\n elapsed: 0,\n }\n result = `[sub-agent ${id}] Error: ${error.message}`\n await ctx.hooks.callHook('spawn:error', { id, task, depth: childDepth, error })\n }\n }\n finally {\n // Promote the subagent's still-running background tasks up\n // to the parent's handle BEFORE destroying the child.\n // Without this, `agent.destroy()` would SIGTERM every task\n // the subagent started (per-handle scoping is what makes\n // sibling subagents isolated, but it also kills `&`-shell\n // semantics where backgrounded work outlives the spawning\n // process). With it, the user can `ctrl+k` a long-running\n // task the subagent started even after the subagent returns,\n // and `killBackgroundTask` from the parent agent actually\n // finds it.\n //\n // The reassignment also REPLACES the task's `onExit`\n // callback — the original closed over the child's hook bus,\n // which is about to be torn down. The new callback fires\n // `background:exit` on the PARENT's bus so the TUI's\n // existing listener drains the entry when the task\n // eventually terminates.\n //\n // Optional in the contract — sandboxes that don't expose\n // `reassignBackgroundTasks` fall through to the previous\n // behavior (destroy kills the subagent's tasks). Same for\n // a child agent that minted no execution handle (no runs\n // ever executed).\n const childHandle = agent.handle\n if (childHandle && ctx.execution.reassignBackgroundTasks) {\n try {\n const reassigned = await ctx.execution.reassignBackgroundTasks(\n childHandle,\n ctx.handle,\n (info) => {\n void ctx.hooks.callHook('background:exit', info)\n },\n )\n for (const entry of reassigned) {\n await ctx.hooks.callHook('background:reassign', {\n taskId: entry.taskId,\n fromHandleId: childHandle.id,\n toHandleId: ctx.handle.id,\n childId: id,\n pid: entry.pid,\n command: entry.command,\n outputPath: entry.outputPath,\n startedAt: entry.startedAt,\n })\n }\n }\n catch (err) {\n // Non-fatal — destroy will still kill the tasks. Log\n // under ZIDANE_DEBUG so a misconfigured sandbox surfaces\n // visibly. We don't promote this into `spawn:error`\n // because it doesn't affect the subagent's own outcome.\n if (process.env.ZIDANE_DEBUG)\n process.stderr.write(`[zidane/spawn] reassignBackgroundTasks failed: ${err instanceof Error ? err.message : String(err)}\\n`)\n }\n }\n\n // Always attempt to destroy. Capture any destroy error so it can be\n // surfaced via spawn:error without masking the primary outcome.\n try {\n await agent.destroy()\n }\n catch (err) {\n destroyError = err instanceof Error ? err : new Error(String(err))\n }\n }\n\n // Aggregate after run finalization so in-flight totalIn/Out/turns\n // counters are stable.\n if (finalStats) {\n localStats.totalIn += finalStats.totalIn\n localStats.totalOut += finalStats.totalOut\n localStats.totalCacheRead += finalStats.totalCacheRead\n localStats.totalCacheCreation += finalStats.totalCacheCreation\n localStats.turns += finalStats.turns\n // `elapsed` is a cumulative sum across children — documented as such.\n // Over-counts when children run in parallel; reflects \"total CPU-ish\n // time burned on children\" rather than wall-clock parallelism.\n localStats.elapsed += finalStats.elapsed\n }\n\n // Build ChildRunStats + emit completion hook. Emitted for every\n // terminal state (success/abort/timeout/error) so consumers can\n // reconcile local state without also listening on spawn:error.\n const childRunStats: ChildRunStats = {\n id,\n task,\n stats: finalStats!,\n depth: childDepth,\n status: childRunStatus,\n ...(finalStats!.output ? { output: finalStats!.output } : {}),\n }\n options.onComplete?.(child, finalStats!, childRunStatus)\n await ctx.hooks.callHook('spawn:complete', childRunStats)\n\n if (destroyError) {\n // Non-fatal but worth surfacing: a destroy failure leaves an\n // execution-handle / MCP connection in a weird state.\n await ctx.hooks.callHook('spawn:error', {\n id,\n task,\n depth: childDepth,\n error: destroyError,\n })\n }\n\n return result\n }\n finally {\n unbubble?.()\n localActiveCount--\n localChildren.delete(id)\n }\n },\n }\n}\n","import type { ToolContext, ToolDef } from './types'\nimport { Buffer } from 'node:buffer'\n\n/**\n * Write a file, with an idempotency signal when the content is unchanged.\n *\n * Three return shapes — chosen so the model can recognize a no-op without a\n * separate read:\n * - `Created path (N bytes)` — file did not exist\n * - `Updated path (N bytes)` — content differed from on-disk\n * - `No change needed: path already at target state (N bytes)` — equal\n *\n * Race window: in non-process execution contexts (docker, sandbox) shared by\n * multiple agents, another writer can mutate the file between our read and\n * our write. Local process context is single-writer per agent so the race is\n * a non-issue there. Documented rather than locked because the cost of\n * cross-context locking outweighs the cost of a stale \"No change\" message.\n */\n\nexport const writeFile: ToolDef = {\n spec: {\n name: 'write_file',\n description: 'Write content to a file (creates parent directories). Returns Created / Updated / \"No change needed\" so the model can detect no-ops without a separate read.',\n inputSchema: {\n type: 'object',\n properties: {\n path: { type: 'string', description: 'Relative file path.' },\n content: { type: 'string', description: 'File content.' },\n },\n required: ['path', 'content'],\n },\n },\n async execute({ path, content }, ctx: ToolContext) {\n const targetPath = path as string\n const targetContent = content as string\n\n let existing: string | undefined\n try {\n existing = await ctx.execution.readFile(ctx.handle, targetPath)\n }\n catch {\n // File does not exist (or read failed for unrelated reasons — we'll let\n // writeFile surface that error). Either way: treat as a fresh write.\n }\n\n const bytes = Buffer.byteLength(targetContent)\n\n if (existing === targetContent)\n return `No change needed: ${targetPath} already at target state (${bytes} bytes).`\n\n await ctx.execution.writeFile(ctx.handle, targetPath, targetContent)\n\n return existing === undefined\n ? `Created ${targetPath} (${bytes} bytes).`\n : `Updated ${targetPath} (${bytes} bytes).`\n },\n}\n"],"mappings":"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"}