zidane 5.13.13 → 5.13.16
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.
- package/dist/acp-cli.js +8 -7
- package/dist/acp-cli.js.map +1 -1
- package/dist/{acp-CqXcM2Km.js → acp-eGzYbNGF.js} +8 -8
- package/dist/{acp-CqXcM2Km.js.map → acp-eGzYbNGF.js.map} +1 -1
- package/dist/acp.d.ts +2 -2
- package/dist/acp.js +1 -1
- package/dist/{agent-NkKgz5Dh.d.ts → agent-CNIOsTUg.d.ts} +44 -2
- package/dist/agent-CNIOsTUg.d.ts.map +1 -0
- package/dist/{auth-CGTf8v1_.js → auth-D9rP8khI.js} +2 -2
- package/dist/{auth-CGTf8v1_.js.map → auth-D9rP8khI.js.map} +1 -1
- package/dist/chat/pure.d.ts +3 -3
- package/dist/chat.d.ts +6 -6
- package/dist/chat.js +3 -3
- package/dist/contexts/daytona.d.ts +22 -4
- package/dist/contexts/daytona.d.ts.map +1 -1
- package/dist/contexts/daytona.js +6 -5
- package/dist/contexts/daytona.js.map +1 -1
- package/dist/contexts/docker.js +2 -1
- package/dist/contexts/docker.js.map +1 -1
- package/dist/contexts/e2b.d.ts +2 -2
- package/dist/contexts/sandbox.d.ts +2 -0
- package/dist/contexts/sandbox.js +55 -0
- package/dist/contexts/sandbox.js.map +1 -0
- package/dist/{contexts-DHi8LPCp.js → contexts-BebciJyQ.js} +3 -53
- package/dist/contexts-BebciJyQ.js.map +1 -0
- package/dist/contexts.d.ts +2 -1
- package/dist/contexts.js +2 -1
- package/dist/{errors-BpPfMo_4.js → errors-DJUxZg9b.js} +3 -2
- package/dist/{errors-BpPfMo_4.js.map → errors-DJUxZg9b.js.map} +1 -1
- package/dist/eval.d.ts +1 -1
- package/dist/eval.js +3 -3
- package/dist/{fetch-url-Cgbq-HYx.js → fetch-url-CWE8X5OD.js} +2 -2
- package/dist/{fetch-url-Cgbq-HYx.js.map → fetch-url-CWE8X5OD.js.map} +1 -1
- package/dist/{glob-DCWXy_tr.js → glob-D56-KpBp.js} +2 -12
- package/dist/glob-D56-KpBp.js.map +1 -0
- package/dist/glob-shell-rJMoCIGb.js +21 -0
- package/dist/glob-shell-rJMoCIGb.js.map +1 -0
- package/dist/{headless-C6Idunwh.js → headless-HAFnYaDY.js} +6 -6
- package/dist/{headless-C6Idunwh.js.map → headless-HAFnYaDY.js.map} +1 -1
- package/dist/headless.d.ts +1 -1
- package/dist/headless.js +1 -1
- package/dist/{index-BgB_425D.d.ts → index-Bzh-W2RD.d.ts} +26 -8
- package/dist/index-Bzh-W2RD.d.ts.map +1 -0
- package/dist/{index-BFY7mcar.d.ts → index-DuB7Cf02.d.ts} +2 -2
- package/dist/{index-BFY7mcar.d.ts.map → index-DuB7Cf02.d.ts.map} +1 -1
- package/dist/index-HQJDOWvo.d.ts +7 -0
- package/dist/index-HQJDOWvo.d.ts.map +1 -0
- package/dist/index.d.ts +6 -5
- package/dist/index.js +12 -11
- package/dist/index.js.map +1 -1
- package/dist/{interpolate-ConAiXGy.js → interpolate-BtIgcCuz.js} +2 -2
- package/dist/{interpolate-ConAiXGy.js.map → interpolate-BtIgcCuz.js.map} +1 -1
- package/dist/{logger-LQmSBfD_.d.ts → logger-HOG4EGv6.d.ts} +2 -2
- package/dist/{logger-LQmSBfD_.d.ts.map → logger-HOG4EGv6.d.ts.map} +1 -1
- package/dist/{login-DE-_d045.js → login-CCA-1lgK.js} +2 -2
- package/dist/{login-DE-_d045.js.map → login-CCA-1lgK.js.map} +1 -1
- package/dist/{mcp-2OGi_NQu.js → mcp-Dn5W65Lv.js} +2 -2
- package/dist/{mcp-2OGi_NQu.js.map → mcp-Dn5W65Lv.js.map} +1 -1
- package/dist/mcp.d.ts +1 -1
- package/dist/mcp.js +1 -1
- package/dist/{messages-U_87Z7GH.js → messages-FUqY3pci.js} +2 -2
- package/dist/{messages-U_87Z7GH.js.map → messages-FUqY3pci.js.map} +1 -1
- package/dist/output/stream-json.d.ts +2 -2
- package/dist/output/stream-json.js +1 -1
- package/dist/output/terminal.d.ts +2 -2
- package/dist/{presets-eC4VwuHh.js → presets-DDRkelUs.js} +2 -2
- package/dist/{presets-eC4VwuHh.js.map → presets-DDRkelUs.js.map} +1 -1
- package/dist/presets.d.ts +2 -2
- package/dist/presets.js +1 -1
- package/dist/{providers-DyMPTo51.js → providers-BPVOGmde.js} +13 -5
- package/dist/providers-BPVOGmde.js.map +1 -0
- package/dist/providers.d.ts +1 -1
- package/dist/providers.js +2 -2
- package/dist/restate.d.ts +38 -2
- package/dist/restate.d.ts.map +1 -1
- package/dist/restate.js +22 -0
- package/dist/restate.js.map +1 -1
- package/dist/{index-CF15aqlk.d.ts → sandbox-B-bMq3K6.d.ts} +2 -5
- package/dist/sandbox-B-bMq3K6.d.ts.map +1 -0
- package/dist/session/sqlite.d.ts +1 -1
- package/dist/session/sqlite.js +1 -1
- package/dist/{session-DQ4bEncf.js → session-C0D4p0Gy.js} +2 -2
- package/dist/{session-DQ4bEncf.js.map → session-C0D4p0Gy.js.map} +1 -1
- package/dist/session.d.ts +1 -1
- package/dist/session.js +2 -2
- package/dist/skills.d.ts +2 -2
- package/dist/skills.js +1 -1
- package/dist/{tool-formatters-DvtGhbJN.d.ts → tool-formatters-B4Ll4Xpz.d.ts} +2 -2
- package/dist/{tool-formatters-DvtGhbJN.d.ts.map → tool-formatters-B4Ll4Xpz.d.ts.map} +1 -1
- package/dist/tools/fetch-url.d.ts +1 -1
- package/dist/tools/fetch-url.js +1 -1
- package/dist/tools/web-search.d.ts +1 -1
- package/dist/tools/web-search.js +2 -2
- package/dist/{tools-BvATiiCO.js → tools-BwqbsLcJ.js} +83 -26
- package/dist/tools-BwqbsLcJ.js.map +1 -0
- package/dist/tools.d.ts +2 -2
- package/dist/tools.js +1 -1
- package/dist/{transcript-anchors-DFmfOesU.d.ts → transcript-anchors-CRnGrkTd.d.ts} +4 -4
- package/dist/{transcript-anchors-DFmfOesU.d.ts.map → transcript-anchors-CRnGrkTd.d.ts.map} +1 -1
- package/dist/{transcript-anchors-Cn1Unhn-.js → transcript-anchors-dPvyuqmU.js} +9 -9
- package/dist/{transcript-anchors-Cn1Unhn-.js.map → transcript-anchors-dPvyuqmU.js.map} +1 -1
- package/dist/tui.d.ts +3 -3
- package/dist/tui.js +10 -10
- package/dist/tui.js.map +1 -1
- package/dist/{turn-operations-DWUN8cHo.d.ts → turn-operations-DYbhKmSu.d.ts} +3 -3
- package/dist/{turn-operations-DWUN8cHo.d.ts.map → turn-operations-DYbhKmSu.d.ts.map} +1 -1
- package/dist/types.d.ts +3 -3
- package/dist/types.js +1 -1
- package/docs/ARCHITECTURE.md +2 -0
- package/docs/CHAT.md +3 -3
- package/docs/RESTATE.md +58 -0
- package/docs/SKILL.md +1 -0
- package/docs/TUI.md +1 -1
- package/package.json +6 -1
- package/dist/agent-NkKgz5Dh.d.ts.map +0 -1
- package/dist/contexts-DHi8LPCp.js.map +0 -1
- package/dist/glob-DCWXy_tr.js.map +0 -1
- package/dist/index-BgB_425D.d.ts.map +0 -1
- package/dist/index-CF15aqlk.d.ts.map +0 -1
- package/dist/providers-DyMPTo51.js.map +0 -1
- package/dist/tools-BvATiiCO.js.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tools-BwqbsLcJ.js","names":["turnsToMessages","DEFAULT_LIMIT","pathResolve","builtinShell","entryPath","entryPath","readFile","writeFile"],"sources":["../src/aliasing.ts","../src/compact/policy.ts","../src/dedup-tools.ts","../src/attachment-handle.ts","../src/timeout.ts","../src/compact/errors.ts","../src/compact/messages.ts","../src/compact/prompt.ts","../src/compact/compact.ts","../src/compact/restore.ts","../src/loop-persistence.ts","../src/tools/validation.ts","../src/loop.ts","../src/repeat-guard.ts","../src/tool-budgets.ts","../src/tool-cancel.ts","../src/tools/shell-semantics.ts","../src/tools/truncate.ts","../src/tools/shell.ts","../src/tools/binary-detect.ts","../src/tools/skills-read.ts","../src/tools/skills-run-script.ts","../src/tools/skills-use.ts","../src/tools/tool-search.ts","../src/tools/wait-task.ts","../src/agent.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'\nimport { fnv1aBase36 } from './hash'\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 * Tool names must match `^[a-zA-Z0-9_-]{1,N}$`. The charset is universal; the\n * length cap is taken as the STRICTEST across providers (OpenAI/-compat: 64;\n * Anthropic allows 128) so a sanitized name is valid on EVERY provider — and so\n * a mid-session model switch can never make the provider-level drop guard\n * (`sanitizeToolSpecs`) discard a tool we could have kept.\n *\n * MCP tool names are minted verbatim as `mcp_<server>_<tool>` (see\n * `connectMcpServers`), so a server configured as `SSH Ipseity` yields\n * `mcp_SSH Ipseity_exec` — the space (and `.`/`:`/`/`, unicode, or a >64-char\n * join) 400s the request the instant that tool lands in the `tools` array:\n * `tools.N.custom.name: String should match pattern ...`.\n */\nconst WIRE_NAME_MAX = 64\nconst WIRE_NAME_OK = /^[\\w-]{1,64}$/\nconst WIRE_NAME_BAD_CHAR = /[^\\w-]/g\n\n/**\n * Build OUTBOUND wire aliases that coerce any tool name violating the provider\n * name constraint into a valid one. Only offenders get an entry; clean setups\n * return `undefined` (no alias map churn, prompt cache untouched).\n *\n * Returned as a `Record` so it merges into `effectiveToolAliases` upstream of\n * BOTH wire-name computations — the `tool_search` catalog and the `tools` array\n * — keeping them in lockstep. Canonical names (dispatch, hooks, persisted\n * turns) never change; only the bytes the model sees do.\n *\n * Scoped to MCP names (the only third-party-controlled, thus only realistically\n * invalid, source). The hash suffix keeps distinct originals distinct even when\n * they collapse to the same replacement; `taken` guards the residual collision.\n */\nexport function buildSanitizedWireAliases(\n names: Iterable<string>,\n existing: Record<string, string> | undefined,\n): Record<string, string> | undefined {\n const wireOf = (name: string): string => {\n const alias = existing?.[name]\n return typeof alias === 'string' && alias.length > 0 ? alias : name\n }\n\n // Already-valid wire names stay occupied so a freshly minted one can never\n // shadow another tool.\n const taken = new Set<string>()\n const offenders: string[] = []\n for (const name of names) {\n const wire = wireOf(name)\n if (WIRE_NAME_OK.test(wire))\n taken.add(wire)\n else\n offenders.push(name)\n }\n if (offenders.length === 0)\n return undefined\n\n const out: Record<string, string> = {}\n for (const name of offenders) {\n const sanitized = sanitizeWireName(wireOf(name), taken)\n out[name] = sanitized\n taken.add(sanitized)\n }\n return out\n}\n\nfunction sanitizeWireName(name: string, taken: ReadonlySet<string>): string {\n let base = name.replace(WIRE_NAME_BAD_CHAR, '_')\n if (base.length === 0)\n base = 'tool'\n // Hash the ORIGINAL so `a.b` vs `a/b` (both → `a_b`) and truncated long\n // names stay distinct and deterministic across runs.\n const hash = fnv1aBase36(name)\n const withSuffix = (suffix: string): string =>\n `${base.slice(0, Math.max(1, WIRE_NAME_MAX - suffix.length))}${suffix}`\n let candidate = withSuffix(`_${hash}`)\n for (let n = 1; taken.has(candidate); n++)\n candidate = withSuffix(`_${hash}_${n}`)\n return candidate\n}\n\n/**\n * Augment {@link AliasMaps} with INBOUND-ONLY aliases for the Claude Code\n * `mcp__server__tool` double-underscore naming convention. For every MCP\n * tool registered with the canonical `mcp_<server>_<tool>` form, add a\n * `canonicalByAlias` entry mapping the double-underscore variant to the\n * same canonical name. Outbound (`aliasByCanonical`) is left untouched —\n * the model still sees the single-underscore canonical on the wire, so\n * the tools-array byte count (and the provider prompt cache) is\n * unaffected.\n *\n * Why: SDK consumers and tool descriptions across the ecosystem\n * inconsistently reference one form or the other (Anthropic's docs use\n * double, zidane uses single). A Claude-Code-trained model emitting\n * `mcp__acme__apply_migration` against a server zidane registered as\n * `mcp_acme_apply_migration` would otherwise trip the `tool:unknown`\n * path and waste a turn on the correction.\n *\n * Idempotent: a canonical that already has a double-underscore alias\n * mapped (host's explicit `toolAliases` got there first) is left alone.\n * Tools whose canonical name DOESN'T start with `mcp_` (or that contain\n * no inner separator) are skipped — we don't want to invent aliases for\n * non-MCP tools or malformed names.\n *\n * Mutates `maps.canonicalByAlias` in place and returns the same object\n * for chaining.\n */\nexport function augmentMcpDoubleUnderscoreAliases(\n maps: AliasMaps,\n canonicalNames: Iterable<string>,\n): AliasMaps {\n for (const canonical of canonicalNames) {\n if (!canonical.startsWith('mcp_'))\n continue\n // Strip leading `mcp_`, then require AT LEAST one more `_` so the\n // remainder is `<server>_<tool>` (a degenerate `mcp_foo` with no\n // tool segment has nothing meaningful to double-underscore).\n const tail = canonical.slice(4)\n const sep = tail.indexOf('_')\n if (sep <= 0 || sep >= tail.length - 1)\n continue\n const server = tail.slice(0, sep)\n const tool = tail.slice(sep + 1)\n const doubleForm = `mcp__${server}__${tool}`\n if (doubleForm === canonical)\n continue // shouldn't happen but defensive\n if (maps.canonicalByAlias.has(doubleForm))\n continue // host alias or earlier pass got there first\n maps.canonicalByAlias.set(doubleForm, canonical)\n }\n return maps\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","/**\n * Auto-compaction trigger policy + effective-context-window math.\n *\n * Core module (no chat / loop / provider-catalog dependency) so BOTH the\n * agent loop's loop-native auto-compaction AND the chat hosts share one\n * decision function. Previously `shouldAutoCompact` lived in `src/chat/`\n * and `effectiveContextWindow` in `src/chat/providers.ts`; the loop can't\n * import from `src/chat/`, so the policy lives here and chat re-exports it\n * for back-compat.\n *\n * Pure functions, no I/O. Same inputs -> same answer.\n */\n\n/**\n * Reserved output budget subtracted from the raw context window when\n * computing the effective ceiling for compaction / warning thresholds.\n *\n * Aligned with Claude Code's `COMPACT_MAX_OUTPUT_TOKENS = 20_000` — covers\n * p99.99 of summary + response output. A turn that consumed N input tokens\n * leaves `effectiveWindow - N` headroom for the next prompt's response; once\n * headroom shrinks below the threshold, auto-compaction fires.\n */\nexport const OUTPUT_RESERVE_TOKENS = 20_000\n\n/**\n * Effective context window — what the next user turn can actually pack\n * before the provider reserves space for the assistant's reply. Equals\n * `rawWindow - OUTPUT_RESERVE_TOKENS`, clamped to `>= 1` so a tiny\n * (or absurd) window doesn't yield zero / negative thresholds.\n *\n * Pass `null` through unchanged so callers can pipe a raw window lookup\n * directly without an intermediate check.\n */\nexport function effectiveContextWindow(rawWindow: number | null): number | null {\n if (rawWindow === null)\n return null\n return Math.max(1, rawWindow - OUTPUT_RESERVE_TOKENS)\n}\n\n/**\n * Default hysteresis floor for {@link shouldAutoCompact}. After a successful\n * compaction lands, the next compaction is suppressed until input usage\n * grows by at least this fraction of the effective context window beyond\n * the post-compact baseline.\n *\n * `0.1` = 10% of the window. Picked so the immediate post-compact bounce\n * (summary turn + restored attachments + re-emitted system prefix) never\n * re-fires the trigger by itself, but a meaningful chunk of new work\n * (one or two large tool reads) does. Tune via the predicate's\n * `minGrowthFraction` input if a host needs different ergonomics.\n */\nexport const AUTO_COMPACT_MIN_GROWTH_FRACTION = 0.1\n\n/**\n * Inputs to {@link shouldAutoCompact}.\n *\n * Kept as a struct so the call site reads as the predicate it is, and so\n * future inputs (per-session opt-out, recent-compaction cooldown, …) land\n * without re-threading every consumer.\n */\nexport interface AutoCompactInput {\n /** User's master toggle. `false` → trigger never fires. */\n enabled: boolean\n /**\n * Threshold expressed as a fraction of the effective context window\n * (e.g. `0.8` = 80%). Values outside the open interval `(0, 1)` are\n * rejected with `'invalid-threshold'` — `<= 0` is meaningless, and\n * `>= 1` would schedule the trigger for \"after we've already\n * overflowed\" which is too late to be useful.\n */\n threshold: number\n /**\n * Latest input-token count from the last `turn:after`. Must be a\n * non-negative finite number; `NaN` / `±Infinity` / negative values\n * are treated as \"no usable signal\" and the trigger skips\n * conservatively (we'd rather defer compaction than fire on garbage).\n */\n inputTokens: number\n /**\n * Model's raw context window. `null` for unknown / custom models — the\n * trigger skips since we can't compute a ratio.\n */\n rawContextWindow: number | null\n /**\n * Whether a compaction is already in flight for this session. Passing it\n * through the predicate lets tests assert \"doesn't double-fire\" without\n * spinning up the agent loop.\n */\n alreadyCompacting?: boolean\n /**\n * Input-token count observed immediately AFTER the most recent successful\n * compaction landed. Used together with {@link minGrowthFraction} to\n * suppress thrashing — after compaction lowers the prefix from 85% → 35%,\n * the next few turns drift back up; without hysteresis the trigger fires\n * again the instant the threshold is re-crossed, often before the model\n * has done any new work. Tracking the post-compact baseline lets the\n * predicate require meaningful new growth before re-firing.\n *\n * `undefined` (no compaction yet this session) → hysteresis disabled,\n * predicate behaves as before.\n */\n lastCompactedInputTokens?: number\n /**\n * Minimum fraction of the effective context window that must be CONSUMED\n * by new content (i.e. `inputTokens - lastCompactedInputTokens`) before\n * a follow-up compaction fires. Expressed in the same units as\n * {@link threshold}, e.g. `0.1` = 10% of the window. Defaults to `0`\n * (no hysteresis) when omitted; chat hosts pick a value like `0.1`.\n *\n * Only consulted when {@link lastCompactedInputTokens} is set — first\n * compaction in a session always fires off the absolute threshold.\n */\n minGrowthFraction?: number\n}\n\nexport type AutoCompactDecision\n = | { kind: 'fire', usedFraction: number, effectiveWindow: number }\n | { kind: 'skip', reason: 'disabled' | 'unknown-window' | 'invalid-threshold' | 'invalid-input-tokens' | 'under-threshold' | 'already-compacting' | 'cooldown' }\n\n/**\n * Decide whether auto-compaction should fire for the latest turn.\n *\n * Order of checks is deliberate: cheapest / most common skips first\n * (`disabled` is one boolean read; `unknown-window` is the predictable\n * miss for custom models), invariant violations next, then the actual\n * threshold math. `already-compacting` runs last so the diagnostic\n * surfaces correctly when the predicate is called inside an existing\n * compaction window.\n */\nexport function shouldAutoCompact(input: AutoCompactInput): AutoCompactDecision {\n if (!input.enabled)\n return { kind: 'skip', reason: 'disabled' }\n\n if (input.rawContextWindow === null)\n return { kind: 'skip', reason: 'unknown-window' }\n\n if (!Number.isFinite(input.threshold) || input.threshold <= 0 || input.threshold >= 1)\n return { kind: 'skip', reason: 'invalid-threshold' }\n\n // Guard against `NaN` / `±Infinity` / negative usage. The realistic\n // source for any of these is a provider returning `undefined` (`?? 0`\n // collapses to 0 normally, but a malformed nested object could leak\n // through). We'd rather skip than emit `used NaN%` to the user.\n if (!Number.isFinite(input.inputTokens) || input.inputTokens < 0)\n return { kind: 'skip', reason: 'invalid-input-tokens' }\n\n // `effectiveContextWindow` clamps to `>= 1` and only returns `null`\n // when the raw window is `null` — already short-circuited above. So\n // the result here is guaranteed positive, no further defense needed.\n const effectiveWindow = effectiveContextWindow(input.rawContextWindow)!\n\n const usedFraction = input.inputTokens / effectiveWindow\n if (usedFraction < input.threshold)\n return { kind: 'skip', reason: 'under-threshold' }\n\n // Hysteresis: once a session has compacted, require meaningful new growth\n // before re-firing. Without this, the threshold is crossed again the\n // moment the post-compact summary turn + a re-emitted system prefix push\n // usage back over the line, leading to compact → compact → compact loops\n // that lose more context than they save. Only active when the caller\n // tracks the post-compact baseline AND opts into a non-zero growth floor.\n if (typeof input.lastCompactedInputTokens === 'number'\n && Number.isFinite(input.lastCompactedInputTokens)\n && input.lastCompactedInputTokens >= 0\n && typeof input.minGrowthFraction === 'number'\n && Number.isFinite(input.minGrowthFraction)\n && input.minGrowthFraction > 0) {\n const growthTokens = input.inputTokens - input.lastCompactedInputTokens\n const growthFraction = growthTokens / effectiveWindow\n if (growthFraction < input.minGrowthFraction)\n return { kind: 'skip', reason: 'cooldown' }\n }\n\n if (input.alreadyCompacting)\n return { kind: 'skip', reason: 'already-compacting' }\n\n return { kind: 'fire', usedFraction, effectiveWindow }\n}\n","/**\n * `dedup-tools` middleware — generic per-tool argument deduplication on top\n * of the `tool:gate` writable-`result` / writable-`block` slots.\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 * Two modes (see `DedupToolConfig`):\n * - `'replay'` (default) — identical re-calls replay the prior result.\n * Cheap, but doesn't break the loop — the model keeps seeing the same\n * tool_result and keeps trying.\n * - `'block-after'` — replay for the first `threshold` identical calls,\n * then refuse with `Blocked: <reason>` via `tool:gate`. Sticky: every\n * subsequent identical call also blocks until a different input resets\n * the counter for that tool.\n *\n * Wiring:\n * 1. `tool:gate` — compute hash, compare to prior `(name → entry)`.\n * - Hash miss: record the hash on the per-call `pending` map so\n * `tool:after` writes the entry with `repeats: 0`.\n * - Hash hit, replay mode: set `ctx.result`, bump `pending.repeats`.\n * The Z20 substitute path fires `tool:after`; the handler reads\n * `pending.repeats` and persists it. The model sees the cached\n * payload again.\n * - Hash hit, block-after mode AND `count >= threshold`: set\n * `ctx.block` + `ctx.reason`, persist the bumped counter on\n * `state` directly (block path skips `tool:after`).\n * - Hash hit, block-after mode AND `count < threshold`: same as\n * replay (the cache is still warm, we just haven't crossed the\n * ceiling yet).\n * 2. `tool:after` — record `(hash, result, repeats)` for the dispatched\n * OR replayed call. Recorded post-`tool:transform`, i.e. the payload\n * the model actually saw — replaying a pre-transform value would\n * 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 * State scoping (`DedupToolConfig.scope`):\n * - `'session'` (default) — `(session, toolName)`. Right for pure tools.\n * - `'run'` — `(session, runId, toolName)`. Keeps a parent and its\n * subagents from sharing a dedup entry, so an identical re-call only\n * counts/replays within the run that issued it. `todowrite` uses this\n * so its `block-after` loop-breaker is run-local.\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 interface PendingEntry {\n hash: string\n /**\n * Number of replay-hits this call has accumulated against the prior\n * `state` entry. `0` means the gate took the fresh-dispatch path\n * (hash miss). Persisted into `state.repeats` on `tool:after`.\n */\n repeats: number\n /**\n * Resolved state-map key (`name` or `runId::name`) the gate selected.\n * Carried so `tool:after` records against the exact same slot the\n * gate read — re-deriving there would risk drift if the scope inputs\n * ever differ between the two hooks.\n */\n key: string\n }\n const pending = new Map<string, PendingEntry>()\n\n function pendingKey(callId: string, name: string): string {\n return `${callId}::${name}`\n }\n\n /**\n * Resolve the legacy hasher-only form vs the new config-object form to\n * the same internal shape. Centralised so the gate/after handlers stay\n * single-branch. Returns `null` when the tool has no dedup config.\n */\n function resolveConfig(name: string): {\n hasher: (input: Record<string, unknown>) => string | undefined\n mode: 'replay' | 'block-after'\n threshold: number\n reason: string | ((input: Record<string, unknown>, count: number) => string) | undefined\n scope: 'session' | 'run'\n } | null {\n const dedupTools = getDedupTools()\n const raw = dedupTools?.[name]\n if (!raw)\n return null\n if (typeof raw === 'function')\n return { hasher: raw, mode: 'replay', threshold: Infinity, reason: undefined, scope: 'session' }\n // Threshold floor: `Math.max(2, Math.floor(raw.threshold))`. Values\n // `<= 1` would block the very first call (or first replay), which is\n // indistinguishable from never registering the hasher at all. We\n // clamp to 2 (block on the 2nd identical call) instead of silently\n // failing closed. Non-numbers / NaN fall through to the\n // mode-conditional default. Floats are floored AFTER the clamp so\n // `threshold: 1.5` produces 2, not 1.\n const rawThreshold = typeof raw.threshold === 'number' && Number.isFinite(raw.threshold)\n ? Math.max(2, Math.floor(raw.threshold))\n : undefined\n const threshold = rawThreshold\n ?? (raw.mode === 'block-after' ? 4 : Infinity)\n return {\n hasher: raw.hasher,\n mode: raw.mode ?? 'replay',\n threshold,\n reason: raw.reason,\n scope: raw.scope === 'run' ? 'run' : 'session',\n }\n }\n\n /**\n * State-map key for a tool's dedup entry. Session-scoped tools key on\n * the bare canonical name (the historical behavior). Run-scoped tools\n * prefix the owning `runId` so a parent and a subagent never share an\n * entry — falling back to the bare name when no `runId` is present\n * (hand-built contexts), which keeps the session-scope semantics for\n * those callers.\n */\n function stateKey(name: string, scope: 'session' | 'run', runId: string | undefined): string {\n if (scope === 'run' && runId)\n return `${runId}::${name}`\n return name\n }\n\n function formatReason(\n reason: string | ((input: Record<string, unknown>, count: number) => string) | undefined,\n toolName: string,\n input: Record<string, unknown>,\n count: number,\n ): string {\n if (typeof reason === 'string')\n return reason\n if (typeof reason === 'function') {\n try {\n const out = reason(input, count)\n if (typeof out === 'string' && out.length > 0)\n return out\n }\n catch {\n // Fall through to default\n }\n }\n return `Identical \\`${toolName}\\` call repeated ${count} times — break the loop by changing your approach or moving on to a different step.`\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 config = resolveConfig(ctx.name)\n if (!config)\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 = config.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 key = stateKey(ctx.name, config.scope, ctx.runId)\n const prior = state.get(key)\n if (prior && prior.hash === hash) {\n // Replay-hit. `repeats` in state is the count of replays BEFORE\n // this call (0 right after a fresh dispatch). The total identical\n // calls counting this one = (prior.repeats ?? 0) + 1 + 1 = the\n // first dispatch plus all replays inclusive. We compute that as\n // `count` for both threshold checks and reason-formatting.\n const priorRepeats = prior.repeats ?? 0\n const count = priorRepeats + 2 // dispatch + earlier replays + this one\n if (config.mode === 'block-after' && count >= config.threshold) {\n // Block. The block path skips `tool:after`, so we MUST persist\n // the bumped counter here to keep subsequent identical calls\n // blocking. We do NOT overwrite `prior.result` — keeping the\n // last-seen successful payload around lets the host inspect it\n // post-hoc, and a hash change later resets the counter anyway.\n ctx.block = true\n ctx.reason = formatReason(config.reason, ctx.name, ctx.input, count)\n state.set(key, { hash, result: prior.result, repeats: priorRepeats + 1 })\n return\n }\n // Replay — feed the cached payload back through the Z20 substitute\n // path. `tool:after` fires and records the bumped counter via\n // `pending`.\n ctx.result = prior.result\n pending.set(pendingKey(ctx.callId, ctx.name), { hash, repeats: priorRepeats + 1, key })\n return\n }\n\n // Miss — remember the hash + zero repeats so the matching\n // `tool:after` records a fresh state entry.\n pending.set(pendingKey(ctx.callId, ctx.name), { hash, repeats: 0, key })\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 entry = pending.get(key)\n if (entry === 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(entry.key, { hash: entry.hash, result: ctx.result, repeats: entry.repeats })\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 * Attachment re-resolve handles + the media age-out marker.\n *\n * zidane keeps attachment bytes inline in `session.turns` (browsable history),\n * but ages old base64 OFF THE WIRE so a long session doesn't carry every\n * screenshot / pasted image forever (`behavior.mediaKeepTurns`). An aged-out\n * media block is replaced on the wire with a short text marker carrying a\n * stable `attachment://<hash>` handle; `read_file` resolves that handle back to\n * the original bytes from `session.turns` on demand, so nothing is ever lost -\n * a strict improvement over a dead \"[content cleared]\" stub.\n *\n * The handle is a content digest of the full base64 payload. It's stable across\n * turns (so markers stay byte-identical and the prompt cache holds), needs no\n * schema change / id threading at ingest, and avoids re-serving the wrong image\n * when two attachments share a prefix and length.\n */\n\nimport type { SessionContentBlock, ToolResultContent } from './types'\nimport { createHash } from 'node:crypto'\n\nexport const ATTACHMENT_HANDLE_SCHEME = 'attachment://'\n\n/**\n * Stable digest of the full base64 payload. We keep the visible handle short\n * (128-bit hex prefix) but hash the whole payload so same-size images with\n * identical headers / early pixels cannot alias.\n */\nfunction hashAttachmentData(data: string): string {\n return createHash('sha256').update(data).digest('hex').slice(0, 32)\n}\n\n/** Stable re-resolve handle for a base64 media payload. */\nexport function attachmentHandleFor(data: string): string {\n return `${ATTACHMENT_HANDLE_SCHEME}${hashAttachmentData(data)}`\n}\n\n/**\n * Parse an `attachment://<hash>` path back to its hash, or `null` when the path\n * isn't an attachment handle (i.e. a normal filesystem path). Used by\n * `read_file` to branch into the session re-resolve path.\n */\nexport function parseAttachmentHandle(path: string): string | null {\n if (!path.startsWith(ATTACHMENT_HANDLE_SCHEME))\n return null\n const hash = path.slice(ATTACHMENT_HANDLE_SCHEME.length).trim()\n return hash.length > 0 ? hash : null\n}\n\n/** Decoded byte length of a base64 string (padding-accurate). */\nfunction decodedBase64Bytes(b64: string): number {\n if (b64.length === 0)\n return 0\n const pad = b64.endsWith('==') ? 2 : b64.endsWith('=') ? 1 : 0\n return Math.max(0, Math.floor((b64.length * 3) / 4) - pad)\n}\n\nfunction humanBytes(bytes: number): string {\n if (bytes < 1024)\n return `${bytes} B`\n if (bytes < 1024 * 1024)\n return `${Math.round(bytes / 1024)} KB`\n return `${(bytes / (1024 * 1024)).toFixed(1)} MB`\n}\n\ninterface MediaLike {\n type: 'image' | 'document'\n mediaType: string\n data: string\n name?: string\n}\n\n/**\n * Build the wire marker that replaces an aged-out media block. Stable for a\n * given payload (handle + size + mediaType) so the elided prefix stays\n * byte-identical across turns and the prompt cache holds.\n *\n * `readFileToolName` is the WIRE name of the file-read tool (e.g. `Read` when\n * the host aliases `read_file`), so the re-read hint names the tool the model\n * actually sees. Defaults to the canonical `read_file`. Pass `null` when the\n * media was stripped for emergency overflow recovery but no re-resolving tool\n * is available in this run.\n */\nexport function mediaAgeoutMarker(block: MediaLike, readFileToolName: string | null = 'read_file'): string {\n const handle = attachmentHandleFor(block.data)\n const size = humanBytes(decodedBase64Bytes(block.data))\n const named = block.name ? ` \"${block.name}\"` : ''\n const resolveHint = readFileToolName\n ? `Still saved in this session; re-read with ${readFileToolName} path=\"${handle}\" to view it again.`\n : `Still saved in this session as ${handle}, but no file-read tool is available in this run to re-open it.`\n return `[${block.type} aged out of context to save tokens -${named} ${block.mediaType}, ${size}. ${resolveHint}]`\n}\n\nexport interface ResolvedAttachment {\n kind: 'image' | 'document'\n mediaType: string\n data: string\n name?: string\n encoding?: 'base64' | 'text'\n}\n\nfunction matchMediaBlock(\n block: SessionContentBlock | ToolResultContent,\n hash: string,\n): ResolvedAttachment | null {\n if (block.type === 'image' && typeof (block as { data?: unknown }).data === 'string') {\n const data = (block as { data: string }).data\n if (attachmentHandleFor(data) !== `${ATTACHMENT_HANDLE_SCHEME}${hash}`)\n return null\n return { kind: 'image', mediaType: block.mediaType, data, ...(block.name ? { name: block.name } : {}) }\n }\n if (block.type === 'document' && typeof (block as { data?: unknown }).data === 'string') {\n const data = (block as { data: string }).data\n if (attachmentHandleFor(data) !== `${ATTACHMENT_HANDLE_SCHEME}${hash}`)\n return null\n const encoding = (block as { encoding?: 'base64' | 'text' }).encoding ?? 'base64'\n return { kind: 'document', mediaType: block.mediaType, data, encoding, ...(block.name ? { name: block.name } : {}) }\n }\n return null\n}\n\n/**\n * Find the original bytes for an `attachment://<hash>` handle by scanning\n * persisted turns. Walks newest-to-oldest so a re-pasted image resolves to its\n * latest copy. Searches both top-level `image`/`document` blocks and\n * image/document parts nested in `tool_result` outputs.\n *\n * Returns `null` when no match - the bytes were never present, or only ever\n * lived as an unresolved `ref` (host-offloaded; not recoverable from turns\n * alone).\n */\nexport function resolveAttachmentFromTurns(\n turns: readonly { content: readonly SessionContentBlock[] }[],\n hash: string,\n): ResolvedAttachment | null {\n for (let i = turns.length - 1; i >= 0; i--) {\n for (const block of turns[i].content) {\n const direct = matchMediaBlock(block, hash)\n if (direct)\n return direct\n if (block.type === 'tool_result' && Array.isArray(block.output)) {\n for (const part of block.output) {\n const nested = matchMediaBlock(part, hash)\n if (nested)\n return nested\n }\n }\n }\n }\n return null\n}\n","export class OperationTimeoutError extends Error {\n readonly code = 'operation_timeout' as const\n readonly timeoutMs: number\n readonly operation: string\n\n constructor(operation: string, timeoutMs: number, message?: string) {\n super(message ?? `${operation} timed out after ${timeoutMs}ms`)\n this.name = 'OperationTimeoutError'\n this.operation = operation\n this.timeoutMs = timeoutMs\n }\n}\n\nexport function isPositiveFiniteMs(value: unknown): value is number {\n return typeof value === 'number' && Number.isFinite(value) && value > 0\n}\n\nexport function resolveTimeoutMs(\n configured: number | undefined,\n fallback: number,\n ceiling?: number,\n): number {\n const base = typeof configured === 'number'\n ? (isPositiveFiniteMs(configured) ? configured : 0)\n : fallback\n if (base <= 0)\n return 0\n return isPositiveFiniteMs(ceiling) ? Math.min(base, ceiling) : base\n}\n\nexport function linkAbortSignal(\n source: AbortSignal,\n target: AbortController,\n): () => void {\n if (source.aborted) {\n target.abort(source.reason)\n return () => {}\n }\n const onAbort = () => target.abort(source.reason)\n source.addEventListener('abort', onAbort, { once: true })\n return () => source.removeEventListener('abort', onAbort)\n}\n\nexport function withTimeout<T>(\n promise: Promise<T>,\n options: {\n operation: string\n timeoutMs: number\n onTimeout?: () => void\n message?: string\n },\n): Promise<T> {\n const { operation, timeoutMs, onTimeout, message } = options\n if (!isPositiveFiniteMs(timeoutMs))\n return promise\n\n return new Promise<T>((resolve, reject) => {\n let settled = false\n const timer = setTimeout(() => {\n if (settled)\n return\n settled = true\n try {\n onTimeout?.()\n }\n finally {\n reject(new OperationTimeoutError(operation, timeoutMs, message))\n }\n }, timeoutMs)\n\n promise.then(\n (value) => {\n if (settled)\n return\n settled = true\n clearTimeout(timer)\n resolve(value)\n },\n (err) => {\n if (settled)\n return\n settled = true\n clearTimeout(timer)\n reject(err)\n },\n )\n })\n}\n\n/**\n * Await `work` under `timeoutMs`, swallowing a timeout (logged under\n * `ZIDANE_DEBUG`) and continuing. Non-timeout errors propagate unchanged so a\n * well-behaved-but-throwing handler keeps its prior semantics. Use for\n * observability / lifecycle / teardown awaits that must never block forward\n * progress or strand a run gate (hooks, store writes, MCP close, ctx destroy).\n *\n * A non-positive / non-finite `timeoutMs` disables the bound — `work` is\n * awaited as-is (matching {@link resolveTimeoutMs}'s `0` = \"disabled\").\n */\nexport async function settleWithinTimeout(\n operation: string,\n work: Promise<unknown> | void,\n timeoutMs: number,\n): Promise<void> {\n try {\n await withTimeout(Promise.resolve(work), { operation, timeoutMs })\n }\n catch (err) {\n if (err instanceof OperationTimeoutError) {\n if (process.env.ZIDANE_DEBUG)\n console.error(`[zidane] ${operation} timed out after ${timeoutMs}ms; continuing`)\n return\n }\n throw err\n }\n}\n","/**\n * Typed errors thrown by the compaction helper.\n *\n * Lives in its own file so both the runner and the pure messages module\n * can import without circular dependencies.\n */\n\n/**\n * Raised when the caller's inputs make compaction meaningless before any\n * API call is attempted. Common cases:\n * - empty `turns`\n * - `keepTurns >= turns.length` (no older content to summarize)\n * - `'from'` / `'up_to'` anchor id not found in `turns`\n * - the resolved `toSummarize` slice has no text-bearing content\n *\n * Synchronous — thrown from `compactConversation()` before the provider\n * call so the caller can recover without a network round-trip.\n */\nexport class CompactInvalidInputError extends Error {\n constructor(message: string) {\n super(message)\n this.name = 'CompactInvalidInputError'\n }\n}\n\n/**\n * Raised when the provider rejects the compaction request with\n * `prompt_too_long` (or an equivalent) and the head-truncation retry\n * budget has been exhausted. Callers can inspect `ptlRetries` to log\n * how far the retry loop got before giving up.\n */\nexport class CompactPromptTooLongError extends Error {\n constructor(message: string, public readonly ptlRetries: number) {\n super(message)\n this.name = 'CompactPromptTooLongError'\n }\n}\n","/**\n * Pure helpers for compaction — slicing, image stripping, head-truncation\n * on `prompt_too_long`, and the synthetic-turn builder.\n *\n * Everything in this file is total and side-effect-free. Inputs are never\n * mutated; helpers return fresh arrays whenever they modify content.\n */\n\nimport type {\n SessionContentBlock,\n SessionTurn,\n ToolResultContent,\n TurnUsage,\n} from '../types'\nimport { CompactInvalidInputError } from './errors'\n\n/**\n * What to summarize and what to preserve verbatim.\n *\n * - `'full'` — summarize everything, no preserved tail.\n * - `'tail'` — summarize everything before the last `keepTurns` turns.\n * - `{ kind: 'from', turnId }` — summarize from the anchor turn onward.\n * - `{ kind: 'up_to', turnId }` — summarize up to (and including) the anchor.\n */\nexport type CompactScope\n = | 'full'\n | 'tail'\n | { kind: 'from', turnId: string }\n | { kind: 'up_to', turnId: string }\n\nexport interface CompactionSlice {\n /** The portion of the conversation that will be summarized. */\n toSummarize: readonly SessionTurn[]\n /** The portion that stays verbatim in the post-compact history. */\n preserved: readonly SessionTurn[]\n}\n\n/**\n * Partition `turns` into `(toSummarize, preserved)` according to `scope`.\n *\n * Throws {@link CompactInvalidInputError} on degenerate inputs so the\n * caller doesn't pay for a doomed provider call:\n * - empty `turns`\n * - `scope: 'tail'` with `keepTurns >= turns.length` (nothing to summarize)\n * - `scope: { from | up_to }` with an anchor id that isn't in `turns`\n * - the resulting `toSummarize` slice contains no text-bearing content\n * (only system turns or empty content)\n *\n * Pure. Returns references to the original `SessionTurn` objects — the\n * caller can compare by identity (=== Object.is) to confirm.\n */\nexport function sliceForCompaction(\n turns: readonly SessionTurn[],\n scope: CompactScope,\n keepTurns: number,\n): CompactionSlice {\n if (turns.length === 0)\n throw new CompactInvalidInputError('No turns to compact.')\n\n let toSummarize: readonly SessionTurn[]\n let preserved: readonly SessionTurn[]\n\n if (scope === 'full') {\n toSummarize = turns\n preserved = []\n }\n else if (scope === 'tail') {\n const keep = Math.max(0, keepTurns)\n if (keep >= turns.length) {\n throw new CompactInvalidInputError(\n `Nothing to compact: keepTurns (${keep}) covers the entire conversation (${turns.length} turns).`,\n )\n }\n const safeCut = findSafeRoundBoundary(turns, turns.length - keep)\n toSummarize = turns.slice(0, safeCut)\n preserved = turns.slice(safeCut)\n }\n else if (scope.kind === 'from') {\n const idx = turns.findIndex(t => t.id === scope.turnId)\n if (idx < 0)\n throw new CompactInvalidInputError(`Anchor turn not found: \"${scope.turnId}\".`)\n // 'from' summarizes the recent portion (anchor onward); everything\n // before stays verbatim. Walk the anchor BACKWARD to the start of\n // the round containing it so neither half ends mid-round (avoids\n // orphan `tool_use` in `preserved` AND orphan `tool_result` at\n // the head of `toSummarize`).\n const safeIdx = findSafeRoundBoundary(turns, idx)\n preserved = turns.slice(0, safeIdx)\n toSummarize = turns.slice(safeIdx)\n }\n else {\n // 'up_to' summarizes the older portion (up to and including the anchor);\n // everything after stays verbatim. Same `tool_use ↔ tool_result`\n // adjacency rule as 'tail': cut backward if the proposed boundary\n // would orphan a `tool_use` at the end of `toSummarize`.\n const idx = turns.findIndex(t => t.id === scope.turnId)\n if (idx < 0)\n throw new CompactInvalidInputError(`Anchor turn not found: \"${scope.turnId}\".`)\n const safeCut = findSafeRoundBoundary(turns, idx + 1)\n toSummarize = turns.slice(0, safeCut)\n preserved = turns.slice(safeCut)\n }\n\n if (toSummarize.length === 0)\n throw new CompactInvalidInputError('Compaction scope resolved to zero turns.')\n if (!hasTextBearingContent(toSummarize))\n throw new CompactInvalidInputError('Compaction scope contains no text-bearing turns to summarize.')\n\n return { toSummarize, preserved }\n}\n\n/**\n * Replace every attachment block in `turns` with a short text marker.\n *\n * Covers two shapes:\n * - Top-level `{ type: 'image', ... }` content blocks on user turns.\n * - Image entries inside `tool_result.output` array form (multimodal\n * tool results — e.g. an MCP browser screenshot).\n *\n * Unconditional by design: even on vision-capable models, the summary\n * call doesn't benefit from raw image bytes (the model can't refer to\n * them after the summary lands), and stripping uniformly avoids\n * `prompt_too_long` on image-heavy sessions.\n *\n * Returns a fresh array; input turns / blocks are never mutated.\n */\nexport function stripImagesFromTurns(turns: readonly SessionTurn[]): SessionTurn[] {\n return turns.map(turn => stripImagesFromTurn(turn))\n}\n\nfunction stripImagesFromTurn(turn: SessionTurn): SessionTurn {\n let touched = false\n const nextContent: SessionContentBlock[] = []\n for (const block of turn.content) {\n if (block.type === 'image') {\n touched = true\n nextContent.push({ type: 'text', text: '[image]' })\n continue\n }\n if (block.type === 'audio') {\n touched = true\n nextContent.push({ type: 'text', text: '[audio]' })\n continue\n }\n if (block.type === 'video') {\n touched = true\n nextContent.push({ type: 'text', text: '[video]' })\n continue\n }\n if (block.type === 'document') {\n touched = true\n nextContent.push({ type: 'text', text: '[document]' })\n continue\n }\n if (block.type === 'tool_result' && Array.isArray(block.output)) {\n const flat = stripAttachmentsFromToolResult(block.output)\n if (flat) {\n touched = true\n nextContent.push({ ...block, output: flat })\n continue\n }\n }\n nextContent.push(block)\n }\n return touched ? { ...turn, content: nextContent } : turn\n}\n\n/**\n * Return a fresh `ToolResultContent[]` with images flattened to `[image]`\n * text placeholders, or `null` when no image blocks were present (caller\n * keeps the original input). Returning a new mutable array — never the\n * input — keeps the `tool_result.output` slot's mutable type contract\n * satisfied without leaky `as`-casts at the call site.\n */\nfunction stripAttachmentsFromToolResult(parts: readonly ToolResultContent[]): ToolResultContent[] | null {\n let touched = false\n const out: ToolResultContent[] = []\n for (const part of parts) {\n if (part.type === 'image') {\n touched = true\n out.push({ type: 'text', text: '[image]' })\n }\n else if (part.type === 'audio') {\n touched = true\n out.push({ type: 'text', text: '[audio]' })\n }\n else if (part.type === 'video') {\n touched = true\n out.push({ type: 'text', text: '[video]' })\n }\n else if (part.type === 'document') {\n touched = true\n out.push({ type: 'text', text: '[document]' })\n }\n else {\n out.push(part)\n }\n }\n return touched ? out : null\n}\n\n/**\n * Drop the oldest \"round\" from `turns` and return a fresh array. Used by\n * the PTL retry path to shrink the prompt one round at a time.\n *\n * A round is a contiguous `[user, assistant?, tool_results?]` group. The\n * function walks forward from index 0, advances through the user turn\n * and any trailing assistant + tool-result turns belonging to the same\n * exchange, and returns the remainder.\n *\n * Adjacency-safe: when the oldest user turn carries `tool_result` blocks\n * answering an assistant turn ahead of it (rare — happens during\n * resume), the function keeps walking until the next clean boundary so\n * the resulting array still respects every provider's `tool_use ↔\n * tool_result` adjacency rule.\n *\n * Returns `turns` unchanged when only one round (or less) remains — the\n * caller is expected to interpret that as \"cannot shrink further\" and\n * give up the retry loop.\n */\nexport function truncateHeadForPtlRetry(turns: readonly SessionTurn[]): SessionTurn[] {\n if (turns.length <= 1)\n return turns.slice()\n\n // Find the first turn that opens a clean conversational round we can\n // drop ending on — start with the first `user` turn (it's the natural\n // start-of-round marker).\n const firstUserIdx = turns.findIndex(t => t.role === 'user')\n if (firstUserIdx < 0)\n return turns.slice()\n\n // Skip past every turn that belongs to this round: the user turn\n // itself, the assistant reply (if any), and the next user turn whose\n // content is *only* tool_result blocks (an immediate follow-up that\n // closes out tool calls). Stop when we reach the next round-opening\n // user turn (i.e. one that carries non-tool_result content).\n let cursor = firstUserIdx + 1\n while (cursor < turns.length) {\n const turn = turns[cursor]\n if (turn.role === 'assistant') {\n cursor++\n continue\n }\n if (turn.role === 'user' && isToolResultsOnlyTurn(turn)) {\n cursor++\n continue\n }\n break\n }\n\n // If the cursor walked all the way to the end, refuse to truncate —\n // the remaining slice would be empty, which is never useful.\n if (cursor >= turns.length)\n return turns.slice()\n\n return turns.slice(cursor)\n}\n\nfunction isToolResultsOnlyTurn(turn: SessionTurn): boolean {\n if (turn.content.length === 0)\n return false\n return turn.content.every(block => block.type === 'tool_result')\n}\n\n/**\n * Walk `proposedCut` backward to the nearest position where splitting at\n * that index produces a round-boundary-clean partition — i.e. neither\n * half breaks the `tool_use ↔ tool_result` adjacency rule that every\n * provider (most strictly Anthropic) enforces.\n *\n * The hazardous case: the proposed cut lands BETWEEN an assistant turn\n * carrying `tool_call` blocks and the user turn carrying the matching\n * `tool_result` blocks. Then `toSummarize` ends with an orphan\n * `tool_use` (provider 400 on the summarization request) AND `preserved`\n * starts with an orphan `tool_result` (provider 400 on the next live\n * agent run against the wire-level cutoff output).\n *\n * Algorithm: keep walking `cut` backward as long as `turns[cut - 1]`\n * is an assistant turn with at least one `tool_call` block. The walk\n * stops when:\n * - we reach `cut = 0` (slice would be empty; caller's existing\n * \"scope resolved to zero turns\" guard handles it), or\n * - the trailing turn is user-role (clean — model emits no pending\n * tool_use from user turns), or\n * - the trailing turn is assistant text without tool_use (clean —\n * text-only response is a complete round).\n *\n * Returning the adjusted cut over-preserves the tail relative to the\n * caller's request — `keepTurns` is interpreted as the MINIMUM number\n * of turns kept verbatim, not the exact count.\n */\nfunction findSafeRoundBoundary(turns: readonly SessionTurn[], proposedCut: number): number {\n let cut = Math.max(0, Math.min(turns.length, proposedCut))\n while (cut > 0 && hasPendingToolUse(turns[cut - 1]))\n cut--\n return cut\n}\n\n/** Does this turn end with any unanswered `tool_use` blocks? */\nfunction hasPendingToolUse(turn: SessionTurn): boolean {\n if (turn.role !== 'assistant')\n return false\n for (const block of turn.content) {\n if (block.type === 'tool_call')\n return true\n }\n return false\n}\n\nfunction hasTextBearingContent(turns: readonly SessionTurn[]): boolean {\n for (const turn of turns) {\n if (turn.role === 'system')\n continue\n for (const block of turn.content) {\n if (block.type === 'text' && block.text.trim().length > 0)\n return true\n if (block.type === 'tool_call' || block.type === 'tool_result')\n return true\n }\n }\n return false\n}\n\n// ---------------------------------------------------------------------------\n// Summary turn builder\n// ---------------------------------------------------------------------------\n\n/**\n * Maximum length of an anchor turn's textual preview, in characters. Long\n * enough to give the model recognizable context (the first paragraph of\n * a typical user message), short enough that it doesn't blow the\n * cache-stability invariant for the prompt prefix.\n */\nexport const ANCHOR_PREVIEW_MAX_CHARS = 200\n\n/**\n * Extract the first ~200 chars of text-bearing content from a turn — the\n * preview surfaced in `from` / `up_to` direction prompts so the model\n * knows where the slice begins.\n */\nexport function anchorPreviewFor(turn: SessionTurn): string {\n for (const block of turn.content) {\n if (block.type === 'text' && block.text.trim().length > 0) {\n const flat = block.text.replace(/\\s+/g, ' ').trim()\n return flat.length > ANCHOR_PREVIEW_MAX_CHARS\n ? `${flat.slice(0, ANCHOR_PREVIEW_MAX_CHARS - 1)}…`\n : flat\n }\n }\n return '(no preview available)'\n}\n\n/**\n * Input shape for {@link summaryToTurn}. Designed to align with the\n * fields of `CompactResult` so a caller can spread the runner's output\n * with a single `replacesTurnIds` rename — no field-by-field unpacking\n * required.\n *\n * Primitive shape (no `CompactResult` import) keeps this module free of\n * dependencies on the runner — `compact.ts` imports from here, not the\n * other way around.\n */\nexport interface SummaryToTurnInput {\n /** Summary text — typically `CompactResult.summary`. */\n summary: string\n /** Turn ids being replaced — typically `CompactResult.summarizedTurnIds`. */\n replacesTurnIds: readonly string[]\n /** Model id that produced the summary. */\n model: string\n /** Token usage from the summary call. */\n usage: TurnUsage\n /** Defaults to `Date.now()` when omitted. */\n compactedAt?: number\n /**\n * Turn id. Defaults to `crypto.randomUUID()`. Durable-execution callers\n * (Restate) pass a journaled id (`clock.randomUUID()`) so the marker turn\n * is byte-identical on replay; pair it with a journaled {@link compactedAt}.\n */\n id?: string\n}\n\n/**\n * Build a synthetic `SessionTurn` carrying a single `compact-summary`\n * block, ready to append to a session.\n *\n * The turn's role is `'user'` so it sits at a conversational boundary\n * the way the model expects. The caller is responsible for\n * `session.appendTurns([turn])`. The id defaults to `crypto.randomUUID()`\n * (override via `input.id` for durable-execution replay stability).\n *\n * Typical use after running `compactConversation`:\n *\n * ```ts\n * const result = await compactConversation({ provider, turns })\n * const turn = summaryToTurn({\n * summary: result.summary,\n * replacesTurnIds: result.summarizedTurnIds,\n * model: result.model,\n * usage: result.usage,\n * })\n * await session.appendTurns([turn])\n * ```\n */\nexport function summaryToTurn(input: SummaryToTurnInput): SessionTurn {\n const compactedAt = input.compactedAt ?? Date.now()\n return {\n id: input.id ?? crypto.randomUUID(),\n role: 'user',\n content: [{\n type: 'compact-summary',\n replacesTurnIds: input.replacesTurnIds,\n summary: input.summary,\n model: input.model,\n usage: input.usage,\n compactedAt,\n }],\n createdAt: compactedAt,\n }\n}\n","/**\n * Pure prompt builders for conversation compaction.\n *\n * The builders produce the **system prompt** for a no-tools summary call.\n * They are total functions of `(direction, anchorPreview?)` and produce\n * byte-stable output for the same inputs — that's load-bearing for the\n * provider's prompt cache: repeated compactions in the same host process\n * share the same prefix and read cache instead of writing it.\n *\n * Inspired by Claude Code's `services/compact/prompt.ts` — same 9-section\n * scaffold, same `<analysis> + <summary>` envelope, same no-tools\n * preamble. Adapted to zidane-specific wording (no \"Claude\" references)\n * and trimmed of the bits that don't apply (no `marble_origami`-style\n * query sources, no compaction-fingerprint header).\n */\n\n/** Identifier for the section of the conversation being summarized. */\nexport type CompactDirection = 'full' | 'tail' | 'from' | 'up_to'\n\nexport interface CompactPromptOptions {\n direction: CompactDirection\n /**\n * Short preview of the anchor turn's text — only used by `'from'` and\n * `'up_to'`. Pass the last ~200 chars of the anchor turn so the model\n * has a recognizable handle on where the slice begins / ends. Empty /\n * undefined for `'full'` and `'tail'`.\n */\n anchorPreview?: string\n}\n\n/**\n * Function shape for callers that want to swap in a domain-specific\n * summary prompt (security review handoff, support-ticket continuation,\n * etc.) without touching the runner. Default: {@link buildCompactPrompt}.\n */\nexport type CompactPromptBuilder = (opts: CompactPromptOptions) => string\n\n// ---------------------------------------------------------------------------\n// Composable blocks — each one is a frozen string for cache-stability and\n// quoted verbatim from inside the named builders below. Exported so\n// callers building custom prompts can stitch them together.\n// ---------------------------------------------------------------------------\n\n/**\n * No-tools guard. The runner sends `tools: []` to the provider already,\n * but some models still hallucinate tool-call intent on a long\n * conversation. The prose guard is cheap insurance.\n */\nexport const NO_TOOLS_PREAMBLE = `CRITICAL: Respond with TEXT ONLY. Do NOT call any tools.\n\n- Do NOT use Read, Bash, Grep, Glob, Edit, Write, or ANY other tool.\n- You already have all the context you need in the conversation above.\n- Tool calls will be REJECTED and will waste your only turn — you will fail the task.\n- Your entire response must be plain text: an <analysis> block followed by a <summary> block.`\n\n/**\n * Body shared by every direction. Lays out the 9-section scaffold,\n * mirrors Claude Code's `BASE_COMPACT_PROMPT` so a model already trained\n * on the layout produces the same shape.\n */\nexport const BASE_INSTRUCTIONS = `Your task is to create a detailed summary of the conversation so far, paying close attention to the user's explicit requests and your previous actions.\n\nThis summary should be thorough in capturing technical details, code patterns, and architectural decisions that would be essential for continuing development work without losing context.\n\nBefore providing your final summary, wrap your analysis in <analysis> tags to organize your thoughts and ensure you've covered all necessary points. In your analysis process:\n\n1. Chronologically analyze each message and section of the conversation. For each section thoroughly identify:\n - The user's explicit requests and intents\n - Your approach to addressing the user's requests\n - Key decisions, technical concepts and code patterns\n - Specific details like file names, full code snippets, function signatures, file edits\n - Errors that you ran into and how you fixed them\n - Pay special attention to specific user feedback that you received, especially if the user told you to do something differently.\n2. Double-check for technical accuracy and completeness.\n\nYour summary, wrapped in <summary> tags, must include the following sections:\n\n1. Primary Request and Intent\n2. Key Technical Concepts\n3. Files and Code Sections (with paths; include code snippets only when load-bearing)\n4. Errors and fixes\n5. Problem Solving\n6. All user messages (list ALL non-tool user messages, verbatim)\n7. Pending Tasks\n8. Current Work\n9. Optional Next Step (include direct quotes from the most recent conversation when relevant)`\n\n/** Trailer prompting the model to begin. Same on every direction. */\nexport const TRAILER = 'Provide your <analysis> and <summary> now.'\n\n// ---------------------------------------------------------------------------\n// Direction blurbs — the only knob that varies between builders. Each is\n// frozen and concise so cache-key drift is impossible between calls with\n// the same direction.\n// ---------------------------------------------------------------------------\n\nconst FULL_BLURB = `## Scope\nThe conversation above is being summarized in full. Capture every section the user might need to resume from a fresh context.`\n\nconst TAIL_BLURB = `## Scope\nSummarize the conversation above. The most recent turns will be preserved verbatim alongside your summary, so prioritize older context that would otherwise be lost.`\n\nconst FROM_BLURB = `## Scope\nSummarize the conversation FROM the marked anchor onward (the recent portion). Everything before the anchor will be preserved verbatim.\n\nAnchor turn (preview):\n%ANCHOR_PREVIEW%`\n\nconst UP_TO_BLURB = `## Scope\nSummarize the conversation UP TO the marked anchor (the older portion). Everything from the anchor onward will be preserved verbatim — your summary's job is to compress the prior context the user can no longer scroll back to.\n\nAnchor turn (preview):\n%ANCHOR_PREVIEW%`\n\n// ---------------------------------------------------------------------------\n// Named builders — discoverable, single-purpose, cache-stable.\n// ---------------------------------------------------------------------------\n\n/** Compose the full prompt with a custom direction-blurb. Internal helper. */\nfunction compose(blurb: string): string {\n return [NO_TOOLS_PREAMBLE, BASE_INSTRUCTIONS, blurb, TRAILER].join('\\n\\n')\n}\n\nexport function buildFullCompactPrompt(): string {\n return compose(FULL_BLURB)\n}\n\nexport function buildTailCompactPrompt(): string {\n return compose(TAIL_BLURB)\n}\n\nexport function buildFromCompactPrompt(anchorPreview: string): string {\n return compose(FROM_BLURB.replace('%ANCHOR_PREVIEW%', anchorPreview))\n}\n\nexport function buildUpToCompactPrompt(anchorPreview: string): string {\n return compose(UP_TO_BLURB.replace('%ANCHOR_PREVIEW%', anchorPreview))\n}\n\n/**\n * Default public builder. Dispatches by direction to the four named\n * builders. Throws when `from` / `up_to` are passed without an\n * `anchorPreview` — those scopes only make sense with an anchor and a\n * silent fallback would produce a prompt that doesn't tell the model\n * where the slice begins.\n */\nexport const buildCompactPrompt: CompactPromptBuilder = (opts) => {\n switch (opts.direction) {\n case 'full':\n return buildFullCompactPrompt()\n case 'tail':\n return buildTailCompactPrompt()\n case 'from': {\n const preview = opts.anchorPreview ?? ''\n if (preview.length === 0)\n throw new Error('buildCompactPrompt: `anchorPreview` is required for direction \"from\".')\n return buildFromCompactPrompt(preview)\n }\n case 'up_to': {\n const preview = opts.anchorPreview ?? ''\n if (preview.length === 0)\n throw new Error('buildCompactPrompt: `anchorPreview` is required for direction \"up_to\".')\n return buildUpToCompactPrompt(preview)\n }\n }\n}\n","/**\n * `compactConversation` — drive a one-shot summary call against a\n * provider and return a structured envelope describing what was\n * summarized and what stays verbatim.\n *\n * This is the harness primitive. It does not mutate the session, does\n * not own re-entrancy guards, does not enforce a circuit breaker — those\n * belong to whoever wires compaction into a control loop (the agent\n * loop's autocompact trigger, the TUI's session-details modal action,\n * an SDK consumer's batch job).\n *\n * Architecture: mirrors {@link generateSessionTitle} verbatim. The\n * provider's `stream()` is the one-shot completion primitive zidane\n * already exposes; this module reuses it directly instead of layering\n * a second abstraction on top.\n *\n * Caching: the system prompt is byte-stable per `(direction,\n * anchorPreview)` pair (see `./prompt.ts`). Repeated compactions in the\n * same host process share that prefix and read provider cache instead\n * of writing it.\n */\n\nimport type { Provider } from '../providers'\nimport type {\n SessionContentBlock,\n SessionMessage,\n SessionTurn,\n ThinkingLevel,\n TurnUsage,\n} from '../types'\nimport type { CompactionSlice, CompactScope } from './messages'\nimport type { CompactPromptBuilder } from './prompt'\nimport { ensureEndsWithUserMessage, ensureToolResultPairing } from '../session/messages'\nimport { linkAbortSignal, resolveTimeoutMs, withTimeout } from '../timeout'\nimport { toolOutputByteLength } from '../types'\nimport { CompactPromptTooLongError } from './errors'\nimport {\n anchorPreviewFor,\n sliceForCompaction,\n stripImagesFromTurns,\n truncateHeadForPtlRetry,\n} from './messages'\nimport { buildCompactPrompt } from './prompt'\nimport { utf8ByteLength } from './utils'\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\n\nexport interface CompactOptions {\n /** Provider used for the summary call. Called with empty tools list. */\n provider: Provider\n /** Conversation to compact, in chronological order. */\n turns: readonly SessionTurn[]\n /**\n * What to summarize. Default: `'tail'` — summarize everything before\n * the last `keepTurns` turns. See {@link CompactScope}.\n */\n scope?: CompactScope\n /**\n * Trailing turns left untouched when `scope: 'tail'`. Default: 4.\n * Matches `AgentBehavior.compactKeepTurns` so a host that uses both\n * mechanisms shares one knob.\n */\n keepTurns?: number\n /** Model id used for the summary call. Default: `provider.meta.defaultModel`. */\n model?: string\n /**\n * Maximum tokens the summary itself can occupy. Default: 20_000 — the\n * p99.99 of summary output size in Claude Code's `COMPACT_MAX_OUTPUT`.\n * Reasonable for any model with a 200k+ window.\n */\n maxOutputTokens?: number\n /** Optional reasoning level. Default: `'off'` — summarization rarely benefits from thinking. */\n thinking?: ThinkingLevel\n /** Optional cancellation signal forwarded to `provider.stream()`. */\n signal?: AbortSignal\n /**\n * Watchdog for the provider summary call in milliseconds. If the provider\n * stream never settles, compaction aborts its request signal and fails\n * instead of leaving callers queued forever. Set `0` to disable.\n * Default: 120000 (2 minutes).\n */\n providerStreamTimeoutMs?: number\n /**\n * Retries with head-truncation when the provider reports\n * `prompt_too_long`. Each retry drops the oldest conversational round\n * before re-issuing the call. Default: 3. Set 0 to fail-fast.\n */\n maxPtlRetries?: number\n /**\n * Optional builder override. The default builder dispatches by\n * direction to the named builders in `./prompt.ts`; pass a custom\n * function to swap in a domain-specific summary prompt.\n */\n prompt?: CompactPromptBuilder\n /**\n * Lifecycle hook for observability. Fires once per provider call,\n * including retries. The `kind` field tells you whether the call is\n * the initial attempt, a head-truncated retry, or a transient-error\n * retry — useful for surfacing progress in a UI spinner.\n */\n onAttempt?: (event: { attempt: number, kind: 'initial' | 'ptl-retry' | 'transient-retry' }) => void\n /**\n * Lifecycle hook fired after preflight succeeds and before the first\n * provider call starts. Useful for UI state (\"Compacting...\") and telemetry.\n * Synchronous by design so observability cannot hang compaction.\n */\n onStart?: (event: CompactStartEvent) => void\n /**\n * Lifecycle hook fired exactly once after {@link onStart}, on both success\n * and failure. Synchronous by design so observability cannot hang compaction.\n */\n onEnd?: (event: CompactEndEvent) => void\n}\n\nexport interface CompactStartEvent {\n model: string\n scope: CompactScope\n summarizedTurnIds: readonly string[]\n preservedTurnIds: readonly string[]\n}\n\nexport type CompactEndEvent\n = | {\n status: 'success'\n durationMs: number\n result: CompactResult\n }\n | {\n status: 'error'\n durationMs: number\n error: unknown\n }\n\nfunction fireCompactLifecycle(fn: (() => void) | undefined): void {\n if (!fn)\n return\n try {\n fn()\n }\n catch (err) {\n if (process.env.ZIDANE_DEBUG)\n console.error('[zidane] compact lifecycle hook failed:', err)\n }\n}\n\n/**\n * Directive appended as the trailing user message when the conversation\n * to summarize ends with an assistant turn. Mirrors the system-prompt\n * TRAILER text so the model gets the same imperative cue from both\n * surfaces — system instruction + final user message.\n *\n * Compaction-specific: paired with the compaction system prompt. Other\n * call-sites of {@link ensureEndsWithUserMessage} pass their own directive\n * (or the neutral `DEFAULT_USER_TAIL_DIRECTIVE`) so unrelated codepaths\n * don't inject a \"give me a summary\" cue into a regular turn.\n */\nconst SUMMARY_USER_DIRECTIVE = 'Provide your <analysis> and <summary> now.'\n\nexport interface CompactResult {\n /** The summary text, with any `<analysis>` block stripped. */\n summary: string\n /** Token usage from the (last successful) summary call. */\n usage: TurnUsage\n /** Model id used to produce the summary. */\n model: string\n /** Number of `prompt_too_long` retries that fired before success. 0 on first-try success. */\n ptlRetries: number\n /**\n * Turn ids actually covered by the summary — i.e., the ids of turns\n * that made it to the provider. **PTL-retry safe**: when head-truncation\n * shrank the scope, only the surviving (post-truncation) turn ids\n * appear here. Drives `summaryToTurn`'s `replacesTurnIds`; the wire-\n * level cutoff in `applyCompactSummaryCutoff` reads the same field\n * from the persisted marker.\n */\n summarizedTurnIds: readonly string[]\n /**\n * Turn ids dropped by PTL head-truncation before reaching the\n * provider. **Empty on first-try success.** These turns are NOT\n * covered by the summary — callers should either leave them in the\n * conversation history (they stay visible to the model verbatim,\n * since the id-based cutoff only elides ids the marker explicitly\n * claims) or surface a \"compaction lost N turns of context\" warning.\n *\n * The harness keeps these in the wire-level conversation by default;\n * the safety contract is \"the summary describes exactly what's in\n * `summarizedTurnIds` — nothing more, nothing less\".\n */\n droppedDueToPtl: readonly string[]\n /** Turns left untouched (the preserved tail / verbatim slice). */\n preservedTurns: readonly SessionTurn[]\n /** Byte length of turns actually summarized (post-PTL-truncation). */\n beforeBytes: number\n /** UTF-8 byte length of the summary text (rough \"after\" measure). */\n afterBytes: number\n}\n\nexport { CompactInvalidInputError, CompactPromptTooLongError } from './errors'\nexport type { CompactionSlice, CompactScope, SummaryToTurnInput } from './messages'\nexport type { CompactDirection, CompactPromptBuilder, CompactPromptOptions } from './prompt'\n\n// ---------------------------------------------------------------------------\n// Defaults — locked constants so behavior is predictable across host setups.\n// ---------------------------------------------------------------------------\n\n/** Default `keepTurns` for `scope: 'tail'`. Matches `AgentBehavior.compactKeepTurns`. */\nconst DEFAULT_KEEP_TURNS = 4\nconst DEFAULT_COMPACT_STREAM_TIMEOUT_MS = 2 * 60 * 1000\n\n/** Default max output tokens for the summary call. */\nconst DEFAULT_MAX_OUTPUT_TOKENS = 20_000\n\n/** Default PTL retry budget. */\nconst DEFAULT_MAX_PTL_RETRIES = 3\n\n/** Maximum transient-error retries before giving up. Independent of PTL. */\nconst TRANSIENT_RETRY_BUDGET = 2\n\n// ---------------------------------------------------------------------------\n// Runner\n// ---------------------------------------------------------------------------\n\nexport async function compactConversation(opts: CompactOptions): Promise<CompactResult> {\n // ---- Phase 1: pure preflight (no API call) -----------------------------\n // Throws CompactInvalidInputError on degenerate inputs so the caller can\n // bail without spending a network round-trip on a doomed request.\n const slice = sliceForCompaction(\n opts.turns,\n opts.scope ?? 'tail',\n opts.keepTurns ?? DEFAULT_KEEP_TURNS,\n )\n\n const scope = opts.scope ?? 'tail'\n const direction = scopeToDirection(scope)\n const anchorPreview = direction === 'from' || direction === 'up_to'\n ? anchorPreviewFor(anchorTurnFor(slice, opts.scope!))\n : undefined\n\n const builder = opts.prompt ?? buildCompactPrompt\n const systemPrompt = builder({\n direction,\n ...(anchorPreview !== undefined ? { anchorPreview } : {}),\n })\n\n const model = opts.model ?? opts.provider.meta.defaultModel\n const maxOutputTokens = opts.maxOutputTokens ?? DEFAULT_MAX_OUTPUT_TOKENS\n const maxPtlRetries = Math.max(0, opts.maxPtlRetries ?? DEFAULT_MAX_PTL_RETRIES)\n\n // ---- Phase 2: retry-aware provider call --------------------------------\n // Strips images once up-front (cheap, deterministic) so each retry\n // doesn't re-flatten the same blocks.\n let workingTurns: readonly SessionTurn[] = stripImagesFromTurns(slice.toSummarize)\n let ptlRetries = 0\n let transientRetries = 0\n let attempt = 0\n\n // Fire onStart only once prep that can throw is done, so the onStart/onEnd\n // pair stays balanced: every onStart is followed by exactly one onEnd.\n const startedAt = Date.now()\n fireCompactLifecycle(() => opts.onStart?.({\n model,\n scope,\n summarizedTurnIds: slice.toSummarize.map(t => t.id),\n preservedTurnIds: slice.preserved.map(t => t.id),\n }))\n\n try {\n while (true) {\n attempt++\n const kind: 'initial' | 'ptl-retry' | 'transient-retry'\n = ptlRetries > 0 ? 'ptl-retry' : transientRetries > 0 ? 'transient-retry' : 'initial'\n opts.onAttempt?.({ attempt, kind })\n\n try {\n // Build wire messages, then run the same defensive pairing pass\n // the agent loop uses on every live turn (see `applyPairingRepair`\n // in `src/loop.ts`). A session can contain orphan `tool_use` blocks\n // even after `sliceForCompaction`'s round-boundary walk: PTL\n // head-truncation shrinks the working set after the first attempt\n // (which can lop the head off an in-flight round), prior-loop\n // crashes can leave dangling tool_use ids in the persisted history,\n // and `turnsToMessages` itself can drop the only `text` block on\n // an assistant turn carrying signed thinking that has no signature.\n // Without this pass the summarizer fires a request the provider 400s\n // on with `'tool_use' ids were found without 'tool_result' blocks\n // immediately after`, which surfaces to the user as\n // \"compaction failed\" with a raw provider message.\n const paired = ensureToolResultPairing(turnsToMessages(workingTurns))\n const messages = ensureEndsWithUserMessage(paired, opts.provider, SUMMARY_USER_DIRECTIVE)\n const { summary, usage } = await runOnce({\n provider: opts.provider,\n model,\n system: systemPrompt,\n messages,\n maxTokens: maxOutputTokens,\n ...(opts.thinking !== undefined ? { thinking: opts.thinking } : {}),\n ...(opts.signal !== undefined ? { signal: opts.signal } : {}),\n ...(opts.providerStreamTimeoutMs !== undefined ? { providerStreamTimeoutMs: opts.providerStreamTimeoutMs } : {}),\n })\n\n // PTL retries shrink the scope — the marker should ONLY claim\n // ownership of turns that actually fed the summary. Compute the\n // dropped set up-front (cheap; turn counts are O(100s) at worst)\n // so callers can decide whether to warn the user.\n const workingIds = new Set(workingTurns.map(t => t.id))\n const droppedDueToPtl: string[] = []\n if (ptlRetries > 0) {\n for (const t of slice.toSummarize) {\n if (!workingIds.has(t.id))\n droppedDueToPtl.push(t.id)\n }\n }\n\n const result: CompactResult = {\n summary,\n usage: { ...usage, modelId: usage.modelId ?? model },\n model,\n ptlRetries,\n summarizedTurnIds: workingTurns.map(t => t.id),\n droppedDueToPtl,\n preservedTurns: slice.preserved,\n beforeBytes: bytesIn(workingTurns),\n afterBytes: utf8ByteLength(summary),\n }\n fireCompactLifecycle(() => opts.onEnd?.({\n status: 'success',\n durationMs: Date.now() - startedAt,\n result,\n }))\n return result\n }\n catch (err) {\n // Abort flows propagate unchanged so callers can pattern-match.\n if (isAbortError(err, opts.signal))\n throw err\n\n if (isContextOverflowError(err, opts.provider)) {\n if (ptlRetries >= maxPtlRetries) {\n throw new CompactPromptTooLongError(\n `Compaction failed: prompt_too_long after ${ptlRetries} retries.`,\n ptlRetries,\n )\n }\n const truncated = truncateHeadForPtlRetry(workingTurns)\n if (truncated.length === workingTurns.length) {\n // Nothing more to truncate — refuse to spin.\n throw new CompactPromptTooLongError(\n `Compaction failed: prompt_too_long and conversation cannot be shrunk further.`,\n ptlRetries,\n )\n }\n workingTurns = truncated\n ptlRetries++\n continue\n }\n\n if (isTransientError(err) && transientRetries < TRANSIENT_RETRY_BUDGET) {\n transientRetries++\n continue\n }\n\n throw err\n }\n }\n }\n catch (err) {\n fireCompactLifecycle(() => opts.onEnd?.({\n status: 'error',\n durationMs: Date.now() - startedAt,\n error: err,\n }))\n throw err\n }\n}\n\n// ---------------------------------------------------------------------------\n// Internals\n// ---------------------------------------------------------------------------\n\ninterface RunOnceOptions {\n provider: Provider\n model: string\n system: string\n messages: SessionMessage[]\n maxTokens: number\n thinking?: ThinkingLevel\n signal?: AbortSignal\n providerStreamTimeoutMs?: number\n}\n\ninterface RunOnceResult {\n summary: string\n usage: TurnUsage\n}\n\n/**\n * Single provider call. Strips the `<analysis>` block and returns the\n * post-trim summary text along with the usage report.\n *\n * Why not just use `result.text`: some providers stream deltas without\n * emitting a concatenated `text` field at the end (or set it to an\n * empty string when the stream finished via tool-call). Accumulating\n * deltas in `text` and falling back to `result.text` mirrors\n * `generateSessionTitle` and handles every adapter shape.\n */\nasync function runOnce(opts: RunOnceOptions): Promise<RunOnceResult> {\n let streamed = ''\n const abort = new AbortController()\n const unlink = opts.signal ? linkAbortSignal(opts.signal, abort) : () => {}\n const timeoutMs = resolveTimeoutMs(opts.providerStreamTimeoutMs, DEFAULT_COMPACT_STREAM_TIMEOUT_MS)\n let abandoned = false\n let streamPromise\n try {\n streamPromise = opts.provider.stream(\n {\n model: opts.model,\n system: opts.system,\n tools: [],\n messages: opts.messages,\n maxTokens: opts.maxTokens,\n ...(opts.thinking !== undefined ? { thinking: opts.thinking } : {}),\n signal: abort.signal,\n },\n {\n onText: (delta) => {\n if (!abandoned)\n streamed += delta\n },\n },\n )\n }\n catch (err) {\n unlink()\n throw err\n }\n streamPromise.catch(() => {})\n\n let result\n try {\n result = await withTimeout(streamPromise, {\n operation: 'compact provider stream',\n timeoutMs,\n onTimeout: () => {\n abandoned = true\n abort.abort('compact-provider-timeout')\n },\n message: `Compaction provider stream timed out after ${timeoutMs}ms.`,\n })\n }\n finally {\n unlink()\n }\n\n const raw = streamed.length > 0 ? streamed : result.text\n let summary = stripAnalysisBlock(raw).trim()\n\n // Reasoning-model fallback. Some models (Qwen-thinking, GLM, DeepSeek\n // reasoning variants, …) write the entire response into their\n // reasoning channel rather than `delta.content`. From this layer the\n // `text` stream is empty, but the assistant message carries\n // `{ type: 'thinking', text }` blocks containing the actual summary\n // (often still wrapped in `<analysis>` / `<summary>` tags from the\n // prompt). Walk those blocks and apply the same extraction waterfall\n // before giving up. Saves the user from \"model misbehaved, retry\" on\n // every compaction when their selected model happens to reason\n // instead of speak.\n if (summary.length === 0) {\n const thinking = collectThinkingText(result.assistantMessage)\n if (thinking.length > 0)\n summary = stripAnalysisBlock(thinking).trim()\n }\n\n if (summary.length === 0) {\n // Truly empty — aborted stream, tool-call-only turn, or a model\n // that refused to summarize. `stripAnalysisBlock` already fell back\n // to raw `<analysis>` content when the model wrapped everything in\n // the wrong tag; the thinking fallback above caught the reasoning-\n // channel case. Reaching this branch means there's nothing usable\n // anywhere in the response.\n throw new Error('Compaction failed: provider returned no summary text.')\n }\n return { summary, usage: result.usage }\n}\n\n/**\n * Concatenate every `thinking`-block's text from an assistant message.\n *\n * Reasoning models route their output through provider-specific\n * \"thinking\" channels (Anthropic native thinking, OpenAI o-series\n * reasoning, OpenAI-compat `reasoning_content`, OpenRouter\n * `reasoning_details`). All of them surface as `{ type: 'thinking', text }`\n * blocks on the normalized `SessionMessage.content` by the time the\n * provider's `stream()` returns. Walking the blocks is provider-agnostic\n * — works for every adapter without bespoke per-provider handling.\n *\n * Returns the empty string when no thinking blocks are present, so the\n * caller's \"did we get anything?\" check stays a single `.length === 0`.\n */\nfunction collectThinkingText(message: SessionMessage): string {\n const parts: string[] = []\n for (const block of message.content) {\n if (block.type === 'thinking' && typeof block.text === 'string' && block.text.length > 0)\n parts.push(block.text)\n }\n return parts.join('\\n')\n}\n\n/**\n * Extract the summary text from the provider's response, peeling off\n * any envelope the model wrapped it in.\n *\n * The compact prompt asks for `<analysis>...</analysis><summary>...</summary>`\n * but real models drift from the format. This function tries four\n * extraction paths in order of strictness:\n *\n * 1. **Strict path** — strip `<analysis>...</analysis>` blocks and\n * extract the `<summary>...</summary>` envelope. Matches the\n * prompt-following ideal.\n * 2. **Loose path** — same strip, but accept whatever's outside the\n * `<analysis>` tags as the summary (no `<summary>` envelope\n * required). Handles models that drop the wrapper but keep the\n * analysis.\n * 3. **Analysis-as-summary fallback** — when the entire response is a\n * single `<analysis>` block (model conflated the two concepts),\n * return the analysis content. Better than failing the compaction\n * and forcing the user to retry.\n * 4. **Raw passthrough** — no recognized envelope. Return the text\n * as-is and let `runOnce`'s empty-check decide whether to throw.\n *\n * Matches Claude Code's `formatCompactSummary` for path (1) + (2) and\n * adds (3) as a graceful-degradation layer we ran into in the wild\n * (smaller / non-Anthropic models sometimes produce analysis-only).\n */\nfunction stripAnalysisBlock(text: string): string {\n // Strict + loose: strip `<analysis>...</analysis>` non-greedy + dot-all\n // (no `s` flag in Bun's V8 — `[\\s\\S]` does the same).\n const analysisStripped = text.replace(/<analysis>[\\s\\S]*?<\\/analysis>/g, '')\n\n // Path 1: explicit `<summary>...</summary>` envelope.\n const summaryMatch = analysisStripped.match(/<summary>([\\s\\S]*?)<\\/summary>/)\n if (summaryMatch)\n return summaryMatch[1]\n\n // Path 2: anything outside the `<analysis>` block.\n if (analysisStripped.trim().length > 0)\n return analysisStripped\n\n // Path 3: response was nothing but an `<analysis>` block — extract\n // its content and use that as the summary. The model meant to give\n // us a summary; it just wrapped it in the wrong tag.\n const analysisMatch = text.match(/<analysis>([\\s\\S]*?)<\\/analysis>/)\n if (analysisMatch)\n return analysisMatch[1]\n\n // Path 4: no envelope of any shape — return verbatim.\n return text\n}\n\n/**\n * Convert turns into the wire-level `SessionMessage[]` shape. Drops\n * system turns (rare; they'd confuse a summary call), and inlines any\n * pre-existing `compact-summary` markers as plain text so the\n * provider's wire converter doesn't have to know about zidane's\n * internal block type.\n *\n * Inlining (instead of dropping) is intentional: a session already\n * compacted once still contains the prior summary as load-bearing\n * context. Surfacing it as `[Previous compaction summary]\\n…` lets the\n * new summarization integrate it instead of forgetting it.\n */\nfunction turnsToMessages(turns: readonly SessionTurn[]): SessionMessage[] {\n const out: SessionMessage[] = []\n for (const turn of turns) {\n if (turn.role === 'system')\n continue\n const content: SessionContentBlock[] = []\n for (const block of turn.content) {\n if (block.type === 'compact-summary') {\n content.push({\n type: 'text',\n text: `[Previous compaction summary]\\n${block.summary}`,\n })\n continue\n }\n // Strip `thinking` blocks. They're the model's internal reasoning,\n // signed by the producing provider (`signatureProducer: 'anthropic'`),\n // and only valid in a follow-up request when that same request has\n // extended thinking enabled. The compaction call deliberately runs\n // with `thinking: undefined` (summarization rarely benefits from\n // reasoning, and we don't want to pay the budget), so re-sending\n // thinking blocks puts Anthropic in an inconsistent state: it\n // accepts the request without 400-ing but silently returns an\n // empty `text` response. The user-visible symptom is \"Compaction\n // failed: provider returned no summary text\" on sessions where\n // any turn used extended thinking. Thinking is opaque-by-design;\n // dropping it for the summary call doesn't lose conversational\n // content (the model's visible text is in adjacent `text` blocks).\n if (block.type === 'thinking')\n continue\n content.push(block)\n }\n if (content.length === 0)\n continue\n out.push({ role: turn.role, content })\n }\n return out\n}\n\nfunction scopeToDirection(scope: CompactScope): 'full' | 'tail' | 'from' | 'up_to' {\n if (scope === 'full' || scope === 'tail')\n return scope\n return scope.kind\n}\n\nfunction anchorTurnFor(slice: CompactionSlice, scope: CompactScope): SessionTurn {\n // Caller-side invariant: `sliceForCompaction` already verified the anchor\n // exists when scope.kind is set. We re-locate it here so the preview is\n // built from the exact same turn the prompt builder will reference.\n if (typeof scope === 'string')\n throw new Error('anchorTurnFor: scope must be object form')\n if (scope.kind === 'from')\n return slice.toSummarize[0]!\n return slice.toSummarize[slice.toSummarize.length - 1]!\n}\n\nfunction bytesIn(turns: readonly SessionTurn[]): number {\n let total = 0\n for (const turn of turns) {\n for (const block of turn.content) {\n if (block.type === 'text')\n total += utf8ByteLength(block.text)\n else if (block.type === 'tool_result')\n total += toolOutputByteLength(block.output)\n else if (block.type === 'tool_call')\n total += utf8ByteLength(JSON.stringify(block.input))\n else if (block.type === 'thinking')\n total += utf8ByteLength(block.text)\n }\n }\n return total\n}\n\n// ---------------------------------------------------------------------------\n// Error classification\n// ---------------------------------------------------------------------------\n\n/**\n * Whether `err` is a context-window-overflow rejection — the condition the\n * PTL head-truncation retry exists to recover from.\n *\n * Trusts the PROVIDER's own `classifyError` first: it's authored per provider\n * and knows exactly where that provider puts the overflow signal (top-level\n * `code`, nested `error.code`, HTTP `status`, message phrasing), so compaction\n * recognizes EXACTLY what the agent loop treats as `context_exceeded` — the two\n * paths can't drift. Falls back to the generic {@link isPromptTooLongError}\n * heuristic for providers that ship no classifier (or classify it as something\n * else).\n *\n * This is the fix for the failure mode where a provider surfaces overflow as\n * `code: 'context_length_exceeded'` (which the loop recovers from) but the bare\n * heuristic missed it, so the summary call failed with the raw provider error\n * instead of head-truncating and retrying.\n */\nfunction isContextOverflowError(err: unknown, provider: Provider): boolean {\n try {\n if (provider.classifyError?.(err)?.kind === 'context_exceeded')\n return true\n }\n catch {\n // A throwing classifier must not mask the overflow — fall through to the heuristic.\n }\n return isPromptTooLongError(err)\n}\n\n/**\n * Provider-agnostic predicate for the \"prompt is too long\" rejection.\n * Inspects error code, type, status, and message substring — every\n * provider names this case differently but the message is recognizable.\n */\nfunction isPromptTooLongError(err: unknown): boolean {\n if (!err || typeof err !== 'object')\n return false\n const e = err as Record<string, unknown>\n // Recognize both the `prompt_too_long` code and the structured\n // `context_length_exceeded` code some providers (OpenAI/Codex, xAI) set —\n // the latter often ships with a generic message that the message-regex below\n // wouldn't catch, so the code check is the only signal.\n if (typeof e.code === 'string' && /prompt[_ ]too[_ ]long|context[_ ]length[_ ]exceeded/i.test(e.code))\n return true\n if (typeof e.status === 'number' && (e.status === 413 || e.status === 400)) {\n const message = typeof e.message === 'string' ? e.message : ''\n if (/prompt[_ ]too[_ ]long|context[_ ]length|maximum[_ ]context|too many tokens/i.test(message))\n return true\n }\n if (typeof e.message === 'string'\n && /prompt[_ ]too[_ ]long|context[_ ]length[_ ]exceeded|context window/i.test(e.message)) {\n return true\n }\n // Anthropic SDK shape: `error.error.type === 'invalid_request_error'`\n // with a `prompt is too long` message.\n const nested = (e.error ?? {}) as Record<string, unknown>\n if (typeof nested.type === 'string' && nested.type === 'invalid_request_error') {\n const message = typeof nested.message === 'string' ? nested.message : ''\n if (/prompt[_ ]too[_ ]long|too long|context window/i.test(message))\n return true\n }\n return false\n}\n\n/**\n * Transient network / 5xx errors worth retrying once or twice.\n */\nfunction isTransientError(err: unknown): boolean {\n if (!err || typeof err !== 'object')\n return false\n const e = err as Record<string, unknown>\n if (typeof e.status === 'number' && e.status >= 500 && e.status < 600)\n return true\n if (typeof e.code === 'string' && /ECONNRESET|ETIMEDOUT|ENETUNREACH|EAI_AGAIN|fetch failed/i.test(e.code))\n return true\n if (typeof e.message === 'string'\n && /socket hang up|fetch failed|network error|terminated|ECONNRESET|read ETIMEDOUT/i.test(e.message)) {\n return true\n }\n return false\n}\n\nfunction isAbortError(err: unknown, signal: AbortSignal | undefined): boolean {\n if (signal?.aborted)\n return true\n if (!err || typeof err !== 'object')\n return false\n const e = err as Record<string, unknown>\n if (e.name === 'AbortError')\n return true\n if (typeof e.message === 'string' && /aborted/i.test(e.message))\n return true\n return false\n}\n","/**\n * Post-compact restoration — re-inject load-bearing working state as\n * synthetic tool-call/tool-result pairs after a {@link compactConversation}\n * marker lands in a session.\n *\n * Without this step, the model has a narrative summary but loses direct\n * access to the files it was actively editing and the skills it was\n * following. Restoration re-attaches the top-N recently-read files and\n * active skills so the next turn starts with full working context — no\n * forced re-reads, no degraded continuation.\n *\n * Design — synthetic tool_call/tool_result pairs:\n *\n * Two turns are appended after the compaction marker:\n *\n * [marker] ← from `summaryToTurn(result)`\n * [assistant, tool_calls × N] ← synthetic, one tool_call per item\n * [user, tool_results × N] ← synthetic, matching results\n * [new prompt] ← user's next message\n *\n * The synthetic turns look identical to what the agent would produce if it\n * had actually run `read_file` / `skills_use` — by design, because at the\n * moment of compaction those operations had just happened with that data.\n *\n * Persisted blocks use **canonical** tool names (e.g. `read_file`). The\n * agent loop's `rewriteMessagesToWire` translates them to whatever alias\n * the host configured before they reach the provider, exactly as it does\n * for real calls. Restoration therefore \"just works\" through aliasing —\n * the helper does not need to consult the alias map.\n *\n * Budgets — mirror Claude Code's `services/compact/compact.ts:122-130`:\n *\n * - 50_000 tokens total file budget, 5_000 per file, max 5 files\n * - 25_000 tokens total skill budget, 5_000 per skill (no count cap)\n *\n * Tokens are estimated at 4 chars/token — same heuristic Claude Code uses\n * for budget arithmetic. Hosts can override every limit.\n *\n * Failure modes:\n *\n * - File read fails (deleted, permissions) → skip silently. Other items\n * still proceed.\n * - No `execution` / `handle` passed → file restoration is a no-op;\n * skill restoration still works (skills carry their content inline).\n * - Empty `recentFiles` AND empty `activeSkills` → returns `{ turns: [] }`\n * so the caller's `appendTurns([summary, ...attachments])` is a no-op\n * on the attachments.\n */\n\nimport type { ExecutionContext, ExecutionHandle } from '../contexts'\nimport type { Session } from '../session'\nimport type { ActiveSkill } from '../skills/activation'\nimport type { AgentClock, SessionContentBlock, SessionTurn } from '../types'\nimport { getReadState } from '../tools/read-state'\nimport { DEFAULT_AGENT_CLOCK } from '../types'\nimport { BYTES_PER_TOKEN, estimateTokens, utf8ByteLength } from './utils'\n\n// ---------------------------------------------------------------------------\n// Defaults — match Claude Code's published constants for parity.\n// ---------------------------------------------------------------------------\n\nconst DEFAULT_FILE_TOKEN_BUDGET = 50_000\nconst DEFAULT_FILE_TOKEN_PER_FILE_CAP = 5_000\nconst DEFAULT_MAX_FILES_TO_RESTORE = 5\n\nconst DEFAULT_SKILL_TOKEN_BUDGET = 25_000\nconst DEFAULT_SKILL_TOKEN_PER_SKILL_CAP = 5_000\n\n/** Default canonical tool names — `rewriteMessagesToWire` handles aliasing. */\nconst DEFAULT_READ_FILE_TOOL_NAME = 'read_file'\nconst DEFAULT_SKILLS_USE_TOOL_NAME = 'skills_use'\n\n// ---------------------------------------------------------------------------\n// Public types\n// ---------------------------------------------------------------------------\n\n/** One file selected for restoration, with its last-read timestamp for ranking. */\nexport interface RecentFile {\n /** Path relative to the execution context's `handle.cwd`. */\n path: string\n /** Wall-clock when the file was last read, in ms. Drives recency ranking. */\n mtimeMs: number\n}\n\nexport interface PostCompactRestoreOptions {\n // ---- What to restore ---------------------------------------------------\n\n /**\n * Files to consider for restoration, ranked by recency. Typically derived\n * via {@link selectFilesFromReadState} from `getReadState(session)`.\n * The helper takes the top {@link PostCompactRestoreOptions.maxFilesToRestore}\n * by `mtimeMs` descending, fetches their current content, and synthesizes\n * `read_file` tool_call/tool_result pairs.\n */\n recentFiles?: readonly RecentFile[]\n\n /**\n * Active skills to re-inject. Typically `agent.activeSkills`. Each entry\n * yields a synthetic `skills_use` tool_call/tool_result pair carrying the\n * skill's instructions (possibly truncated to the per-skill budget).\n */\n activeSkills?: readonly ActiveSkill[]\n\n // ---- I/O ---------------------------------------------------------------\n\n /**\n * Execution context used to fetch file content. **Required for file\n * restoration.** Skills come pre-loaded (instructions live on the\n * `SkillConfig`), so they don't need the context.\n */\n execution?: ExecutionContext\n handle?: ExecutionHandle\n\n /**\n * Optional abort signal. Checked before each file read so a caller that\n * bounds restoration (e.g. a host capping its compaction barrier) can stop\n * the FS round-trips promptly instead of reading every candidate. File\n * restoration already isolates per-file failures, so an abort mid-loop just\n * returns whatever was gathered so far.\n */\n signal?: AbortSignal\n\n // ---- Tool naming -------------------------------------------------------\n\n /**\n * Canonical name of the file-read tool. Defaults to `read_file`. Aliases\n * (e.g. `Read`) are NOT specified here — the agent loop's\n * `rewriteMessagesToWire` handles wire conversion automatically.\n *\n * Override only when a host registered file-read under a different\n * canonical name (not just an alias).\n */\n readFileToolName?: string\n /**\n * Canonical name of the skill-activation tool. Defaults to `skills_use`.\n * Same aliasing semantics as `readFileToolName`.\n */\n skillsUseToolName?: string\n\n // ---- Budgets -----------------------------------------------------------\n\n /** Total token budget for file restoration. Default: 50_000. */\n fileTokenBudget?: number\n /** Per-file token cap. Default: 5_000. */\n fileTokenPerFileCap?: number\n /** Maximum file count. Default: 5. */\n maxFilesToRestore?: number\n\n /** Total token budget for skill restoration. Default: 25_000. */\n skillTokenBudget?: number\n /** Per-skill token cap. Default: 5_000. */\n skillTokenPerSkillCap?: number\n\n // ---- Filtering ---------------------------------------------------------\n\n /**\n * Paths to skip — typically the file paths already covered by the\n * compaction result's `preservedTurns`. Avoids double-injection when the\n * recent reads sit in the preserved tail.\n */\n excludePaths?: readonly string[]\n\n // ---- Synthesis ---------------------------------------------------------\n\n /**\n * Optional `runId` to tag the synthetic turns with — useful for hosts\n * that want the restoration turns to roll up under the same run as the\n * compaction marker. Defaults to undefined (orphan turns).\n */\n runId?: string\n\n /**\n * Time + UUID source for the synthesized turns' `id` / `createdAt`.\n * Defaults to {@link DEFAULT_AGENT_CLOCK} (`Date.now()` /\n * `crypto.randomUUID()`). Durable-execution callers (Restate) pass the\n * loop's journaled clock so the restoration turns are byte-identical on\n * replay. The synthetic `tool_call`/`tool_result` `callId`s are already\n * deterministic (`compact-restore-file-${i}`), so only the turn envelope\n * needs the clock.\n */\n clock?: AgentClock\n}\n\n/**\n * Envelope returned by {@link buildPostCompactAttachments}. The caller\n * spreads `turns` into `session.appendTurns([summaryTurn, ...turns])`.\n * Count fields drive UI banners (\"restored 5 files + 2 skills\").\n */\nexport interface PostCompactAttachments {\n /**\n * Two synthetic turns when at least one item was restored, otherwise\n * an empty array. The pair is `[assistant_with_tool_calls,\n * user_with_tool_results]` — adjacent and well-formed for every\n * provider's `tool_use ↔ tool_result` invariant.\n */\n turns: readonly SessionTurn[]\n /** Count of files actually restored (post-budget). */\n restoredFiles: number\n /** Count of skills actually restored (post-budget). */\n restoredSkills: number\n /** Rough total token cost of the restoration payload (sum of all content). */\n estimatedTokens: number\n}\n\n// ---------------------------------------------------------------------------\n// Pure helpers — exported for tests + caller convenience\n// ---------------------------------------------------------------------------\n\n/**\n * Convert a raw read-state map into a deduped, path-ranked list ready for\n * restoration. Multiple entries for the same path (different\n * `(offset, limit, maxBytes)` slices) collapse to one — keeping the most\n * recent `mtimeMs`.\n *\n * Filters out entries whose key doesn't share the given `cwd` prefix:\n * those came from a different execution context and can't be read back\n * through this agent's handle.\n *\n * Pure. Most callers want {@link selectFilesFromSession}, which wraps\n * `getReadState(session)` + this function in one call so the host\n * doesn't have to reach into the tools layer.\n */\nexport function selectFilesFromReadState(\n readState: ReadonlyMap<string, { mtimeMs: number }>,\n cwd: string,\n): RecentFile[] {\n const prefix = `${cwd}::`\n const byPath = new Map<string, number>()\n for (const [key, entry] of readState) {\n if (!key.startsWith(prefix))\n continue\n const path = key.slice(prefix.length)\n if (path.length === 0)\n continue\n const prior = byPath.get(path) ?? -Infinity\n if (entry.mtimeMs > prior)\n byPath.set(path, entry.mtimeMs)\n }\n return Array.from(byPath, ([path, mtimeMs]) => ({ path, mtimeMs }))\n .sort((a, b) => b.mtimeMs - a.mtimeMs)\n}\n\n/**\n * Session-aware convenience: extract recently-read files directly from\n * a {@link Session} via its per-session read-state map.\n *\n * Hosts (TUI / SDK consumers) typically have a `Session` and a `cwd`\n * (the active agent's `handle.cwd`) on hand — this wrapper saves them\n * from reaching into `src/tools/read-state.ts` directly. Returns an\n * empty list when no read state has been recorded yet (fresh session,\n * or `behavior.dedupReads === false`).\n *\n * Equivalent to:\n *\n * ```ts\n * const state = getReadState(session)\n * return state ? selectFilesFromReadState(state, cwd) : []\n * ```\n */\nexport function selectFilesFromSession(\n session: Session,\n cwd: string,\n): RecentFile[] {\n const state = getReadState(session)\n return state ? selectFilesFromReadState(state, cwd) : []\n}\n\n/**\n * Pick the top `maxFiles` from `files` (descending by `mtimeMs`),\n * dropping any whose path appears in `excludePaths`.\n *\n * Stable for equal mtimes — files with the same timestamp retain their\n * input order. Pure.\n */\nexport function selectRecentFiles(\n files: readonly RecentFile[],\n opts: { maxFiles: number, excludePaths?: readonly string[] },\n): RecentFile[] {\n const excluded = new Set(opts.excludePaths ?? [])\n const filtered = files.filter(f => !excluded.has(f.path))\n // Defensive sort — caller may pass an already-sorted list, but doing it\n // here keeps the function total regardless of input order.\n const sorted = filtered.slice().sort((a, b) => b.mtimeMs - a.mtimeMs)\n return sorted.slice(0, Math.max(0, opts.maxFiles))\n}\n\n// ---------------------------------------------------------------------------\n// Internal — content formatting + truncation\n// ---------------------------------------------------------------------------\n\ninterface FormattedFile {\n /** Tool-result content (line-numbered, with optional truncation footer). */\n body: string\n /** Whether the content was truncated to fit the per-file budget. */\n truncated: boolean\n /** Estimated token cost of `body` (post-truncation). */\n estimatedTokens: number\n}\n\ninterface FormattedSkill {\n body: string\n truncated: boolean\n estimatedTokens: number\n}\n\n/**\n * Format a file's contents to match `read_file`'s output shape — 1-indexed\n * line numbers separated by tabs, identical to what the model has seen\n * from real `read_file` calls. Applies the per-file token cap by truncating\n * at the nearest line boundary; appends a footer pointing at the next\n * offset so the model can re-read the rest if needed.\n */\nfunction formatFileForRestoration(\n content: string,\n perFileTokenCap: number,\n): FormattedFile {\n const totalBytes = utf8ByteLength(content)\n const allLines = content.split('\\n')\n const totalLines = allLines.length\n\n // Build the line-numbered body first; tokens are counted on the\n // numbered form because that's what reaches the provider.\n const numbered: string[] = []\n let runningChars = 0\n const charCap = Math.max(1, perFileTokenCap) * BYTES_PER_TOKEN\n let truncatedAt = -1\n let midLineCut = false\n\n for (let i = 0; i < allLines.length; i++) {\n const numberedLine = `${i + 1}\\t${allLines[i]}`\n const lineCharCost = numberedLine.length + (i < allLines.length - 1 ? 1 : 0) // +1 for the joining '\\n' on every line but the last\n if (runningChars + lineCharCost > charCap) {\n if (numbered.length === 0) {\n // The first line on its own already overflows (typical for\n // minified JS, CSV-on-one-line, etc.). Cut it at the char\n // boundary so we return SOMETHING useful — the head of the\n // file — instead of either failing or admitting a huge first\n // line that blows the budget. Mid-line cuts mean we can't\n // suggest a precise re-read offset; the footer says so.\n const cutTo = Math.max(1, charCap - `${i + 1}\\t`.length)\n numbered.push(`${i + 1}\\t${allLines[i].slice(0, cutTo)}`)\n midLineCut = true\n }\n truncatedAt = i\n break\n }\n numbered.push(numberedLine)\n runningChars += lineCharCost\n }\n\n const body = numbered.join('\\n')\n if (truncatedAt < 0)\n return { body, truncated: false, estimatedTokens: estimateTokens(body) }\n\n const lineLabel = midLineCut\n ? `line ${truncatedAt + 1} (mid-line)`\n : `line ${truncatedAt}`\n const offsetHint = midLineCut\n ? `mid-line cut prevents a precise offset — re-read with offset=${truncatedAt + 1} and a larger maxBytes`\n : `re-read with offset=${truncatedAt + 1} to continue`\n const footer = `\\n\\n…truncated at ${lineLabel} (post-compact restoration cap: ${perFileTokenCap} tokens). File has ${totalLines} lines, ${totalBytes} bytes total — ${offsetHint}.`\n const truncated = body + footer\n return { body: truncated, truncated: true, estimatedTokens: estimateTokens(truncated) }\n}\n\n/**\n * Format a skill's instructions for restoration. Skills are plain markdown\n * — no line numbering, no wrapping XML envelope (the model already\n * understands the format from real `skills_use` calls). Truncation cuts\n * at a line boundary when possible; appends a marker so the model knows\n * the body is incomplete.\n */\nfunction formatSkillForRestoration(\n instructions: string,\n perSkillTokenCap: number,\n): FormattedSkill {\n const charCap = Math.max(1, perSkillTokenCap) * BYTES_PER_TOKEN\n if (instructions.length <= charCap) {\n return { body: instructions, truncated: false, estimatedTokens: estimateTokens(instructions) }\n }\n // Truncate at the nearest line boundary at or before charCap so the\n // body stays markdown-parseable.\n const head = instructions.slice(0, charCap)\n const lastNewline = head.lastIndexOf('\\n')\n const cutoff = lastNewline > 0 ? lastNewline : charCap\n const truncatedBody\n = `${instructions.slice(0, cutoff).trimEnd()}\\n\\n…[truncated post-compact at ${perSkillTokenCap} tokens; full skill body lives at the skill's location]`\n return {\n body: truncatedBody,\n truncated: true,\n estimatedTokens: estimateTokens(truncatedBody),\n }\n}\n\n// ---------------------------------------------------------------------------\n// Public runner\n// ---------------------------------------------------------------------------\n\n/**\n * Build the synthetic turns to append after a `compact-summary` marker.\n *\n * Returns a `PostCompactAttachments` envelope; the caller is responsible\n * for `session.appendTurns([summaryTurn, ...result.turns])`. The two\n * synthetic turns are always emitted together (or both omitted when\n * nothing was restored) so the `tool_use ↔ tool_result` adjacency\n * invariant holds regardless of caller code path.\n *\n * Failure isolation: a single file's read failure never blocks the rest\n * of restoration — that file is skipped silently and processing\n * continues. The returned `restoredFiles` count reflects what actually\n * landed in the synthesized turns.\n */\nexport async function buildPostCompactAttachments(\n opts: PostCompactRestoreOptions,\n): Promise<PostCompactAttachments> {\n const fileTokenBudget = opts.fileTokenBudget ?? DEFAULT_FILE_TOKEN_BUDGET\n const fileTokenPerFileCap = opts.fileTokenPerFileCap ?? DEFAULT_FILE_TOKEN_PER_FILE_CAP\n const maxFilesToRestore = opts.maxFilesToRestore ?? DEFAULT_MAX_FILES_TO_RESTORE\n const skillTokenBudget = opts.skillTokenBudget ?? DEFAULT_SKILL_TOKEN_BUDGET\n const skillTokenPerSkillCap = opts.skillTokenPerSkillCap ?? DEFAULT_SKILL_TOKEN_PER_SKILL_CAP\n const readFileToolName = opts.readFileToolName ?? DEFAULT_READ_FILE_TOOL_NAME\n const skillsUseToolName = opts.skillsUseToolName ?? DEFAULT_SKILLS_USE_TOOL_NAME\n\n // ---- Phase 1: pick candidates --------------------------------------\n //\n // Tool-availability is governed at the input layer: callers control\n // which categories are restored by what they pass. To skip files,\n // pass empty `recentFiles` (or none). To skip skills, pass empty\n // `activeSkills` (or none). The runner trusts the caller to know\n // what tools the next agent run will have access to — auto-injected\n // tools (`skills_use`, MCP, interaction tools) aren't introspectable\n // from a profile config and constructing a tool-allowlist here would\n // be incomplete by design.\n const candidateFiles = opts.recentFiles && opts.recentFiles.length > 0\n ? selectRecentFiles(opts.recentFiles, {\n maxFiles: maxFilesToRestore,\n ...(opts.excludePaths ? { excludePaths: opts.excludePaths } : {}),\n })\n : []\n const candidateSkills = opts.activeSkills ?? []\n\n // ---- Phase 2: fetch + format files (with per-file + group budget) --\n const fileCalls: Array<{ callId: string, path: string, body: string, estimatedTokens: number }> = []\n let fileBudgetUsed = 0\n if (candidateFiles.length > 0 && opts.execution && opts.handle) {\n for (let i = 0; i < candidateFiles.length; i++) {\n // Stop early if the caller aborted (e.g. a bounded compaction barrier) —\n // `readFile` itself takes no signal, so this is the cancellation seam.\n if (opts.signal?.aborted)\n break\n const file = candidateFiles[i]\n let content: string\n try {\n content = await opts.execution.readFile(opts.handle, file.path)\n }\n catch {\n // File deleted / unreadable since the last read — skip silently.\n continue\n }\n const formatted = formatFileForRestoration(content, fileTokenPerFileCap)\n if (fileBudgetUsed + formatted.estimatedTokens > fileTokenBudget) {\n // Adding this file would blow the group budget. Stop here — the\n // remaining files are older anyway (candidates are pre-sorted by\n // recency descending).\n break\n }\n fileBudgetUsed += formatted.estimatedTokens\n fileCalls.push({\n callId: `compact-restore-file-${i}`,\n path: file.path,\n body: formatted.body,\n estimatedTokens: formatted.estimatedTokens,\n })\n }\n }\n\n // ---- Phase 3: format skills (no I/O — instructions are inline) -----\n const skillCalls: Array<{ callId: string, name: string, body: string, estimatedTokens: number }> = []\n let skillBudgetUsed = 0\n for (let i = 0; i < candidateSkills.length; i++) {\n const active = candidateSkills[i]\n const instructions = active.skill.instructions ?? ''\n if (instructions.trim().length === 0)\n continue\n const formatted = formatSkillForRestoration(instructions, skillTokenPerSkillCap)\n if (skillBudgetUsed + formatted.estimatedTokens > skillTokenBudget)\n break\n skillBudgetUsed += formatted.estimatedTokens\n skillCalls.push({\n callId: `compact-restore-skill-${i}`,\n name: active.skill.name,\n body: formatted.body,\n estimatedTokens: formatted.estimatedTokens,\n })\n }\n\n // ---- Phase 4: synthesize turns -------------------------------------\n if (fileCalls.length === 0 && skillCalls.length === 0) {\n return { turns: [], restoredFiles: 0, restoredSkills: 0, estimatedTokens: 0 }\n }\n\n const assistantBlocks: SessionContentBlock[] = []\n const userBlocks: SessionContentBlock[] = []\n\n for (const fc of fileCalls) {\n assistantBlocks.push({\n type: 'tool_call',\n id: fc.callId,\n name: readFileToolName,\n input: { path: fc.path },\n })\n userBlocks.push({\n type: 'tool_result',\n callId: fc.callId,\n output: fc.body,\n })\n }\n for (const sc of skillCalls) {\n assistantBlocks.push({\n type: 'tool_call',\n id: sc.callId,\n name: skillsUseToolName,\n input: { name: sc.name },\n })\n userBlocks.push({\n type: 'tool_result',\n callId: sc.callId,\n output: sc.body,\n })\n }\n\n const clock = opts.clock ?? DEFAULT_AGENT_CLOCK\n const now = await clock.now()\n const tag = opts.runId ? { runId: opts.runId } : {}\n const turns: SessionTurn[] = [\n {\n id: clock.randomUUID(),\n role: 'assistant',\n content: assistantBlocks,\n createdAt: now,\n ...tag,\n },\n {\n id: clock.randomUUID(),\n role: 'user',\n content: userBlocks,\n // +1 ms so the user turn sorts after the assistant turn under\n // tie-breaking sorts that walk by `createdAt`.\n createdAt: now + 1,\n ...tag,\n },\n ]\n\n return {\n turns,\n restoredFiles: fileCalls.length,\n restoredSkills: skillCalls.length,\n estimatedTokens: fileBudgetUsed + skillBudgetUsed,\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 routing: when the caller supplies `writeBlob` (the agent\n * loop passes `ctx.execution.writeFile(handle, …)`), the blob is written to the\n * SAME filesystem the model reads it back from (`read_file` →\n * `ctx.execution.readFile`). That makes docker / sandbox / remote / edge\n * contexts work — the spill lands where the model can re-read it — not just the\n * host process. Without `writeBlob` (direct SDK callers) the write falls back\n * to a local atomic write on the host `fs`. The byte cap, idempotent\n * \"already exists\" check, and session cleanup stay best-effort LOCAL\n * housekeeping (the `ExecutionContext` interface exposes no remote delete/stat);\n * remote contexts rely on their own ephemeral teardown.\n */\n\nimport type { SessionMessage, ToolResultContent } from './types'\nimport { readdir, rm, stat, unlink } from 'node:fs/promises'\nimport { isAbsolute, join } from 'node:path'\nimport { writeFileAtomicAsync } from './atomic-write'\nimport { utf8ByteLength } from './compact/utils'\nimport { errorMessage } from './errors'\nimport { toolOutputByteLength } from './types'\nimport { escapeXml } from './xml'\n\n/** Upper bound on the error message embedded in a persistence-failure stub. */\nconst MAX_PERSIST_ERROR_CHARS = 500\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 * Preview size for persist-on-elide stubs. Much smaller than\n * {@link PERSISTENCE_PREVIEW_BYTES}: this content is being COMPACTED OUT (it's\n * old), so the stub only needs a hint plus the recovery path, not a full\n * preview. Keeping it small stops the elided region from ballooning ~40× over\n * the lossy stub it replaces while still being recoverable by reading `path`.\n */\nconst ELIDE_PERSIST_PREVIEW_BYTES = 256\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 * Resolve the per-session MCP diagnostic directory under\n * `<userDir>/<sessionId>/mcp-warnings/`.\n *\n * Warning logs are cache/session artifacts like background task logs:\n * useful for debugging, safe to delete, and cleaned up with the session.\n */\nexport function resolveMcpWarningsDir(opts: { userDir: string, sessionId: string }): string {\n if (!isAbsolute(opts.userDir))\n throw new Error(`resolveMcpWarningsDir: userDir must be absolute, got \"${opts.userDir}\"`)\n if (!opts.sessionId)\n throw new Error('resolveMcpWarningsDir: sessionId must be a non-empty string')\n return join(opts.userDir, opts.sessionId, 'mcp-warnings')\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. Also skipped when\n * {@link writeBlob} is supplied: without remote stat/delete primitives, a\n * local host sweep would inspect the wrong filesystem. Remote contexts rely\n * on their own ephemeral teardown / storage quotas instead. Eviction failures\n * (permissions, races) are surfaced through `ZIDANE_DEBUG` but never block\n * the calling tool result; an over-cap dir is a housekeeping concern, not a\n * correctness one.\n */\n maxBytes?: number\n /**\n * Optional writer that routes the blob through a specific filesystem. The\n * agent loop passes `(path, content) => ctx.execution.writeFile(handle, …)`\n * so the persisted blob lands on the SAME filesystem the model reads it back\n * from (`read_file` → `ctx.execution.readFile`) — correct for docker /\n * sandbox / remote / edge contexts, not only the host process.\n *\n * Omitted → falls back to a local atomic write (`<path>.tmp` + rename) on the\n * host `fs`, the historical behavior. A routed write is not atomic, which is\n * immaterial here: the blob is written once and fully awaited BEFORE the stub\n * that references it is emitted, so a crash mid-write loses both halves\n * consistently — there is no torn-read window for a subsequent turn.\n */\n writeBlob?: (path: string, content: string) => Promise<void>\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, output: string, originalBytes: number }\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: the local fallback writes through `<path>.tmp` + `rename` so a\n * concurrent read (or a crash mid-write) never sees a half-written blob. A\n * routed write (`input.writeBlob`, e.g. a sandbox `writeFile`) is not atomic;\n * see the field doc for why that's safe here.\n *\n * Text-only `ToolResultContent[]` results are flattened and persisted as\n * text. Mixed structured content still bypasses persistence because the inline\n * image/document bytes are the point of the call and a mixed result is not\n * representable as a single `.txt` file without dropping media.\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 const persistableOutput = textForPersistence(input.output)\n if (persistableOutput === null)\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(persistableOutput)\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 (input.writeBlob ?? writeAtomic)(persistedPath, persistableOutput)\n }\n catch (err) {\n const error = err instanceof Error ? err : new Error(String(err))\n return {\n kind: 'error',\n reason: 'write-failed',\n error,\n originalBytes,\n output: buildPersistenceUnavailableStub({\n toolName: input.toolName,\n originalBytes,\n output: persistableOutput,\n error,\n }),\n }\n }\n\n const stub = buildPersistedStub({\n toolName: input.toolName,\n originalBytes,\n persistedPath,\n output: persistableOutput,\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 (!input.writeBlob && 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 * Blobs younger than this are never evicted by {@link enforcePersistDirCap}\n * (override via its `graceMs` option). The \"preserve newest\" guard only\n * protects the single newest blob — when two tool calls in the same batch\n * both persist, the second writer's sweep would otherwise evict the first\n * writer's blob milliseconds after the stub referencing it was emitted.\n * A short mtime grace window protects all in-flight siblings; the cap is a\n * soft housekeeping bound, so temporarily exceeding it is acceptable.\n */\nexport const PERSIST_EVICTION_GRACE_MS = 15_000\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. Blobs newer than `graceMs` (default\n * {@link PERSIST_EVICTION_GRACE_MS}) are skipped entirely — see the\n * constant's doc for the same-batch-writer race this prevents.\n */\nexport async function enforcePersistDirCap(\n persistDir: string,\n maxBytes: number,\n opts: { graceMs?: 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 const graceMs = opts.graceMs ?? PERSIST_EVICTION_GRACE_MS\n const evictableBefore = Date.now() - graceMs\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 // Within the grace window — and the list is mtime-ascending, so every\n // later candidate is too. Stop the sweep; the cap is soft.\n if (graceMs > 0 && meta.mtime > evictableBefore)\n break\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 /** Preview cap in bytes. Default {@link PERSISTENCE_PREVIEW_BYTES}. */\n previewBytes?: number\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, input.previewBytes ?? 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}${escapeXml(input.toolName)}\" bytes=\"${input.originalBytes}\" path=\"${escapeXml(input.persistedPath)}\">`,\n `${previewSlice}${previewMarker}`,\n '</persisted-output>',\n ].join('\\n')\n}\n\n/**\n * Render a bounded fallback when persistence was enabled but the blob write\n * failed. The full output is intentionally NOT kept inline: when this path\n * fires, preserving the context-window guarantee is more important than\n * recoverability of a failed best-effort spill.\n */\nfunction buildPersistenceUnavailableStub(input: {\n toolName: string\n originalBytes: number\n output: string\n error: Error\n}): string {\n const { slice: previewSlice, bytes: previewBytes } = sliceFirstBytes(input.output, PERSISTENCE_PREVIEW_BYTES)\n const previewMarker = previewSlice.length < input.output.length\n ? `\\n…(${input.originalBytes - previewBytes} more bytes omitted because persistence failed)`\n : ''\n // Cap the error message so the stub stays bounded even if a sandbox / FS\n // surfaces a pathologically long failure — the whole point of this path is\n // to NOT let large content ride inline.\n const reason = input.error.message.length > MAX_PERSIST_ERROR_CHARS\n ? `${input.error.message.slice(0, MAX_PERSIST_ERROR_CHARS)}…`\n : input.error.message\n return [\n `<persisted-output-unavailable tool=\"${escapeXml(input.toolName)}\" bytes=\"${input.originalBytes}\" reason=\"write-failed\">`,\n `Full output could not be saved to disk, so it was omitted from inline context. Error: ${escapeXml(reason)}`,\n '',\n 'Preview:',\n `${previewSlice}${previewMarker}`,\n '</persisted-output-unavailable>',\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 * Persist-on-elide: belatedly write tool results that tail compaction is\n * about to replace with the lossy `[…elided…]` stub, swapping in a\n * recoverable `<persisted-output … path=…>` stub instead. This is the lazy\n * counterpart to {@link maybePersistToolResult}'s eager (emit-time)\n * persistence — it only fires when a result is actually being compacted out,\n * so recent results stay full inline while OLD ones become recoverable rather\n * than destroyed. Closes the context-loss gap for non-read results between\n * the lossy stub and re-runnable side effects: after this, a compacted result\n * is always retrievable by `read_file`-ing its path.\n *\n * `items` are the {@link TailCompactionResult.persistableElided} entries (the\n * caller has already filtered out reads — which recover via the read-state\n * dedup re-serve — and results too small to be worth persisting). Each\n * `output` is the ORIGINAL content captured before the lossy stub replaced it.\n *\n * Idempotency: local blobs are immutable (a tool result never changes), so we\n * write `<callId>.txt` only when it's absent and otherwise just rebuild the\n * byte-stable stub. With `opts.writeBlob`, existence cannot be checked through\n * the current `ExecutionContext` interface (no remote stat), so we write the\n * same content to the same path again; this is safe and preserves correctness.\n * The session keeps the original content; only the wire copy carries the stub —\n * consistent with compaction being wire-only. Returns the messages with the\n * lossy stubs swapped; unchanged when nothing was persisted.\n */\nexport async function persistElidedToolResults(\n messages: SessionMessage[],\n items: ReadonlyArray<{ callId: string, toolName: string, output: string }>,\n opts: { persistDir: string, maxBytes?: number, writeBlob?: (path: string, content: string) => Promise<void> },\n): Promise<SessionMessage[]> {\n if (items.length === 0 || !isAbsolute(opts.persistDir))\n return messages\n\n const stubByCallId = new Map<string, string>()\n let wroteAny = false\n for (const item of items) {\n // Same path-traversal guard as maybePersistToolResult — a hostile or\n // malformed callId must not escape persistDir.\n if (!SAFE_CALL_ID.test(item.callId) || item.callId.includes('..'))\n continue\n const persistedPath = join(opts.persistDir, `${item.callId}.txt`)\n let exists = false\n if (!opts.writeBlob) {\n try {\n exists = (await stat(persistedPath)).isFile()\n }\n catch {\n exists = false\n }\n }\n if (!exists) {\n try {\n await (opts.writeBlob ?? writeAtomic)(persistedPath, item.output)\n wroteAny = true\n }\n catch {\n // Write failed — leave the lossy stub in place rather than emit a\n // recoverable stub pointing at a blob that isn't on disk.\n continue\n }\n }\n stubByCallId.set(item.callId, buildPersistedStub({\n toolName: item.toolName,\n originalBytes: toolOutputByteLength(item.output),\n persistedPath,\n output: item.output,\n previewBytes: ELIDE_PERSIST_PREVIEW_BYTES,\n }))\n }\n\n // LRU sweep only after a real LOCAL write, mirroring maybePersistToolResult.\n // Routed writes target the execution context's filesystem; a local sweep\n // would inspect/delete the wrong directory.\n if (!opts.writeBlob && wroteAny && typeof opts.maxBytes === 'number' && Number.isFinite(opts.maxBytes) && opts.maxBytes > 0)\n await enforcePersistDirCap(opts.persistDir, opts.maxBytes)\n\n if (stubByCallId.size === 0)\n return messages\n\n return messages.map((msg) => {\n if (!msg.content.some(b => b.type === 'tool_result' && stubByCallId.has(b.callId)))\n return msg\n return {\n ...msg,\n content: msg.content.map(b =>\n b.type === 'tool_result' && stubByCallId.has(b.callId)\n ? { ...b, output: stubByCallId.get(b.callId)! }\n : b,\n ),\n }\n })\n}\n\n// ---------------------------------------------------------------------------\n// Internal helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Write-then-rename for atomicity — delegates to the shared\n * {@link writeFileAtomicAsync} helper (unique pid+counter tmp suffix,\n * fsync before rename, tmp cleanup on failure). Creates the parent\n * directory on demand so the caller doesn't have to track which\n * sessions have already initialized their dir.\n */\nasync function writeAtomic(path: string, content: string): Promise<void> {\n await writeFileAtomicAsync(path, content, { ensureDir: true })\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 = utf8ByteLength(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 = utf8ByteLength(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\nfunction textForPersistence(output: string | ToolResultContent[]): string | null {\n if (typeof output === 'string')\n return output\n const parts: string[] = []\n for (const block of output) {\n if (block.type !== 'text')\n return null\n parts.push(block.text)\n }\n return parts.join('\\n')\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\n// ---------------------------------------------------------------------------\n// Path argument sanity\n// ---------------------------------------------------------------------------\n\n/**\n * Longest path most filesystems accept (Linux PATH_MAX). Anything beyond\n * this would fail at the syscall with ENAMETOOLONG anyway — rejecting here\n * produces an actionable message instead of a raw errno.\n */\nconst MAX_PATH_LENGTH = 4096\n/** Longest single path segment (Linux/APFS NAME_MAX, in bytes ≈ chars here). */\nconst MAX_PATH_SEGMENT_LENGTH = 255\n\n/**\n * Sanity-check a model-supplied filesystem path argument.\n *\n * Returns an actionable error string when the value cannot be a real path,\n * `undefined` when it looks plausible. This is NOT path *security*\n * validation (traversal etc. is the execution context's job) — it's a\n * steering backstop for a failure mode observed in production: reasoning\n * models leaking chain-of-thought into path arguments\n * (`src/components/Trust<think>The user wants me to build…`), which\n * otherwise surfaces as a raw `ENAMETOOLONG` the model rarely recovers\n * from.\n *\n * Checks, all objective:\n * - control characters (newlines, tabs, NUL, …) — never legal in a path\n * the model should be writing;\n * - `<think` / `</think` fragments — thinking-tag leakage;\n * - a segment over 255 chars or total length over 4096 — exceeds\n * filesystem limits (the ENAMETOOLONG shape).\n */\nexport function pathArgError(value: string, field = 'path'): string | undefined {\n for (let i = 0; i < value.length; i++) {\n const c = value.charCodeAt(i)\n if (c < 0x20 || c === 0x7F) {\n return `invalid \\`${field}\\`: contains a control character (e.g. a newline) — `\n + `this usually means reasoning text leaked into the argument. Re-issue the call with a clean file path.`\n }\n }\n if (/<\\/?think/i.test(value)) {\n return `invalid \\`${field}\\`: contains a thinking tag — `\n + `reasoning text leaked into the argument. Re-issue the call with a clean file path.`\n }\n if (value.length > MAX_PATH_LENGTH) {\n return `invalid \\`${field}\\`: ${value.length} characters exceeds the filesystem limit of ${MAX_PATH_LENGTH} — `\n + `this usually means non-path text leaked into the argument. Re-issue the call with a clean file path.`\n }\n for (const segment of value.split(/[/\\\\]/)) {\n if (segment.length > MAX_PATH_SEGMENT_LENGTH) {\n return `invalid \\`${field}\\`: segment \"${segment.slice(0, 40)}…\" is ${segment.length} characters `\n + `(filesystem limit: ${MAX_PATH_SEGMENT_LENGTH} per segment) — this usually means non-path text leaked `\n + `into the argument. Re-issue the call with a clean file path.`\n }\n }\n return undefined\n}\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 //\n // Special case: a completely empty input object against a schema with\n // required fields. Streaming layers degrade lost / truncated tool-call\n // arguments to `{}` rather than failing the whole turn (see `consumeSSE`\n // and `fromOpenAI`), so an empty object frequently means \"the arguments\n // never arrived\", not \"the model omitted one field\". A bare \"Missing\n // required field: X\" would misdirect the model into patching one field;\n // the appended hint steers it to re-issue the full call. The historic\n // `Missing required field:` prefix is kept so substring matchers and\n // log groupings keyed on it survive.\n if (required.length > 0 && Object.keys(input).length === 0) {\n return {\n valid: false,\n error: `Missing required field: ${required[0]} (no arguments received — they may have been `\n + `lost or truncated in streaming; re-issue the call with all required fields: ${required.join(', ')})`,\n }\n }\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, TurnResult as ProviderTurnResult, StreamCallbacks, StreamOptions, ToolCall, ToolResult, ToolSpec } from './providers'\nimport type { Session } from './session'\nimport type { PairingRepair } from './session/messages'\nimport type { ActiveSkill } from './skills/activation'\nimport type { SkillsConfig } from './skills/types'\nimport type { ToolCancelRegistry } from './tool-cancel'\nimport type { ReadStateMap } from './tools/read-state'\nimport type { ToolContext, ToolDef } from './tools/types'\nimport type { AgentBehavior, AgentClock, AgentStats, AutoCompact, McpServerConfig, ResolveContentRefBlock, RetryConfig, SessionContentBlock, SessionMessage, SessionTurn, ThinkingLevel, ToolBatchExecutionContext, ToolBatchExecutor, ToolHookContext, ToolOutcome, ToolResultContent, TurnUsage } from './types'\nimport { rewriteContentToCanonical, rewriteMessagesToWire, toCanonicalName, toWireName } from './aliasing'\nimport { mediaAgeoutMarker } from './attachment-handle'\nimport { AUTO_COMPACT_MIN_GROWTH_FRACTION, buildPostCompactAttachments, compactConversation, CompactInvalidInputError, selectFilesFromSession, shouldAutoCompact, summaryToTurn } from './compact'\nimport { AgentAbortedError, AgentBudgetExceededError, AgentProviderError, AgentToolPairingError, errorMessage, toTypedError } from './errors'\nimport { maybePersistToolResult, PERSISTED_STUB_PREFIX, persistElidedToolResults, PERSISTENCE_PREVIEW_BYTES } from './loop-persistence'\nimport { ensureEndsWithUserMessage, ensureToolResultPairing, remintDuplicateToolCallIds, resolveContentRefsInMessages } from './session/messages'\nimport { effectiveInputFromTurn } from './stats'\nimport { linkAbortSignal, OperationTimeoutError, resolveTimeoutMs, settleWithinTimeout, withTimeout } from './timeout'\nimport { markReadStateElided } from './tools/read-state'\nimport { validateToolArgs } from './tools/validation'\nimport { documentBlockMarker, toolOutputBudgetByteLength, toolOutputByteLength } from './types'\n\nconst DEFAULT_PROVIDER_STREAM_START_TIMEOUT_MS = 5 * 60 * 1000\nconst DEFAULT_PROVIDER_STREAM_TIMEOUT_MS = 30 * 60 * 1000\n\n/**\n * Default watchdog for awaited hook dispatch (`AgentBehavior.hookTimeoutMs`).\n * Generous (5 min) — bounds infinite hangs without policing slow but finite\n * host handlers. Shared with `agent.ts` (run lifecycle hooks) so both halves\n * resolve the same fallback.\n */\nexport const DEFAULT_HOOK_TIMEOUT_MS = 5 * 60 * 1000\n\nfunction hookTimeoutMsFor(ctx: LoopContext): number {\n return resolveTimeoutMs(ctx.hookTimeoutMs, DEFAULT_HOOK_TIMEOUT_MS)\n}\n\n/**\n * Dispatch an already-invoked hook promise under {@link LoopContext.hookTimeoutMs},\n * surfacing a timeout as a typed `OperationTimeoutError`. Use for state-mutating\n * pre-stream hooks that gate the wire (`context:transform` / `system:transform` /\n * `turn:before`) — failing closed is safer than shipping a half-applied transform.\n */\nfunction boundedHook(ctx: LoopContext, operation: string, work: Promise<unknown> | void): Promise<void> {\n return withTimeout(Promise.resolve(work), {\n operation,\n timeoutMs: hookTimeoutMsFor(ctx),\n }) as Promise<void>\n}\n\n/**\n * Like {@link boundedHook} but swallows a timeout (logs under `ZIDANE_DEBUG`)\n * and continues. Use for observability / lifecycle hooks (`stream:start`,\n * `stream:end`, `turn:after`, `tool-results:after`, …) where a hung handler\n * must neither block forward progress nor alter the run's primary outcome.\n * Non-timeout handler errors still propagate (preserving prior semantics).\n */\nfunction settledHook(ctx: LoopContext, operation: string, work: Promise<unknown> | void): Promise<void> {\n return settleWithinTimeout(operation, work, hookTimeoutMsFor(ctx))\n}\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 /** Watchdog for custom `toolBatchExecutor`. See `AgentBehavior.toolBatchTimeoutMs`. */\n toolBatchTimeoutMs?: number\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 /** Optional durable/runtime-specific tool batch scheduler. */\n toolBatchExecutor?: ToolBatchExecutor\n /** Tool errors that should escape the loop instead of becoming tool_results. */\n shouldRethrowToolError?: (error: unknown) => boolean\n signal: AbortSignal\n execution: ExecutionContext\n handle: ExecutionHandle\n steeringQueue: string[]\n /**\n * Synthetic user messages queued by middleware (tool-budget 'steer'\n * nudges) for injection at the next turn boundary. Kept separate from\n * `steeringQueue` on purpose: the batch schedulers treat a non-empty\n * steering queue as \"superseded by user message\" and skip the remaining\n * sibling tool calls mid-batch — a gate-time budget nudge must NOT have\n * that effect. Nudges never interrupt a batch and are dropped if the\n * run ends before the next turn.\n */\n toolBudgetNudgeQueue?: 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 /**\n * Remaining-turn threshold at which a wrap-up warning is injected as a\n * synthetic user message. See `AgentBehavior.maxTurnsWarning`. Only\n * consulted when `maxTurns` is finite.\n */\n maxTurnsWarning?: number\n /** Retry-with-backoff policy applied around `provider.stream()`. See `AgentBehavior.retry`. */\n retry?: RetryConfig\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 /** Run-level wall-clock ceiling in ms, measured via `ctx.clock`. See `AgentBehavior.maxWallMs`. */\n maxWallMs?: number\n /** First-response watchdog for provider.stream(). See `AgentBehavior.providerStreamStartTimeoutMs`. */\n providerStreamStartTimeoutMs?: number\n /** Total watchdog for provider.stream(). See `AgentBehavior.providerStreamTimeoutMs`. */\n providerStreamTimeoutMs?: number\n /** Watchdog for awaited hook dispatch. See `AgentBehavior.hookTimeoutMs`. */\n hookTimeoutMs?: 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 /** Enabled model-specific options (e.g. `{ fast: true }`). See `StreamOptions.modelOptions`. */\n modelOptions?: Record<string, boolean>\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 text-equivalent 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?: AgentBehavior['compactStrategy']\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 /**\n * Age base64 media off the wire once older than the last `mediaKeepTurns`\n * messages, replacing it with a re-resolvable marker. See\n * `AgentBehavior.mediaKeepTurns`.\n */\n mediaKeepTurns?: number\n /**\n * Resolved loop-native auto-compaction config, or `undefined` when off.\n * Normalized from `AgentBehavior.autoCompact` (with the `provider.meta`\n * window fallback already applied) by `resolveAutoCompact` in `agent.run`.\n */\n autoCompact?: ResolvedAutoCompact\n /**\n * Persist the freshly-appended tail of `ctx.turns` to the bound session\n * and fire `session:turns`. Set by `agent.run` only when a session is\n * attached AND per-turn sync is on (`persistTurns !== false`). The loop\n * calls it after appending a mid-run compaction marker so the summary is\n * durable + the host repaints immediately; in batch-persist mode it's\n * `undefined` and the run-end flush covers the marker.\n */\n persistTurns?: () => Promise<void>\n /**\n * Snapshot of the run's currently-active skills, for post-compact\n * restoration (`buildPostCompactAttachments`). Set by `agent.run` to\n * `() => skillActivationState.active()`; absent for hosts that don't wire\n * skills (restoration then re-injects files only).\n */\n getActiveSkills?: () => readonly ActiveSkill[]\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 /**\n * Per-turn pairing-repair circuit breaker. See\n * `AgentBehavior.maxPairingRepairsPerTurn`. Unset / non-positive /\n * non-finite disables (default: disabled).\n */\n maxPairingRepairsPerTurn?: number\n /**\n * Hard cap on consecutive Anthropic `pause_turn` recoveries within a\n * single run. Each `pause_turn` with no tool calls + no text triggers\n * a synthetic \"Please continue.\" user message that re-enters the loop;\n * without a cap, a model that returns `pause_turn` indefinitely (which\n * happens with some 4.6 streaming edge cases) spins forever burning\n * tokens. When the counter reaches the cap (a cap of 3 exits after\n * exactly 3 consecutive empty pauses), the loop ends the run with\n * `finishReason: 'pause'` instead of injecting another continue.\n *\n * Counter resets every turn that EMITS visible output (text, thinking,\n * or tool_calls) — only consecutive empty pauses count. Default in\n * `AgentBehavior.maxConsecutivePauseTurns` is `5`.\n */\n maxConsecutivePauseTurns?: number\n /** Provider name forwarded into `AgentToolPairingError` for strict-mode throws. */\n providerName?: string\n /**\n * Optional mutable mirror of the run-cumulative usage, updated after\n * every completed turn. `runLoop` returns its accumulated `AgentStats`\n * only on clean exits — when the loop THROWS (mid-stream abort, budget\n * breach), the caller would otherwise have no access to the tokens\n * already burned. `agent.run()` passes a mirror and reads it on the\n * catch paths so `agent:done` / `session.abortRun` carry real numbers\n * instead of zeros.\n */\n usageMirror?: RunUsageMirror\n /**\n * Per-callId cancellation registry shared with the agent so an external\n * `agent.cancelTool(callId)` can flip a single in-flight tool's signal\n * without aborting the whole run. The loop registers a controller for each\n * call right before gate/execute and unregisters it in a `finally` — so the\n * live set only ever holds *currently dispatching* calls. The registry also\n * applies kills parked before a call dispatched (the pre-registration race).\n *\n * When undefined, per-call cancellation is unavailable; the loop still\n * honors `ctx.signal` (run / sibling abort) the same way.\n */\n toolCancels?: ToolCancelRegistry\n}\n\n/**\n * Mutable run-usage accumulator shared between `runLoop` and its caller.\n * See {@link LoopContext.usageMirror}.\n */\nexport interface RunUsageMirror {\n totalIn: number\n totalOut: number\n totalCacheRead: number\n totalCacheCreation: number\n turns: number\n cost: number\n turnUsage: TurnUsage[]\n}\n\nconst IMAGE_OMITTED_MARKER = '[image omitted — model does not support vision]'\nconst AUDIO_OMITTED_MARKER = '[audio omitted — model does not support audio input]'\nconst VIDEO_OMITTED_MARKER = '[video omitted — model does not support video input]'\n\nfunction documentOmittedMarker(doc: Extract<ToolResultContent, { type: 'document' }>): string {\n return documentBlockMarker(doc, 'document omitted — model does not support document attachments')\n}\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/** 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 * 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 * Compose the run-scoped `ctx.signal` and a per-call abort into one child\n * signal for a tool body — WITHOUT `AbortSignal.any`.\n *\n * `AbortSignal.any([runSignal, perCall])` registers a dependent-signal\n * algorithm on each source; the run signal is long-lived (whole run), so one\n * composite per tool call accumulates on it until the composite is GC'd —\n * a slow leak across a long run with many calls. Manual `linkAbortSignal`\n * listeners removed in `dispose()` keep the run signal's listener set bounded\n * to currently-dispatching calls. `dispose()` MUST be called once the body has\n * settled. `reason` propagates from whichever source fires first (so a\n * signal-aware body still sees the per-call cancel reason).\n */\nfunction composePerCallSignal(\n runSignal: AbortSignal,\n perCallSignal: AbortSignal,\n): { signal: AbortSignal, dispose: () => void } {\n const controller = new AbortController()\n const unlinkRun = linkAbortSignal(runSignal, controller)\n const unlinkPerCall = linkAbortSignal(perCallSignal, controller)\n return {\n signal: controller.signal,\n dispose: () => {\n unlinkRun()\n unlinkPerCall()\n },\n }\n}\n\n/**\n * Grace window after a run-level abort before we force-settle a tool body that\n * hasn't returned. Well-behaved tools observe `childSignal` and finalize their\n * own bookkeeping (e.g. `spawn` awaits its child's session-persisting `finally`\n * block); they settle within this window and the forced rejection never fires.\n * Only a body that *ignores* its signal hits the timeout — that's the wedge we\n * guard against. Kept short so a real wedge doesn't stall `drain()` for long,\n * but long enough for a normal abort-driven cleanup to complete.\n *\n * Overridable via `ZIDANE_RUN_ABORT_GRACE_MS` (internal/testing only — lets\n * the wedge test exercise the forced-rejection path without a multi-second\n * real wait). Read per-use (not memoized) so tests can set it; falls back to\n * the 2s default on any non-finite value.\n */\nconst DEFAULT_RUN_ABORT_GRACE_MS = 2000\nfunction runAbortGraceMs(): number {\n const raw = Number(process.env.ZIDANE_RUN_ABORT_GRACE_MS)\n return Number.isFinite(raw) && raw >= 0 ? raw : DEFAULT_RUN_ABORT_GRACE_MS\n}\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 * Writer that routes a persisted tool-result blob through the run's execution\n * context, so the spill lands on the SAME filesystem `read_file` reads it back\n * from (`ctx.execution.readFile`) — correct for docker / sandbox / remote /\n * edge contexts, not just the host process. Shared by the eager\n * ({@link maybePersistToolResult}) and persist-on-elide\n * ({@link persistElidedToolResults}) paths so the routing lives in one place.\n */\nfunction executionBlobWriter(ctx: LoopContext): (path: string, content: string) => Promise<void> {\n return (path, content) => ctx.execution.writeFile(ctx.handle, path, 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: older tool output trimmed to fit the context budget.]'\n// UTF-8 size of the stub — `toolOutputByteLength` measures UTF-8 bytes, so\n// the \"already short enough\" comparison below must too ('…' is 3 UTF-8\n// bytes but 1 UTF-16 unit; `.length` would undercount the floor).\nconst COMPACTION_STUB_BYTES = toolOutputByteLength(COMPACTION_STUB)\n\n// Production quantization for the tail-compaction elision frontier. Snapping\n// the cutoff to multiples of this many messages keeps the elided prefix\n// byte-identical for `COMPACT_CHUNK_TURNS` requests at a time, so the\n// provider's prefix cache breaks once per chunk instead of every turn. See\n// `applyTailCompaction`'s `chunkTurns` option for the cache-thrash this avoids.\nconst COMPACT_CHUNK_TURNS = 8\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 while\n * keeping the largest recent suffix of tool output that still fits under\n * `threshold`. `keepTurns` is a floor — the newest `keepTurns` messages are\n * never eligible for elision even if their output alone exceeds the budget —\n * not a cap, so the model retains as much fresh tool context as the budget\n * allows rather than just the last few messages.\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 interface TailCompactionResult {\n messages: SessionMessage[]\n /**\n * Paths whose `read_file` outputs were replaced with a stub on this pass.\n * The caller routes these through `markReadStateForElidedPaths` so a\n * subsequent `read_file` misses its own dedup and re-serves the full body.\n * Otherwise the \"unchanged since the previous read\" replay stub points the\n * model at content compaction already removed from the wire — leaving it\n * with zero visible content and driving an infinite re-read loop (the\n * failure this return value exists to prevent).\n */\n elidedReadPaths: string[]\n /**\n * Non-read `tool_result`s this pass replaced with the lossy stub that are\n * large enough to be worth persisting to disk instead. The caller writes\n * each to the persist dir and swaps the lossy stub for a recoverable\n * `<persisted-output … path=…>` stub, so a compacted result stays\n * retrievable by reading its path rather than being lost. `output` is the\n * ORIGINAL content captured before the stub replaced it. Reads are excluded\n * (they recover via the read-state dedup re-serve); already-persisted stubs\n * are skipped.\n */\n persistableElided: Array<{ callId: string, toolName: string, output: string }>\n}\n\nexport function applyTailCompaction(\n messages: SessionMessage[],\n threshold: number,\n keepTurns: number,\n options?: {\n /**\n * call_id → file path for `read_file` calls, resolved from the canonical\n * (pre-alias) message list. Lets compaction report which stubbed results\n * were file reads so their read-state can be invalidated. Omit to skip\n * read-path tracking.\n */\n readPathByCallId?: ReadonlyMap<string, string>\n /**\n * Quantize the elision frontier to multiples of this many messages so the\n * elided prefix grows in discrete jumps instead of advancing one message\n * per turn. A per-turn frontier rewrites the wire bytes at the elision\n * boundary every request, collapsing the provider's prefix cache to the\n * system prompt (observed: cached tokens oscillating ~500k→20k while the\n * model made zero progress). Default 1 (legacy per-turn behavior); the\n * loop passes a larger value in production.\n */\n chunkTurns?: number\n /**\n * call_id → tool name for every tool call (canonical, pre-alias). Lets\n * persist-on-elide label the recoverable stub with the originating tool.\n */\n toolNameByCallId?: ReadonlyMap<string, string>\n /**\n * Minimum byte size for a non-read result to be reported in\n * {@link TailCompactionResult.persistableElided}. 0 (default) disables\n * persist-on-elide reporting — every elided result uses the lossy stub.\n * The loop passes the persisted-stub preview size, below which persisting\n * wouldn't save bytes.\n */\n persistElideMinBytes?: number\n },\n): TailCompactionResult {\n const elidedReadPaths: string[] = []\n const persistableElided: TailCompactionResult['persistableElided'] = []\n const toolNameByCallId = options?.toolNameByCallId\n const persistElideMinBytes = options?.persistElideMinBytes ?? 0\n if (messages.length === 0)\n return { messages, elidedReadPaths, persistableElided }\n\n // Budget the elision decision on the MODEL-VISIBLE size, not wire bytes:\n // attachments (images/audio/video/documents) in a tool result flatten to a\n // short `[image: …]`-style marker via `toolOutputBudgetByteLength`. Counting\n // their full base64 length here (as `toolOutputByteLength` does) let a single\n // large screenshot/PDF/video instantly blow the byte budget and prematurely\n // elide unrelated tool outputs. The marker-based sizer keeps the elision\n // frontier driven by textual context volume, which is what compaction is for.\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 += toolOutputBudgetByteLength(block.output)\n }\n }\n if (totalBytes <= threshold)\n return { messages, elidedReadPaths, persistableElided }\n\n // `keepTurns` is a FLOOR: the trailing `keep` messages are never eligible\n // for elision. Nothing older than that → bail out.\n const keep = Math.max(0, keepTurns)\n const floorCutoff = messages.length - keep\n if (floorCutoff <= 0)\n return { messages, elidedReadPaths, persistableElided }\n\n // Byte-budget eviction: keep the LARGEST recent suffix of tool output that\n // fits under `threshold`, eliding only as far forward as needed — instead\n // of bluntly eliding everything before the last `keep` messages. The blunt\n // message-count rule discarded ~all tool context the moment a session\n // crossed `threshold` (measured: 38 of 40 results gone; a freshly-read file\n // already elided by the time the model went to edit it), starving the model\n // even though the budget had room. Walk newest→oldest accumulating tool\n // bytes; the first message that tips the kept suffix over `threshold` (and\n // everything before it) is the eviction range.\n let keptBytes = 0\n let budgetCutoff = floorCutoff\n for (let i = messages.length - 1; i >= 0; i--) {\n for (const block of messages[i].content) {\n if (block.type === 'tool_result')\n keptBytes += toolOutputBudgetByteLength(block.output)\n }\n if (keptBytes > threshold) {\n budgetCutoff = i + 1\n break\n }\n }\n // Floor wins: never keep fewer than `keep` trailing messages, even if their\n // tool output alone busts the budget.\n const rawCutoff = Math.min(budgetCutoff, floorCutoff)\n if (rawCutoff <= 0)\n return { messages, elidedReadPaths, persistableElided }\n\n // Cache-stable frontier: snap the cutoff DOWN to a multiple of `chunk` so\n // the elided prefix [0, cutoff) is byte-identical across the next `chunk`\n // requests. A frontier that advanced one message per turn rewrote the wire\n // at the elision boundary every request, so the provider's prefix cache\n // collapsed to the system prompt every turn instead of holding. Rounding\n // DOWN keeps a few extra recent messages visible (mild, bounded overshoot of\n // `threshold`) — the safe direction for model context.\n const chunk = Math.max(1, options?.chunkTurns ?? 1)\n const cutoff = Math.floor(rawCutoff / chunk) * chunk\n if (cutoff <= 0)\n return { messages, elidedReadPaths, persistableElided }\n\n const readPathByCallId = options?.readPathByCallId\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_BYTES)\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 const readPath = readPathByCallId?.get(block.callId)\n if (readPath !== undefined) {\n elidedReadPaths.push(readPath)\n }\n else if (\n persistElideMinBytes > 0\n && typeof block.output === 'string'\n && existingBytes > persistElideMinBytes\n ) {\n // Non-read result big enough that persisting it to disk saves bytes\n // AND keeps it recoverable. The caller upgrades this COMPACTION_STUB\n // to a `<persisted-output … path=…>` stub; if persistence is off or\n // the write fails, the lossy stub stands.\n persistableElided.push({\n callId: block.callId,\n toolName: toolNameByCallId?.get(block.callId) ?? 'tool',\n output: block.output,\n })\n }\n return { ...block, output: COMPACTION_STUB }\n })\n if (msgChanged)\n out[i] = { ...msg, content: newContent }\n }\n return { messages: changed ? out : messages, elidedReadPaths, persistableElided }\n}\n\nexport interface MediaAgeoutResult {\n messages: SessionMessage[]\n /**\n * `read_file` paths whose image/document output was aged out this pass. The\n * caller routes these through `markReadStateForElidedPaths` so a subsequent\n * `read_file <path>` re-serves the full body instead of the dedup\n * \"unchanged\" stub (which would point at media no longer on the wire).\n */\n elidedReadPaths: string[]\n}\n\n/**\n * Age base64 media off the wire. Replaces top-level `image` / `document`\n * blocks — and image/document parts nested in `tool_result` outputs — with a\n * short, re-resolvable text marker (`mediaAgeoutMarker`) for every message\n * older than the last `keep` messages. Wire-only: the persisted session keeps\n * the original bytes, and the marker's `attachment://<hash>` handle lets the\n * model pull any image back via `read_file`.\n *\n * This is the long-run de-bloat lever for image/document-heavy sessions —\n * complementary to `applyTailCompaction`, which only stubs `tool_result`\n * text* and (deliberately) never counts or trims media bytes, so a session\n * full of screenshots would otherwise carry every base64 payload forever.\n *\n * Cache-stable: the frontier is snapped DOWN to a multiple of `chunkTurns` so\n * the aged prefix is byte-identical across `chunkTurns` requests (same\n * discipline as `applyTailCompaction`); markers are content-hash-stable, so a\n * frozen frontier yields a frozen prefix. `keep = 0` ages out ALL media — used\n * by the reactive context-overflow recovery.\n *\n * Pure: returns the input reference unchanged when nothing aged out.\n */\nexport function applyMediaAgeout(\n messages: SessionMessage[],\n keep: number,\n options?: { chunkTurns?: number, readPathByCallId?: ReadonlyMap<string, string>, readFileToolName?: string | null },\n): MediaAgeoutResult {\n const elidedReadPaths: string[] = []\n if (messages.length === 0)\n return { messages, elidedReadPaths }\n const readFileToolName = options?.readFileToolName ?? 'read_file'\n\n const keepFloor = Math.max(0, keep)\n const rawCutoff = messages.length - keepFloor\n if (rawCutoff <= 0)\n return { messages, elidedReadPaths }\n const chunk = Math.max(1, options?.chunkTurns ?? 1)\n const cutoff = Math.floor(rawCutoff / chunk) * chunk\n if (cutoff <= 0)\n return { messages, elidedReadPaths }\n\n const readPathByCallId = options?.readPathByCallId\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): SessionContentBlock => {\n // Top-level media block with inline bytes → marker. Ref-form blocks\n // (no `data`) are skipped — they're host-resolved, not recoverable here.\n if ((block.type === 'image' || block.type === 'document') && typeof (block as { data?: unknown }).data === 'string') {\n msgChanged = true\n changed = true\n return { type: 'text', text: mediaAgeoutMarker(block as { type: 'image' | 'document', mediaType: string, data: string, name?: string }, readFileToolName) }\n }\n // Media nested inside a tool_result output array (Read images, MCP\n // screenshots, attached PDFs).\n if (block.type === 'tool_result' && Array.isArray(block.output)) {\n let outputChanged = false\n const newOutput: ToolResultContent[] = block.output.map((part) => {\n if ((part.type === 'image' || part.type === 'document') && typeof (part as { data?: unknown }).data === 'string') {\n outputChanged = true\n return { type: 'text' as const, text: mediaAgeoutMarker(part as { type: 'image' | 'document', mediaType: string, data: string, name?: string }, readFileToolName) }\n }\n return part\n })\n if (outputChanged) {\n msgChanged = true\n changed = true\n const p = readPathByCallId?.get(block.callId)\n if (p !== undefined)\n elidedReadPaths.push(p)\n return { ...block, output: newOutput }\n }\n }\n return block\n })\n if (msgChanged)\n out[i] = { ...msg, content: newContent }\n }\n return { messages: changed ? out : messages, elidedReadPaths }\n}\n\n/**\n * Build a call_id → file path map for `read_file` tool calls in `messages`.\n * Tail compaction uses it to learn which stubbed `tool_result`s were file\n * reads, so their read-state dedup can be invalidated. Resolve this from the\n * canonical, pre-alias history — call_ids are stable across the alias rewrite,\n * so the map still keys the wire-level `tool_result` blocks.\n */\nfunction collectCallMaps(messages: SessionMessage[]): {\n readPathByCallId: Map<string, string>\n toolNameByCallId: Map<string, string>\n} {\n const readPathByCallId = new Map<string, string>()\n const toolNameByCallId = new Map<string, string>()\n for (const msg of messages) {\n for (const block of msg.content) {\n if (block.type !== 'tool_call')\n continue\n toolNameByCallId.set(block.id, block.name)\n if (block.name === 'read_file') {\n const path = (block.input as { path?: unknown }).path\n if (typeof path === 'string' && path.length > 0)\n readPathByCallId.set(block.id, path)\n }\n }\n }\n return { readPathByCallId, toolNameByCallId }\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: this file was edited later in the run, so this earlier read is stale.]'\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 * Flag 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 mark by direct\n * lookup. No suffix matching, no cross-cwd ambiguity.\n *\n * Marks, does NOT delete. Earlier versions deleted the entry, which made\n * the `requireReadBeforeEdit` gate reject the model's very next edit on a\n * file it had just read and edited (\"has not been read in this session\"\n * on a demonstrably-read file). That failure mode defeats the guard\n * itself: models degrade to raw shell one-liner edits that bypass the\n * gate entirely. The entry must therefore survive elision — its\n * contentHash still guards against on-disk drift, which is the\n * corruption the gate exists to prevent.\n *\n * The deletion originally served `dedupReads` honesty: a post-elision\n * re-read would dedup-hit (entry hash == current disk hash) and return\n * the \"unchanged since the previous read\" stub while the only on-wire\n * read is the elision stub — leaving the model with zero visible content\n * for the path (the trap described on `StaleReadElisionResult.\n * elidedPaths`). The `elided` flag solves that more surgically:\n * `read_file`'s dedup check misses on flagged entries and serves the\n * full body again, then clears the flag. Paths the model re-read after\n * the mutation are exempt via `pathsWithFreshRead`. The marking re-fires\n * on later turns while the elided read remains the path's newest on-wire\n * read, keeping an edit-only refresh from re-arming the dedup trap.\n */\nfunction markReadStateForElidedPaths(\n ctx: { session?: Session, readState?: ReadStateMap },\n cwd: string,\n elidedPaths: readonly string[],\n): void {\n if (elidedPaths.length === 0)\n return\n if (!ctx.readState && !ctx.session)\n return\n for (const p of elidedPaths)\n markReadStateElided(ctx, 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 // Repair-storm circuit breaker (opt-in). A repair count this high in a\n // single wire build means the persisted transcript is systematically\n // corrupt (e.g. a provider reusing tool_use ids on every turn) — each\n // further turn would re-strip the model's work, make zero progress, and\n // grow the repair count quadratically. Abort with the same typed error\n // strict mode uses so hosts get the full repair list for diagnosis.\n // Unset / non-positive / non-finite disables (the historic behavior:\n // repair silently, never throw).\n const rawLimit = ctx.maxPairingRepairsPerTurn\n const limit = typeof rawLimit === 'number' && Number.isFinite(rawLimit) && rawLimit > 0\n ? rawLimit\n : Number.POSITIVE_INFINITY\n if (repairs.length > limit) {\n throw new AgentToolPairingError({\n message:\n `Pairing-repair storm: ${repairs.length} repairs in a single turn (limit ${limit}). `\n + `The transcript is systematically corrupt (likely duplicate tool_use ids from the provider); `\n + `continuing would loop without forward progress.`,\n ...(ctx.providerName ? { provider: ctx.providerName } : {}),\n repairs,\n })\n }\n\n for (const repair of repairs) {\n void Promise.resolve(ctx.hooks.callHook('pairing:repair', { ...repair, turnId })).catch(() => {})\n }\n\n return repaired\n}\n\nfunction sanitizeStoredToolResults(\n provider: Provider,\n messages: SessionMessage[],\n): SessionMessage[] {\n // No top-level capability short-circuit here: `sanitizeToolOutputForProvider`\n // owns the fast path (it returns the output unchanged when the provider\n // accepts every media kind). A gate here previously checked only\n // `vision`/`documents` and so skipped audio/video degradation for\n // vision+document-capable-but-audio-incapable routes (e.g. OpenRouter to a\n // PDF-capable, audio-incapable model), letting stored audio/video bytes reach\n // the wire builder and throw mid-run. Delegating to the per-block sanitizer\n // keeps the two predicates from drifting.\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 const output = sanitizeToolOutputForProvider(provider, block.output)\n if (output === block.output)\n return block\n changed = true\n return { ...block, output }\n })\n return changed ? { ...msg, content: newContent } : msg\n })\n}\n\n/**\n * Build the `agent:abort` ctx, attaching the run id and (when present) the\n * string reason carried on `signal.reason` — lets telemetry correlate the\n * abort with its run and distinguish a reasoned abort (e.g. a guard) from a\n * bare `agent.abort()`.\n */\nfunction buildAbortCtx(ctx: LoopContext): { runId?: string, reason?: string } {\n const reason = ctx.signal.reason\n return {\n ...(ctx.runId ? { runId: ctx.runId } : {}),\n ...(typeof reason === 'string' && reason.length > 0 ? { reason } : {}),\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 // Consecutive `pause_turn` recovery counter — incremented every time a\n // turn returns `pauseEmpty: true` (Anthropic 4.6+ stopped with no tools\n // and no text), reset on any turn that produces visible output. When\n // the counter REACHES `maxConsecutivePauseTurns` (default 5), the loop\n // ends the run with the last turn's `finishReason: 'pause'` instead of\n // injecting another \"Please continue.\" Without this, a model returning\n // empty pauses indefinitely (observed on some 4.6 streaming edge cases)\n // spins forever burning tokens — the host's only escape was an external\n // abort. Cap was added because Claude Code's v2 SDK collapses pause_turn\n // to stop outright; zidane's recovery is more forgiving but needs a\n // finite budget.\n const pauseCap = ctx.maxConsecutivePauseTurns ?? 5\n let consecutiveEmptyPauseTurns = 0\n\n // Wall-clock deadline anchor. Read through `ctx.clock` (NOT `Date.now()`)\n // so durable hosts with a journaled clock get a replay-stable start: the\n // same invocation replays with the same anchor and the breach trips at\n // the same turn boundary every time. Only sampled when the knob is set —\n // each journaled `clock.now()` call is a journal entry on Restate-style\n // runtimes and we don't spend one on runs that never check the deadline.\n const wallDeadlineActive = typeof ctx.maxWallMs === 'number' && Number.isFinite(ctx.maxWallMs) && ctx.maxWallMs > 0\n const wallStartMs = wallDeadlineActive ? await ctx.clock.now() : 0\n\n // Remaining-turn threshold for the wrap-up warning. Validated once:\n // requires a finite cap, a positive warning value strictly below it.\n const maxTurnsWarnAt\n = Number.isFinite(maxTurns)\n && typeof ctx.maxTurnsWarning === 'number'\n && Number.isFinite(ctx.maxTurnsWarning)\n && ctx.maxTurnsWarning > 0\n && ctx.maxTurnsWarning < maxTurns\n ? Math.floor(ctx.maxTurnsWarning)\n : undefined\n let maxTurnsWarned = false\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 // Loop-native auto-compaction state (see `maybeAutoCompact`). Run-scoped:\n // the hysteresis baseline + circuit breaker reset every fresh run.\n const autoCompactState: AutoCompactRunState = {\n lastCompactedInputTokens: undefined,\n consecutiveFailures: 0,\n disabled: false,\n }\n\n try {\n for (let turn = 0; turn < maxTurns; turn++) {\n if (ctx.signal.aborted) {\n await ctx.hooks.callHook('agent:abort', buildAbortCtx(ctx))\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 // Keep the caller-visible mirror in sync per turn so throw paths\n // (budget breach below, mid-stream abort inside `executeTurn`)\n // still expose the partial spend. See `LoopContext.usageMirror`.\n if (ctx.usageMirror) {\n ctx.usageMirror.totalIn = totalIn\n ctx.usageMirror.totalOut = totalOut\n ctx.usageMirror.totalCacheRead = totalCacheRead\n ctx.usageMirror.totalCacheCreation = totalCacheCreation\n ctx.usageMirror.turns = turnsCompleted\n ctx.usageMirror.cost += result.usage.cost ?? 0\n ctx.usageMirror.turnUsage.push(result.usage)\n }\n\n await ctx.hooks.callHook('usage', { turn, turnId: result.turnId, usage: result.usage, totalIn, totalOut })\n\n // Pause-turn cap. Increment on consecutive empty pauses, reset on\n // any productive turn. The check is post-`usage` so the offending\n // turn's tokens are billed back to the run AND the `usage` hook\n // fires for that turn before the loop exits — telemetry consumers\n // never see a missing-usage event for an in-`turnUsages` entry.\n // When the cap trips, we `break` cleanly: the last entry in\n // `turnUsages` carries `finishReason: 'pause'`, so consumers can\n // detect pause exhaustion from `stats` alone. We do NOT fire\n // `agent:abort` here — this is graceful loop termination, not a\n // user-driven cancel, and host code keyed on `agent:abort` would\n // misclassify it as such.\n if (result.pauseEmpty) {\n consecutiveEmptyPauseTurns += 1\n if (pauseCap > 0 && consecutiveEmptyPauseTurns >= pauseCap)\n break\n }\n else {\n consecutiveEmptyPauseTurns = 0\n }\n\n // Check abort after turn completes. This also catches a repeat-guard\n // abort: the guard's `tool:gate` handler called `abortController.abort()`\n // when a tracked tool's consecutive-identical streak hit its threshold,\n // which flips `ctx.signal.aborted`. A `repeat-guard:exceeded` event with\n // `action: 'abort'` fired first, so consumers can distinguish it from a\n // user-driven cancel.\n if (ctx.signal.aborted) {\n await ctx.hooks.callHook('agent:abort', buildAbortCtx(ctx))\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 maxWallMs: ctx.maxWallMs,\n cost: turnUsages.reduce((s, t) => s + (t.cost ?? 0), 0),\n tokens: totalIn + totalOut,\n // Same journaled-clock source as the anchor — replay-stable pairs.\n elapsedMs: wallDeadlineActive ? (await ctx.clock.now()) - wallStartMs : 0,\n })\n if (breach) {\n throw new AgentBudgetExceededError(breach)\n }\n\n // Loop-native auto-compaction. Evaluated at every turn boundary —\n // mid-run tool loops AND the final turn, so a resumed session starts\n // already compacted. Runs AFTER abort/budget checks so compaction never\n // spends an extra summary call when the just-finished turn already ended\n // the run. No-op unless `behavior.autoCompact` is enabled and the last\n // turn's effective input crossed the threshold; never throws (failures\n // fall through to the reactive `context_exceeded` net). When it fires it\n // appends a `compact-summary` marker to `ctx.turns`, which the next\n // `executeTurn`'s `applyCompactSummaryCutoff` acts on.\n await maybeAutoCompact(ctx, turn, result.usage, autoCompactState)\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 // Drain middleware nudges (tool-budget 'steer' mode) at the turn\n // boundary. These ride their own queue — see the\n // `toolBudgetNudgeQueue` doc on `LoopContext` for why pushing them\n // into `steeringQueue` would skip sibling tool calls mid-batch.\n // Only drop them when the run is truly over (ended with no queued\n // follow-up): a follow-up continues the run, and the model may\n // resume calling the over-budget tool, so the nudge stays relevant.\n if (ctx.toolBudgetNudgeQueue && ctx.toolBudgetNudgeQueue.length > 0) {\n if (result.ended && ctx.followUpQueue.length === 0) {\n ctx.toolBudgetNudgeQueue.length = 0\n }\n else {\n while (ctx.toolBudgetNudgeQueue.length > 0) {\n const nudge = ctx.toolBudgetNudgeQueue.shift()!\n await ctx.hooks.callHook('steer:inject', { message: nudge })\n const nudgeMsg = ctx.provider.userMessage(nudge)\n ctx.turns.push({\n id: await ctx.generateTurnId(),\n runId: ctx.runId,\n role: nudgeMsg.role,\n content: nudgeMsg.content,\n createdAt: await ctx.clock.now(),\n })\n }\n }\n }\n\n // Turn-cap proximity warning (opt-in via `maxTurnsWarning`). Fires\n // once, after the first non-ended turn that leaves at most `warnAt`\n // turns remaining, so the model gets a deterministic chance to wrap\n // up instead of being killed mid-loop by the cap with no closing\n // summary. `<=` + the fired flag (not `===`): a user-steer `continue`\n // above can skip this check on the exact threshold turn, and a\n // strict-equality match would then never fire at all.\n if (!result.ended && !maxTurnsWarned && maxTurnsWarnAt !== undefined && maxTurns - (turn + 1) <= maxTurnsWarnAt) {\n maxTurnsWarned = true\n const remaining = maxTurns - (turn + 1)\n const warning\n = `[Turn limit approaching: ${remaining} of ${maxTurns} turns remaining. `\n + `Stop exploring, finish the most important remaining change, and summarize what you did and what is left.]`\n await ctx.hooks.callHook('steer:inject', { message: warning })\n const warnMsg = ctx.provider.userMessage(warning)\n ctx.turns.push({\n id: await ctx.generateTurnId(),\n runId: ctx.runId,\n role: warnMsg.role,\n content: warnMsg.content,\n createdAt: await ctx.clock.now(),\n })\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 // Exhausted exit — `maxTurns` ran out, the pause-cap tripped, or an\n // abort `break` landed above. Schema-enforced runs must still produce\n // a verdict here: historically `__output__` was only injected on the\n // natural `result.done` path, so a run that ground into the turn cap\n // mid-investigation ended verdict-less and hosts had to steer their\n // own synthesis retry. Aborts are exempt (a cancel means \"stop now\"),\n // and a zero-turn run has nothing to synthesize from.\n let exhaustedOutput: Record<string, unknown> | undefined\n if (ctx.schema && !ctx.signal.aborted && turnsCompleted > 0) {\n const enforcement = await enforceSchemaOutput(ctx, ctx.clock.randomUUID())\n exhaustedOutput = enforcement.output\n totalIn += enforcement.usage.input\n totalOut += enforcement.usage.output\n totalCacheRead += enforcement.usage.cacheRead ?? 0\n totalCacheCreation += enforcement.usage.cacheCreation ?? 0\n turnUsages.push(enforcement.usage)\n if (ctx.usageMirror) {\n ctx.usageMirror.totalIn = totalIn\n ctx.usageMirror.totalOut = totalOut\n ctx.usageMirror.totalCacheRead = totalCacheRead\n ctx.usageMirror.totalCacheCreation = totalCacheCreation\n ctx.usageMirror.cost += enforcement.usage.cost ?? 0\n ctx.usageMirror.turnUsage.push(enforcement.usage)\n }\n // Same telemetry contract as in-loop turns: every `turnUsages` entry\n // gets a `usage` event. `turn` is the would-be next index.\n await ctx.hooks.callHook('usage', { turn: turnsCompleted, turnId: enforcement.turnId, usage: enforcement.usage, totalIn, totalOut })\n }\n\n return {\n totalIn,\n totalOut,\n totalCacheRead,\n totalCacheCreation,\n turns: turnsCompleted,\n elapsed: Date.now() - startTime,\n turnUsage: turnUsages,\n ...(exhaustedOutput !== undefined ? { output: exhaustedOutput } : {}),\n ...(ttft.mark !== undefined ? { timeTillFirstTokenMs: ttft.mark } : {}),\n }\n }\n finally {\n unregisterTtftText()\n unregisterTtftThinking()\n unregisterTtftTool()\n }\n}\n\n// ---------------------------------------------------------------------------\n// Schema enforcement\n// ---------------------------------------------------------------------------\n\nasync function resolveContentRefsForWire(\n ctx: LoopContext,\n messages: readonly SessionMessage[],\n): Promise<SessionMessage[]> {\n return resolveContentRefsInMessages(messages, {\n resolveContentRef: async (block: ResolveContentRefBlock) => {\n const hookCtx: { block: ResolveContentRefBlock, data?: string } = { block }\n await ctx.hooks.callHook('content-ref:resolve', hookCtx)\n if (typeof hookCtx.data !== 'string')\n throw new Error(`Content ref \"${block.ref}\" (${block.type}) was not resolved by a content-ref:resolve hook.`)\n return { data: hookCtx.data }\n },\n })\n}\n\n/**\n * Force one provider call whose only tool is the synthetic `__output__`\n * spec built from `behavior.schema`, with `toolChoice` pinned to it, so\n * the model must emit a schema-conforming payload. Fires the `output`\n * hook and appends the schema turn to `ctx.turns`; the caller accounts\n * the returned usage into its own ledger.\n *\n * Two call sites:\n *\n * - `executeTurn`'s natural end (`result.done`) — the historic path;\n * the schema usage is merged into the final turn's `TurnUsage`.\n * - `runLoop`'s exhausted exit (`maxTurns` / pause-cap) — without this,\n * a schema-enforced run that ended on a tool turn could never produce\n * a conforming verdict and hosts had to steer their own synthesis\n * retry. Aborts stay exempt: a cancel means \"stop now\", not \"spend\n * one more provider call\".\n *\n * Message-shaping notes (apply to both call sites):\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: pair-repair fills\n * orphan `tool_use` adjacency; ensureEndsWithUserMessage covers the\n * plain assistant-text-tail case (a text-only assistant turn lands\n * here with an assistant-terminated history — pairing repair can't\n * fix it because pairing is already valid).\n */\nasync function enforceSchemaOutput(\n ctx: LoopContext,\n turnId: string,\n): Promise<{ output?: Record<string, unknown>, usage: TurnUsage, turnId: string }> {\n const outputSpec: ToolSpec = {\n name: '__output__',\n description: 'Return the final structured output matching the required schema.',\n inputSchema: ctx.schema!,\n }\n const schemaCanonicalMessages = await resolveContentRefsForWire(\n ctx,\n turnsToMessages(applyCompactSummaryCutoff(ctx.turns)),\n )\n const schemaMessages = ensureEndsWithUserMessage(\n applyPairingRepair(\n ctx,\n rewriteMessagesToWire(\n schemaCanonicalMessages,\n ctx.aliasMaps,\n ),\n turnId,\n ),\n ctx.provider,\n )\n let schemaResult\n try {\n // Same first-response / total-stream watchdogs as the main turn — the\n // forced-tool-choice schema-extraction call is just as prone to a provider\n // that never settles, and would otherwise leave `agent.run()` pending.\n schemaResult = await streamWithWatchdogs(\n ctx,\n {\n model: ctx.model,\n system: ctx.system,\n tools: ctx.provider.formatTools([outputSpec]),\n messages: schemaMessages,\n maxTokens: ctx.maxTokens ?? 16384,\n toolChoice: { type: 'tool', name: '__output__' },\n },\n {\n onText: () => {},\n onOAuthRefresh(refreshCtx) {\n return ctx.hooks.callHook('oauth:refresh', refreshCtx)\n },\n },\n turnId,\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: ctx.clock.randomUUID(),\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 { output, usage: schemaResult.usage, turnId: schemaTurn.id }\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 * Set to `'empty-pause'` when the turn produced no tool calls, no text,\n * and the provider stop reason was `pause_turn` — the case the\n * \"Please continue.\" recovery exists for. `runLoop` uses this signal to\n * count consecutive empty pauses and abort with `finishReason: 'pause'`\n * after `maxConsecutivePauseTurns` to prevent infinite pause loops on\n * Anthropic 4.6+ streaming edge cases.\n *\n * `undefined` when the turn produced ANY visible output (tool calls,\n * text, or thinking) — including a non-empty pause turn (the loop\n * counter resets in that case).\n */\n pauseEmpty?: true\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 the cost, token, or wall-clock ledger\n * has 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 maxWallMs?: number\n cost: number\n tokens: number\n elapsedMs?: number\n}): { limit: 'cost' | 'tokens' | 'wallClock', limitValue: number, actualValue: number } | null {\n const { maxCostUsd, maxTotalTokens, maxWallMs, cost, tokens, elapsedMs } = 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 if (typeof maxWallMs === 'number' && Number.isFinite(maxWallMs) && maxWallMs > 0 && typeof elapsedMs === 'number' && elapsedMs >= maxWallMs) {\n return { limit: 'wallClock', limitValue: maxWallMs, actualValue: elapsedMs }\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\nfunction providerWatchdogTimeouts(ctx: LoopContext): {\n startTimeoutMs: number\n totalTimeoutMs: number\n} {\n return {\n startTimeoutMs: resolveTimeoutMs(\n ctx.providerStreamStartTimeoutMs,\n DEFAULT_PROVIDER_STREAM_START_TIMEOUT_MS,\n ctx.maxWallMs,\n ),\n totalTimeoutMs: resolveTimeoutMs(\n ctx.providerStreamTimeoutMs,\n DEFAULT_PROVIDER_STREAM_TIMEOUT_MS,\n ctx.maxWallMs,\n ),\n }\n}\n\nasync function streamWithWatchdogs(\n ctx: LoopContext,\n options: StreamOptions,\n callbacks: StreamCallbacks,\n turnId: string,\n): Promise<ProviderTurnResult> {\n const { startTimeoutMs, totalTimeoutMs } = providerWatchdogTimeouts(ctx)\n const attemptAbort = new AbortController()\n const unlinkParentAbort = linkAbortSignal(ctx.signal, attemptAbort)\n\n let settled = false\n let firstObserved = false\n let startTimer: ReturnType<typeof setTimeout> | undefined\n let totalTimer: ReturnType<typeof setTimeout> | undefined\n\n const cleanup = () => {\n if (startTimer)\n clearTimeout(startTimer)\n if (totalTimer)\n clearTimeout(totalTimer)\n unlinkParentAbort()\n }\n\n const markFirstObserved = () => {\n if (firstObserved)\n return\n firstObserved = true\n if (startTimer) {\n clearTimeout(startTimer)\n startTimer = undefined\n }\n }\n\n let streamPromise: Promise<ProviderTurnResult>\n try {\n streamPromise = ctx.provider.stream(\n { ...options, signal: attemptAbort.signal },\n {\n ...callbacks,\n // Once the watchdog has settled (timed out, or the stream already\n // resolved/rejected), drop further deltas: a provider that ignores the\n // abort signal must not keep mutating an abandoned turn.\n onText(delta) {\n if (settled)\n return\n markFirstObserved()\n callbacks.onText(delta)\n },\n ...(callbacks.onThinking\n ? {\n onThinking(delta: string) {\n if (settled)\n return\n markFirstObserved()\n callbacks.onThinking?.(delta)\n },\n }\n : {}),\n ...(callbacks.onServerToolUse\n ? {\n onServerToolUse(block: { id: string, name: string, input: Record<string, unknown> }) {\n if (settled)\n return\n markFirstObserved()\n callbacks.onServerToolUse?.(block)\n },\n }\n : {}),\n ...(callbacks.onServerToolResult\n ? {\n onServerToolResult(block: { toolUseId: string, toolName: string, content: unknown }) {\n if (settled)\n return\n markFirstObserved()\n callbacks.onServerToolResult?.(block)\n },\n }\n : {}),\n // Tool-only turns stream tool-call argument deltas but no text/\n // thinking — without observing them, `firstObserved` stays false and\n // the first-response watchdog can abort an actively-progressing\n // stream. Always subscribe (providers no-op when they don't emit it)\n // and forward to any caller-supplied handler. (B2)\n onToolCallDelta() {\n if (settled)\n return\n markFirstObserved()\n callbacks.onToolCallDelta?.()\n },\n },\n )\n }\n catch (err) {\n unlinkParentAbort()\n throw err\n }\n\n streamPromise.catch(() => {\n // If a watchdog wins the race below, the provider promise may reject later\n // after seeing the timeout abort signal. Observe it to avoid unhandled\n // rejections without changing the returned timeout error.\n })\n\n return await new Promise<ProviderTurnResult>((resolve, reject) => {\n const failTimeout = (kind: 'first response' | 'total stream', timeoutMs: number) => {\n if (settled)\n return\n settled = true\n const err = new OperationTimeoutError(\n `provider ${kind}`,\n timeoutMs,\n `Provider stream timed out waiting for ${kind} after ${timeoutMs}ms (turn ${turnId}).`,\n )\n attemptAbort.abort(err)\n cleanup()\n reject(err)\n }\n\n if (startTimeoutMs > 0) {\n startTimer = setTimeout(() => {\n if (!firstObserved)\n failTimeout('first response', startTimeoutMs)\n }, startTimeoutMs)\n }\n if (totalTimeoutMs > 0)\n totalTimer = setTimeout(failTimeout, totalTimeoutMs, 'total stream', totalTimeoutMs)\n\n streamPromise.then(\n (value) => {\n if (settled)\n return\n settled = true\n markFirstObserved()\n cleanup()\n resolve(value)\n },\n (err) => {\n if (settled)\n return\n settled = true\n markFirstObserved()\n cleanup()\n reject(err)\n },\n )\n })\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\n/** Defaults applied when `behavior.retry` is absent or has missing fields. */\nconst RETRY_DEFAULTS = {\n maxAttempts: 3,\n initialDelayMs: 1000,\n maxDelayMs: 30_000,\n} as const\n\n/**\n * Reactive context-overflow recovery schedule. When a turn is rejected with\n * `context_exceeded` (pre-stream — the request was too big for the model's\n * window), the loop emergency-compacts older `tool_result` bodies via\n * {@link applyTailCompaction} and re-issues, instead of surfacing the error.\n *\n * Each entry is the `keepTurns` floor for one recovery pass: the first pass\n * keeps the last 4 turns' tool output intact, then 1, then everything is\n * eligible. Progressive so the model loses as little fresh context as possible\n * while still shrinking enough to fit. The array length bounds the number of\n * recoveries per turn. Runs regardless of `behavior.compactStrategy` (calls the\n * compactor directly), so it protects runs that never opted into compaction.\n */\nconst CONTEXT_OVERFLOW_KEEP_TURNS = [4, 1, 0] as const\n\n/** Default fire threshold for loop-native auto-compaction (fraction of effective window). */\nconst DEFAULT_AUTO_COMPACT_THRESHOLD = 0.85\n/** Default consecutive-failure circuit-breaker budget for auto-compaction. */\nconst DEFAULT_AUTO_COMPACT_MAX_FAILURES = 2\n\n/** Normalized {@link AutoCompact} config the loop consumes. See {@link resolveAutoCompact}. */\nexport interface ResolvedAutoCompact {\n threshold: number\n /** Raw context window in tokens, or `null` when no source resolved one (trigger inert). */\n contextWindow: number | null\n keepTurns: number\n minGrowthFraction: number\n restore: boolean\n maxFailures: number\n}\n\n/**\n * Normalize `behavior.autoCompact` into the loop's {@link ResolvedAutoCompact}\n * shape, or `undefined` when disabled (`false` / unset). Applies the\n * `provider.meta.contextWindow` fallback and inherits `compactKeepTurns` for\n * the preserved tail. Pure — exported so `agent.run` builds the LoopContext\n * field and tests can assert the defaults.\n */\nexport function resolveAutoCompact(\n raw: AutoCompact | undefined,\n opts: { fallbackContextWindow?: number | null, compactKeepTurns?: number } = {},\n): ResolvedAutoCompact | undefined {\n if (raw === undefined || raw === false)\n return undefined\n const cfg = raw === true ? {} : raw\n\n const explicitWindow = typeof cfg.contextWindow === 'number' && Number.isFinite(cfg.contextWindow) && cfg.contextWindow > 0\n ? cfg.contextWindow\n : null\n const fallbackWindow = typeof opts.fallbackContextWindow === 'number' && Number.isFinite(opts.fallbackContextWindow) && opts.fallbackContextWindow > 0\n ? opts.fallbackContextWindow\n : null\n\n const keepTurns = typeof cfg.keepTurns === 'number' && Number.isFinite(cfg.keepTurns) && cfg.keepTurns >= 0\n ? Math.floor(cfg.keepTurns)\n : (typeof opts.compactKeepTurns === 'number' && Number.isFinite(opts.compactKeepTurns) && opts.compactKeepTurns >= 0\n ? Math.floor(opts.compactKeepTurns)\n : 4)\n\n return {\n threshold: typeof cfg.threshold === 'number' ? cfg.threshold : DEFAULT_AUTO_COMPACT_THRESHOLD,\n contextWindow: explicitWindow ?? fallbackWindow,\n keepTurns,\n minGrowthFraction: typeof cfg.minGrowthFraction === 'number' ? cfg.minGrowthFraction : AUTO_COMPACT_MIN_GROWTH_FRACTION,\n restore: cfg.restore !== false,\n maxFailures: typeof cfg.maxFailures === 'number' && Number.isFinite(cfg.maxFailures) && cfg.maxFailures > 0\n ? Math.floor(cfg.maxFailures)\n : DEFAULT_AUTO_COMPACT_MAX_FAILURES,\n }\n}\n\n/**\n * Mutable per-run state for loop-native auto-compaction. Lives in `runLoop`'s\n * scope; threaded into {@link maybeAutoCompact} on each turn boundary.\n */\ninterface AutoCompactRunState {\n /**\n * Effective input-token estimate observed right after the most recent\n * successful compaction — the hysteresis baseline `shouldAutoCompact`\n * compares against so a post-compact bounce doesn't immediately re-fire.\n */\n lastCompactedInputTokens: number | undefined\n /** Consecutive failed compaction attempts this run (reset on success). */\n consecutiveFailures: number\n /** Latched once the circuit breaker trips — no further attempts this run. */\n disabled: boolean\n}\n\n/**\n * Loop-native auto-compaction, evaluated at a turn boundary.\n *\n * When the last turn's EFFECTIVE input usage (`input + cacheRead +\n * cacheCreation`, so prompt caching doesn't mask a full window) crosses the\n * configured fraction of the effective context window, run a real LLM summary\n * over the older turns (`compactConversation`, `scope: 'tail'`), append a\n * `compact-summary` marker plus optional file/skill restoration to\n * `ctx.turns`, and persist. The next `executeTurn` runs\n * `applyCompactSummaryCutoff` over `ctx.turns`, so the provider sees the\n * summary + preserved tail with no extra wiring.\n *\n * Never throws — failures (including aborts and \"nothing to compact\") are\n * swallowed: the run continues on the un-compacted history (the reactive\n * `context_exceeded` net still applies) and the circuit breaker counts the\n * failure so a persistently-failing compaction can't stall the run. Always\n * fires exactly one `compact:end` per `compact:start`.\n */\nasync function maybeAutoCompact(\n ctx: LoopContext,\n turn: number,\n lastTurnUsage: TurnUsage,\n state: AutoCompactRunState,\n): Promise<void> {\n const cfg = ctx.autoCompact\n if (!cfg || state.disabled || ctx.signal.aborted)\n return\n\n const inputTokens = effectiveInputFromTurn(lastTurnUsage)\n const decision = shouldAutoCompact({\n enabled: true,\n threshold: cfg.threshold,\n inputTokens,\n rawContextWindow: cfg.contextWindow,\n alreadyCompacting: false,\n ...(state.lastCompactedInputTokens !== undefined ? { lastCompactedInputTokens: state.lastCompactedInputTokens } : {}),\n minGrowthFraction: cfg.minGrowthFraction,\n })\n if (decision.kind !== 'fire')\n return\n\n // Cheap structural skip: nothing summarizable beyond the preserved tail.\n // Avoids a doomed provider round-trip (and a spurious start/end pair) for\n // the degenerate \"one giant turn nearly fills the window\" case.\n if (ctx.turns.length <= cfg.keepTurns + 1)\n return\n\n const sessionId = ctx.session?.id\n const startedAt = Date.now()\n // Shared coordinates for the start/end pair (avoids re-spelling the\n // run/session/turn keys three times).\n const base = { runId: ctx.runId, ...(sessionId ? { sessionId } : {}), turn, reason: 'auto' as const }\n await settledHook(ctx, 'hook compact:start', ctx.hooks.callHook('compact:start', {\n ...base,\n usedFraction: decision.usedFraction,\n effectiveWindow: decision.effectiveWindow,\n }))\n\n try {\n const result = await compactConversation({\n provider: ctx.provider,\n model: ctx.model,\n turns: ctx.turns,\n scope: 'tail',\n keepTurns: cfg.keepTurns,\n signal: ctx.signal,\n ...(ctx.providerStreamTimeoutMs !== undefined ? { providerStreamTimeoutMs: ctx.providerStreamTimeoutMs } : {}),\n })\n\n // Journaled clock so the marker turn (id + createdAt + the block's\n // `compactedAt`) is byte-identical on replay under durable execution\n // (Restate). Mirrors the rest of the loop's `ctx.clock` usage.\n const summaryTurn = summaryToTurn({\n summary: result.summary,\n replacesTurnIds: result.summarizedTurnIds,\n model: result.model,\n usage: result.usage,\n compactedAt: await ctx.clock.now(),\n id: ctx.clock.randomUUID(),\n })\n\n // Post-compact restoration — re-inject recently-read files + active\n // skills as synthetic tool_call/tool_result pairs so the model keeps its\n // working set. Canonical tool names; the per-turn `rewriteMessagesToWire`\n // handles aliasing. Best-effort: a restoration failure never blocks the\n // summary (the marker still lands).\n let restoredFiles = 0\n let restoredSkills = 0\n let restorationTurns: readonly SessionTurn[] = []\n let restorationTokens = 0\n if (cfg.restore) {\n try {\n const recentFiles = ctx.session ? selectFilesFromSession(ctx.session, ctx.handle.cwd) : []\n const activeSkills = ctx.getActiveSkills?.() ?? []\n const built = await buildPostCompactAttachments({\n recentFiles,\n activeSkills,\n execution: ctx.execution,\n handle: ctx.handle,\n signal: ctx.signal,\n clock: ctx.clock,\n ...(ctx.runId ? { runId: ctx.runId } : {}),\n })\n restorationTurns = built.turns\n restoredFiles = built.restoredFiles\n restoredSkills = built.restoredSkills\n restorationTokens = built.estimatedTokens\n }\n catch (err) {\n if (process.env.ZIDANE_DEBUG)\n process.stderr.write(`[zidane/loop] post-compact restoration failed: ${errorMessage(err)}\\n`)\n }\n }\n\n // Land the marker + restoration in the live turns, then persist so the\n // summary is durable and hosts repaint now (the persist callback fires\n // `session:turns`). In batch-persist mode the callback is absent and the\n // run-end flush covers these turns.\n ctx.turns.push(summaryTurn, ...restorationTurns)\n await ctx.persistTurns?.()\n\n // Estimate the post-compact effective size for the hysteresis baseline +\n // the host's context indicator: the summary's own output tokens plus the\n // restored attachment estimate. Under-counts the system prompt + tail,\n // both small; the next real `turn:after` corrects it precisely.\n const effectiveTokens = (result.usage.output ?? 0) + restorationTokens\n state.lastCompactedInputTokens = effectiveTokens\n state.consecutiveFailures = 0\n\n await settledHook(ctx, 'hook compact:end', ctx.hooks.callHook('compact:end', {\n ...base,\n status: 'success',\n replacedCount: result.summarizedTurnIds.length,\n droppedCount: result.droppedDueToPtl.length,\n restoredFiles,\n restoredSkills,\n summaryModel: result.model,\n effectiveTokens,\n durationMs: Date.now() - startedAt,\n }))\n }\n catch (err) {\n // Aborts end the run on the next loop check — don't burn the breaker on\n // them. Every other failure (provider error, prompt_too_long exhausted,\n // \"nothing to compact\") counts toward the breaker so a persistently-\n // failing trigger goes quiet instead of re-firing every turn.\n const aborted = ctx.signal.aborted || (err instanceof Error && err.name === 'AbortError')\n if (!aborted) {\n state.consecutiveFailures += 1\n if (state.consecutiveFailures >= cfg.maxFailures || err instanceof CompactInvalidInputError)\n state.disabled = true\n }\n await settledHook(ctx, 'hook compact:end', ctx.hooks.callHook('compact:end', {\n ...base,\n status: 'error',\n error: err,\n durationMs: Date.now() - startedAt,\n }))\n }\n}\n\ninterface ResolvedRetryConfig {\n maxAttempts: number\n initialDelayMs: number\n maxDelayMs: number\n}\n\n/**\n * Normalize a user-supplied `RetryConfig` (any field may be missing or invalid)\n * into a fully-populated config the retry loop can use without per-field guards.\n *\n * Invalid values (non-finite, < 1 for `maxAttempts`, < 0 for delays) fall back\n * to defaults rather than throwing — this is a behavior knob, not a validation\n * boundary, and a bad value here shouldn't crash a run.\n */\nfunction resolveRetryConfig(cfg: RetryConfig | undefined): ResolvedRetryConfig {\n const maxAttempts = Number.isFinite(cfg?.maxAttempts) && (cfg!.maxAttempts as number) >= 1\n ? Math.floor(cfg!.maxAttempts as number)\n : RETRY_DEFAULTS.maxAttempts\n const initialDelayMs = Number.isFinite(cfg?.initialDelayMs) && (cfg!.initialDelayMs as number) >= 0\n ? (cfg!.initialDelayMs as number)\n : RETRY_DEFAULTS.initialDelayMs\n const maxDelayMs = Number.isFinite(cfg?.maxDelayMs) && (cfg!.maxDelayMs as number) >= 0\n ? (cfg!.maxDelayMs as number)\n : RETRY_DEFAULTS.maxDelayMs\n return { maxAttempts, initialDelayMs, maxDelayMs }\n}\n\n/**\n * Compute the backoff delay for the upcoming retry.\n *\n * Strategy:\n * - If the server returned `retry-after` / `retry-after-ms`, honor it (capped at `maxDelayMs`).\n * - Otherwise: `initialDelayMs * 2^(attempt-1)`, capped at `maxDelayMs`, then \"full jitter\"\n * (`Math.random() * computed`). Full jitter scatters retries from concurrent clients\n * across the whole window — empirically better than fixed exponential under thundering-herd\n * (per AWS Architecture Blog's analysis of EBO+jitter strategies).\n *\n * `attempt` is the 1-indexed number of the attempt that just *failed*, so the first\n * post-failure delay uses `attempt=1` → `initialDelayMs * 1` before jitter.\n */\nfunction computeRetryDelayMs(\n attempt: number,\n cfg: ResolvedRetryConfig,\n retryAfterMs: number | undefined,\n): number {\n if (retryAfterMs !== undefined && retryAfterMs >= 0)\n return Math.min(retryAfterMs, cfg.maxDelayMs)\n const exponential = cfg.initialDelayMs * 2 ** (attempt - 1)\n const capped = Math.min(exponential, cfg.maxDelayMs)\n return Math.floor(Math.random() * capped)\n}\n\n/**\n * Extract a `retry-after` value (in milliseconds) from an error's `.headers`, if any.\n *\n * Supports both `retry-after-ms` (non-standard but Stainless-emitted; already in ms,\n * always integer) and the standard `retry-after` header (integer seconds, RFC 7231).\n * Returns `undefined` when neither is present, non-numeric, or negative.\n *\n * `parseInt` over `parseFloat` here matches the actual contract — Stainless emits\n * `retry-after-ms` as an integer and RFC 7231 specifies a non-negative integer for\n * the seconds form. The HTTP-date form of `retry-after` is deliberately unsupported\n * (Anthropic + OpenAI both emit numeric seconds; supporting dates invites clock-skew\n * concerns for negligible benefit).\n */\nfunction extractRetryAfterMs(err: unknown): number | undefined {\n if (!err || typeof err !== 'object')\n return undefined\n const headers = (err as { headers?: unknown }).headers\n if (!headers || typeof headers !== 'object')\n return undefined\n // Support both `Map`/`Headers`-like with `.get()` and plain objects.\n const get = (key: string): string | undefined => {\n const h = headers as { get?: (k: string) => string | null | undefined } & Record<string, unknown>\n if (typeof h.get === 'function') {\n const v = h.get(key)\n return typeof v === 'string' ? v : undefined\n }\n const v = h[key]\n return typeof v === 'string' ? v : undefined\n }\n const ms = get('retry-after-ms')\n if (ms !== undefined) {\n const parsed = Number.parseInt(ms, 10)\n if (Number.isFinite(parsed) && parsed >= 0)\n return parsed\n }\n const seconds = get('retry-after')\n if (seconds !== undefined) {\n const parsed = Number.parseInt(seconds, 10)\n if (Number.isFinite(parsed) && parsed >= 0)\n return parsed * 1000\n }\n return undefined\n}\n\n/**\n * `setTimeout` that rejects if the given signal aborts before the timer fires.\n *\n * Resolves with `undefined` on normal completion. Rejects with the signal's\n * `reason` (or a generic AbortError when no reason is set) on abort. Cleans\n * up its `abort` listener in both paths so callers don't leak handlers.\n */\nfunction abortableSleep(ms: number, signal: AbortSignal): Promise<void> {\n if (signal.aborted)\n return Promise.reject(signal.reason ?? new DOMException('Aborted', 'AbortError'))\n return new Promise((resolve, reject) => {\n let timer: ReturnType<typeof setTimeout>\n const onAbort = () => {\n clearTimeout(timer)\n signal.removeEventListener('abort', onAbort)\n reject(signal.reason ?? new DOMException('Aborted', 'AbortError'))\n }\n timer = setTimeout(() => {\n signal.removeEventListener('abort', onAbort)\n resolve()\n }, ms)\n signal.addEventListener('abort', onAbort, { once: true })\n })\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 // Assistant turns live on the durable-execution hot path, so use the\n // AgentClock source that durable workflow adapters make replay-stable.\n const turnId = ctx.clock.randomUUID()\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. Resolve content refs for wire use only. This must happen before\n // repair/sanitization/provider formatting, but never writes back to\n // `ctx.turns`.\n // 4. `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 // 5. Alias rewrite, image sanitization, tail compaction follow.\n const turnsAfterCutoff = applyCompactSummaryCutoff(ctx.turns)\n let canonicalMessages = turnsToMessages(turnsAfterCutoff)\n canonicalMessages = await resolveContentRefsForWire(ctx, canonicalMessages)\n\n if (ctx.elideStaleReads === true) {\n const elision = applyStaleReadElision(canonicalMessages)\n canonicalMessages = elision.messages\n // Defeat the dedup stub for the elided paths — the model can no\n // longer see the original read in the message history, so the next\n // re-read must serve the full body. Registration survives: the\n // read-before-edit gate keeps passing on the post-edit hash.\n markReadStateForElidedPaths(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 compaction. Non-Anthropic OSS-model fallback for what the\n // `context-management-2025-06-27` beta gives Anthropic users server-side.\n // Three modes: `'tail'` (built-in elision), a {@link CompactFunction}\n // (pluggable host strategy — LLM summarizer, semantic compactor, etc.),\n // or `'off'` / `undefined` (skip).\n //\n // Function path is async-aware so an LLM summarizer can do a real\n // provider round-trip; we forward the same `threshold`/`keepTurns`\n // knobs `'tail'` uses plus the pre-compaction byte total so the\n // function can short-circuit cheaply on under-threshold inputs.\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 // Resolve call_id → read_file path and call_id → tool name from the\n // canonical (pre-alias) history. call_ids survive the alias rewrite, so\n // they still match the wire-level tool_result blocks tail compaction\n // operates on — letting compaction report which stubbed results were file\n // reads, and label persist-on-elide stubs with their originating tool.\n const { readPathByCallId, toolNameByCallId } = collectCallMaps(canonicalMessages)\n const compacted = applyTailCompaction(sanitizedMessages, threshold, keep, {\n readPathByCallId,\n toolNameByCallId,\n chunkTurns: COMPACT_CHUNK_TURNS,\n // Persist-on-elide non-read results large enough that a disk stub saves\n // bytes — only when a persist dir is configured. Below this size the\n // recoverable stub would be as big as the content, so the lossy stub is\n // used instead.\n persistElideMinBytes: ctx.persistDir ? PERSISTENCE_PREVIEW_BYTES : 0,\n })\n sanitizedMessages = compacted.messages\n // Invalidate the read-state dedup for any file whose body tail\n // compaction just stubbed — same rationale as stale-read elision above:\n // the model can no longer see the original read, so the next re-read\n // must serve the full body instead of the \"unchanged\" replay stub that\n // would otherwise point it at content no longer on the wire. Run\n // unconditionally (not gated on `elideStaleReads`): tail compaction\n // elides regardless of that setting.\n markReadStateForElidedPaths(ctx, ctx.handle.cwd, compacted.elidedReadPaths)\n // Persist-on-elide: upgrade the lossy stub to a recoverable\n // `<persisted-output … path=…>` stub for non-read results, so a compacted\n // result stays retrievable by reading its path rather than being lost.\n if (ctx.persistDir && compacted.persistableElided.length > 0) {\n sanitizedMessages = await persistElidedToolResults(sanitizedMessages, compacted.persistableElided, {\n persistDir: ctx.persistDir,\n maxBytes: ctx.persistMaxBytes,\n writeBlob: executionBlobWriter(ctx),\n })\n }\n }\n else if (typeof ctx.compactStrategy === 'function') {\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 // Model-visible size (attachments → short markers), matching\n // `applyTailCompaction`'s budget so custom strategies see the same number\n // and aren't tripped by large base64 attachments in tool results.\n let totalBytes = 0\n for (const msg of sanitizedMessages) {\n for (const block of msg.content) {\n if (block.type === 'tool_result')\n totalBytes += toolOutputBudgetByteLength(block.output)\n }\n }\n try {\n const compacted = await ctx.compactStrategy(sanitizedMessages, {\n threshold,\n keepTurns: keep,\n totalBytes,\n })\n if (Array.isArray(compacted))\n sanitizedMessages = compacted\n }\n catch (err) {\n // Compactor failures should not kill the turn — degrade to the\n // uncompacted payload. The pair-repair pass downstream still has\n // a chance to fix any structural damage. There is no dedicated\n // hook for host-strategy failures (`stream:error` is reserved for\n // provider stream errors), so the error is logged to stderr only.\n console.error('[zidane] compactStrategy function threw:', err)\n }\n }\n\n // Media age-out (wire-only): drop base64 for image/document blocks older\n // than the keep window, replacing each with a re-resolvable marker. Runs\n // independently of `compactStrategy` — that path only stubs `tool_result`\n // text and never trims media bytes, so without this an image-heavy session\n // re-sends every screenshot's base64 on every turn. The model can pull any\n // aged-out image back via `read_file path=\"attachment://<hash>\"`.\n if (typeof ctx.mediaKeepTurns === 'number' && ctx.mediaKeepTurns >= 0 && ctx.tools.read_file) {\n const { readPathByCallId } = collectCallMaps(canonicalMessages)\n const aged = applyMediaAgeout(sanitizedMessages, ctx.mediaKeepTurns, {\n chunkTurns: COMPACT_CHUNK_TURNS,\n readPathByCallId,\n readFileToolName: toWireName('read_file', ctx.aliasMaps),\n })\n sanitizedMessages = aged.messages\n markReadStateForElidedPaths(ctx, ctx.handle.cwd, aged.elidedReadPaths)\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 ...(ctx.modelOptions ? { modelOptions: ctx.modelOptions } : {}),\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 boundedHook(ctx, 'hook context:transform', ctx.hooks.callHook('context:transform', transformCtx))\n streamOptions.messages = await resolveContentRefsForWire(ctx, 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 boundedHook(ctx, 'hook system:transform', ctx.hooks.callHook('system:transform', systemCtx))\n streamOptions.system = systemCtx.system\n\n await boundedHook(ctx, 'hook turn:before', ctx.hooks.callHook('turn:before', { turn, turnId, options: streamOptions }))\n\n // Defense-in-depth: `turn:before` exposes mutable `options.messages` for\n // hosts that need to splice last-mile context (per-turn reminders,\n // dynamic system-as-user injections, audit-tag wrappers). Any such\n // mutation lands AFTER the earlier `applyPairingRepair` + tail guard, so\n // re-run both. Both are idempotent on healthy input (return the same\n // reference, fire zero `pairing:repair` events) — when the hook didn't\n // touch messages, this is free. When it did, this is the difference\n // between a clean wire send and a `tool_result must be preceded by a\n // tool_call with the same toolCallId` 400 from a corrupted re-splice.\n streamOptions.messages = await resolveContentRefsForWire(ctx, streamOptions.messages)\n streamOptions.messages = applyPairingRepair(ctx, streamOptions.messages, turnId)\n streamOptions.messages = ensureEndsWithUserMessage(streamOptions.messages, ctx.provider)\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 settledHook(ctx, 'hook stream:start', ctx.hooks.callHook('stream:start', { turnId, startedAt: streamStartedAt }))\n\n // Stream-callback hooks fire from synchronous provider callbacks, so they\n // can't be awaited inline. Serialize them onto a single chain so:\n // 1. handler rejections are observed (a throwing `stream:text` handler\n // previously became an unhandled promise rejection), and\n // 2. async handlers run in delta order and are fully settled before\n // `stream:end` fires (no interleaving past the stream boundary).\n // Handler errors never kill the stream — they're observability concerns;\n // surfaced under ZIDANE_DEBUG only.\n let streamHookChain: Promise<void> = Promise.resolve()\n const enqueueStreamHook = (fire: () => Promise<unknown> | void): void => {\n streamHookChain = streamHookChain.then(fire).then(\n () => {},\n (err) => {\n if (process.env.ZIDANE_DEBUG)\n process.stderr.write(`[zidane/loop] stream hook handler threw: ${errorMessage(err)}\\n`)\n },\n )\n }\n\n const retryCfg = resolveRetryConfig(ctx.retry)\n let result\n let attempt = 0\n // Bounded counter for reactive context-overflow recovery — indexes\n // CONTEXT_OVERFLOW_KEEP_TURNS. Separate from `attempt` (network retry) so an\n // overflow recovery doesn't consume the backoff budget and vice-versa.\n let overflowRecoveries = 0\n // Bounded retry-with-backoff. Triggers only when:\n // 1. The provider classifies the error as `retryable: true`, AND\n // 2. Nothing has streamed to the user yet (text + thinking both empty).\n // Condition (2) avoids replaying over partial output the user already saw;\n // the proper fix for that case is prefill-continuation (replay with the\n // partial assistant response prefilled so the model continues from there),\n // which is a separate, larger change.\n while (true) {\n attempt += 1\n try {\n result = await streamWithWatchdogs(\n ctx,\n streamOptions,\n {\n onText(delta) {\n markTurnTtft()\n currentText += delta\n // Snapshot the accumulated text now — the chained handler may\n // run after later deltas have already grown `currentText`.\n const text = currentText\n enqueueStreamHook(() => ctx.hooks.callHook('stream:text', { delta, text, turnId }))\n },\n onThinking(delta) {\n markTurnTtft()\n currentThinking += delta\n const thinking = currentThinking\n enqueueStreamHook(() => ctx.hooks.callHook('stream:thinking', { delta, thinking, turnId }))\n },\n onServerToolUse({ id, name, input }) {\n enqueueStreamHook(() => ctx.hooks.callHook('stream:server_tool_use', { id, name, input, turnId }))\n },\n onServerToolResult({ toolUseId, toolName, content }) {\n enqueueStreamHook(() => ctx.hooks.callHook('stream:server_tool_result', { toolUseId, toolName, content, turnId }))\n },\n onOAuthRefresh(refreshCtx) {\n return ctx.hooks.callHook('oauth:refresh', refreshCtx)\n },\n },\n turnId,\n )\n break\n }\n catch (caught) {\n // `terminalErr` is what eventually feeds the placeholder/`stream:error`/\n // `wrapProviderError` path. Starts as the caught error; gets replaced by\n // a synthesized AbortError when `agent.abort()` lands mid-backoff so the\n // existing abort handling (`AgentAbortedError`, abort placeholder) kicks in.\n let terminalErr: unknown = caught\n let wasAborted = ctx.signal.aborted || (caught instanceof Error && caught.name === 'AbortError')\n const classified = !wasAborted ? ctx.provider.classifyError?.(caught) : null\n const isRetryable = classified?.kind === 'provider_error' && classified.retryable === true\n // No partial output yet — safe to discard the half-built stream and re-issue.\n const cleanSlate = currentText === '' && currentThinking === ''\n const canRetry = !wasAborted && isRetryable && cleanSlate && attempt < retryCfg.maxAttempts\n\n if (canRetry) {\n const meta = extractStreamErrorMeta(caught)\n const delayMs = computeRetryDelayMs(attempt, retryCfg, extractRetryAfterMs(caught))\n await settledHook(ctx, 'hook stream:retry', ctx.hooks.callHook('stream:retry', {\n turnId,\n attempt,\n nextAttempt: attempt + 1,\n delayMs,\n err: caught,\n ...meta,\n }))\n try {\n await abortableSleep(delayMs, ctx.signal)\n continue\n }\n catch {\n // Aborted mid-backoff. Fall through to the terminal-error path\n // below with a synthesized AbortError so the existing handling\n // takes over (placeholder turn, AgentAbortedError).\n const abortErr = new Error('Agent run aborted')\n abortErr.name = 'AbortError'\n terminalErr = abortErr\n wasAborted = true\n }\n }\n\n // Reactive context-overflow recovery. The request was rejected for\n // exceeding the model's window (`context_exceeded` is pre-stream, so\n // `cleanSlate` always holds here). Emergency-compact older `tool_result`\n // bodies and re-issue — works even when `compactStrategy` is off, since\n // we call `applyTailCompaction` directly. `applyTailCompaction` returns\n // the SAME array reference when nothing was elided, our reliable\n // \"did it shrink?\" signal: we try progressively smaller keep-floors and,\n // when none can shrink further (text-heavy overflow), fall through to the\n // terminal path so the error still surfaces (no infinite loop).\n const isContextOverflow = !wasAborted && classified?.kind === 'context_exceeded'\n if (isContextOverflow && cleanSlate && overflowRecoveries < CONTEXT_OVERFLOW_KEEP_TURNS.length) {\n const { readPathByCallId, toolNameByCallId } = collectCallMaps(canonicalMessages)\n let recoveredMessages: SessionMessage[] | undefined\n const elidedReadPaths: string[] = []\n for (let k = overflowRecoveries; k < CONTEXT_OVERFLOW_KEEP_TURNS.length; k++) {\n const keepFloor = CONTEXT_OVERFLOW_KEEP_TURNS[k]\n // Strip base64 media FIRST — when an overflow is media-driven, the\n // image/PDF payloads dominate and `applyTailCompaction` can't touch\n // them (it only stubs `tool_result` text and never counts media\n // bytes). Then stub older tool_result text. Either shrinking counts\n // as a recovery (media-only overflows produce no tail change).\n const aged = applyMediaAgeout(streamOptions.messages, keepFloor, {\n chunkTurns: 1,\n readPathByCallId,\n readFileToolName: ctx.tools.read_file ? toWireName('read_file', ctx.aliasMaps) : null,\n })\n const compacted = applyTailCompaction(aged.messages, 0, keepFloor, {\n readPathByCallId,\n toolNameByCallId,\n chunkTurns: 1,\n })\n if (compacted.messages !== streamOptions.messages) {\n recoveredMessages = compacted.messages\n elidedReadPaths.push(...aged.elidedReadPaths, ...compacted.elidedReadPaths)\n overflowRecoveries = k + 1\n break\n }\n }\n if (recoveredMessages) {\n // Invalidate read-state dedup for elided reads so a subsequent\n // `read_file` re-serves the full body instead of an \"unchanged\" stub\n // pointing at content no longer on the wire (mirrors the tail path).\n markReadStateForElidedPaths(ctx, ctx.handle.cwd, elidedReadPaths)\n // Stubbing rewrote tool_result bodies; re-run the wire-safety passes\n // so the shrunk list still pairs cleanly and ends on a user message.\n let repaired = applyPairingRepair(ctx, recoveredMessages, turnId)\n repaired = ensureEndsWithUserMessage(repaired, ctx.provider)\n streamOptions.messages = repaired\n await settledHook(ctx, 'hook context:overflow', ctx.hooks.callHook('context:overflow', { turnId, attempt: overflowRecoveries, err: caught }))\n continue\n }\n }\n\n // Terminal failure path: 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 // Settle any in-flight stream-callback handlers before the terminal\n // hooks fire — `stream:error` / `turn:after` must not race a still-\n // running `stream:text` handler. The chain never rejects.\n await streamHookChain\n\n const errorUsage: TurnUsage = { input: 0, output: 0 }\n const placeholderText = wasAborted\n ? '[⏹ Streaming was aborted.]'\n : buildStreamErrorPlaceholder(terminalErr)\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. On an abort we skip `stream:error`\n // (a cancel is not a provider error); the abort itself surfaces as\n // `agent:abort`, fired at the run boundary in `agent.ts` after this\n // throw propagates out (the loop's between-turn checks are skipped on a\n // mid-stream abort).\n if (!wasAborted) {\n const meta = extractStreamErrorMeta(terminalErr)\n await settledHook(ctx, 'hook stream:error', ctx.hooks.callHook('stream:error', { err: terminalErr, turnId, ...meta }))\n }\n await settledHook(ctx, 'hook turn:after', ctx.hooks.callHook('turn:after', {\n turn,\n turnId,\n usage: errorUsage,\n message: errorTurn,\n aborted: wasAborted,\n toolCounts: { turn: Object.freeze({}), run: Object.freeze({ ...ctx.runToolCounts }) },\n cumulativeUsage: buildCumulativeUsage(priorUsage, errorUsage),\n }))\n throw wrapProviderError(terminalErr, ctx)\n }\n }\n\n // Settle the serialized stream-callback handlers before `stream:end` so\n // an async `stream:text` handler can never resolve after the end event.\n await streamHookChain\n\n if (currentText) {\n await settledHook(ctx, 'hook stream:end', 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 let canonicalToolCalls = result.toolCalls.map(tc => ({\n ...tc,\n name: toCanonicalName(tc.name, ctx.aliasMaps),\n }))\n let canonicalContent = rewriteContentToCanonical(\n result.assistantMessage?.content ?? [],\n ctx.aliasMaps,\n )\n\n // Persist-time duplicate-id healing. Providers that reuse tool_use ids\n // across turns (per-turn `call_0` counters, retry-replay hosts, Kimi-style\n // deployments) would otherwise persist a collision the wire-time pairing\n // pass can only repair by STRIPPING — erasing the call + result from the\n // model's view every subsequent turn (no forward progress) and\n // re-accumulating repairs quadratically. Re-minting here keeps the pair\n // intact end-to-end: dispatch, the persisted assistant turn, and the\n // tool_result all carry the fresh id.\n //\n // Disabled in strict mode: `strictToolPairing` consumers (training-data\n // collectors) contract on duplicate-id corruption THROWING at the next\n // wire send so the transcript can be quarantined — silently healing it\n // here would swallow that signal. Non-strict runs get the heal.\n if (canonicalToolCalls.length > 0 && !ctx.strictToolPairing) {\n const priorIds = new Set<string>()\n for (const t of ctx.turns) {\n for (const block of t.content) {\n if (block.type === 'tool_call')\n priorIds.add(block.id)\n }\n }\n const reminted = remintDuplicateToolCallIds(canonicalToolCalls, canonicalContent, priorIds)\n if (reminted.reminted.length > 0) {\n canonicalToolCalls = reminted.toolCalls\n canonicalContent = reminted.content\n for (const r of reminted.reminted) {\n void Promise.resolve(ctx.hooks.callHook('pairing:repair', {\n mode: 'duplicate-tool-use-remint',\n callId: r.oldId,\n messageIndex: ctx.turns.length,\n turnId,\n })).catch(() => {})\n }\n }\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 settledHook(ctx, 'hook turn:after', ctx.hooks.callHook('turn:after', {\n turn,\n turnId,\n usage: result.usage,\n message: assistantTurn,\n // `signal.aborted` captured at fire time. On this (post-stream, pre-tool-\n // dispatch) path it's true only when an abort already landed by now — an\n // external cancel or a `stream:end`/hook-driven `agent.abort()` in the\n // window since the stream closed. The streaming-abort case is carried by\n // the error-path `turn:after` above (`aborted: wasAborted`); a tool-gate /\n // repeat-guard abort trips AFTER this fires (during the tool batch below)\n // and is observed by the loop's post-turn check, which fires `agent:abort`\n // — that turn's `turn:after` carries `finishReason: 'tool-calls'`, never\n // `'stop'`, so a Stop-hook consumer keyed on `'stop'` won't misfire on it.\n aborted: ctx.signal.aborted,\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 enforcement = await enforceSchemaOutput(ctx, turnId)\n\n // Combine the main turn's usage with the schema call's. Spread the\n // main usage first so non-additive metadata (finishReason, modelId,\n // timeToFirstTokenMs) survives, then sum every additive numeric\n // field across both calls — dropping cost/cache numbers here would\n // under-count `maxCostUsd` budget checks, cache stats, and the\n // `cumulativeUsage` consumers accumulate from this return.\n const sumOptional = (a: number | undefined, b: number | undefined): { has: boolean, value: number } =>\n ({ has: a !== undefined || b !== undefined, value: (a ?? 0) + (b ?? 0) })\n const cacheRead = sumOptional(result.usage.cacheRead, enforcement.usage.cacheRead)\n const cacheCreation = sumOptional(result.usage.cacheCreation, enforcement.usage.cacheCreation)\n const thinkingTokens = sumOptional(result.usage.thinking, enforcement.usage.thinking)\n const cost = sumOptional(result.usage.cost, enforcement.usage.cost)\n return {\n ended: true,\n turnId,\n usage: {\n ...result.usage,\n input: result.usage.input + enforcement.usage.input,\n output: result.usage.output + enforcement.usage.output,\n ...(cacheRead.has ? { cacheRead: cacheRead.value } : {}),\n ...(cacheCreation.has ? { cacheCreation: cacheCreation.value } : {}),\n ...(thinkingTokens.has ? { thinking: thinkingTokens.value } : {}),\n ...(cost.has ? { cost: cost.value } : {}),\n },\n output: enforcement.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 //\n // We also classify the turn as `pauseEmpty: true` here. `runLoop` reads\n // that flag to track consecutive empty pauses and abort the loop with\n // `finishReason: 'pause'` after `behavior.maxConsecutivePauseTurns`, so\n // a stuck model returning empty pauses indefinitely doesn't burn tokens\n // forever. A pause turn that DID produce text or thinking is not\n // classified empty — those resume cleanly without a cap.\n const finishedEmptyPause = canonicalToolCalls.length === 0\n && result.usage.finishReason === 'pause'\n && currentText.length === 0\n && currentThinking.length === 0\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 {\n ended: false,\n turnId,\n usage: result.usage,\n ...(finishedEmptyPause ? { pauseEmpty: true as const } : {}),\n }\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: ctx.clock.randomUUID(),\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 settledHook(ctx, 'hook tool-results:after', ctx.hooks.callHook('tool-results:after', {\n turn,\n turnId,\n message: toolResultsTurn,\n results: toolResults,\n }))\n\n // Enforce per-turn text-output budget. Sum the post-transform text-equivalent\n // bytes of every tool result; attachment blocks count as short markers so\n // vision/PDF reads do not trip the text summarization nudge by themselves.\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 (typeof name === 'string' && excludeSet.has(name))\n return sum\n }\n return sum + toolOutputBudgetByteLength(r.content)\n },\n 0,\n )\n if (totalBytes > ctx.toolOutputBudget) {\n const warning = `[Tool output budget exceeded: ${totalBytes} text-equivalent 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 return sanitizeToolOutputForProvider(provider, output)\n}\n\nfunction sanitizeToolOutputForProvider(\n provider: Provider,\n output: string | ToolResultContent[],\n): string | ToolResultContent[] {\n if (typeof output === 'string')\n return output\n const caps = provider.meta.capabilities\n // Fast path: provider accepts every media kind we might carry in a tool\n // result. Preserve the historical image behavior (`vision` omitted means\n // \"let the provider try\"), but audio/video/documents are opt-in because their\n // wire shapes are less portable and wrong routing is more likely to 400.\n if (caps?.vision !== false && caps?.audio === true && caps?.video === true && caps?.documents === true)\n return output\n\n let changed = false\n const sanitized = output.map((block): ToolResultContent => {\n if (block.type === 'image' && caps?.vision === false) {\n changed = true\n return { type: 'text', text: IMAGE_OMITTED_MARKER }\n }\n if (block.type === 'audio' && caps?.audio !== true) {\n changed = true\n return { type: 'text', text: AUDIO_OMITTED_MARKER }\n }\n if (block.type === 'video' && caps?.video !== true) {\n changed = true\n return { type: 'text', text: VIDEO_OMITTED_MARKER }\n }\n if (block.type === 'document' && caps?.documents !== true) {\n changed = true\n return { type: 'text', text: documentOmittedMarker(block) }\n }\n return block\n })\n\n if (!changed)\n return output\n if (sanitized.every((block): block is Extract<ToolResultContent, { type: 'text' }> => block.type === 'text'))\n return sanitized.map(block => block.text).join('\\n')\n return sanitized\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.toolCancels?.register(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 // registry on destroy) is a no-op.\n ctx.toolCancels?.unregister(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 const result = await handleUndispatchableTool(ctx, {\n turnId,\n callId,\n name: call.name,\n displayName,\n input: effectiveInput,\n runToolCounts,\n reason: `Unknown tool: ${call.name}`,\n })\n return { result }\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 // Structured side-effect outcome a mutating tool reports via\n // `ctx.reportOutcome`. Captured here and stamped onto `tool:after` so hosts\n // get a byte-level signal without parsing the result prose. Last call wins.\n let reportedOutcome: ToolOutcome | undefined\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. See {@link composePerCallSignal} for why this is manual\n // listener composition rather than `AbortSignal.any` — `dispose()` in the\n // inner `finally` below detaches it from the long-lived run signal.\n const composedSignal = composePerCallSignal(ctx.signal, perCallAbort.signal)\n const childSignal = composedSignal.signal\n\n try {\n const toolCtx: ToolContext = {\n provider: ctx.provider,\n model: ctx.model,\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 reportOutcome: (outcome: ToolOutcome) => {\n reportedOutcome = outcome\n },\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 // Also guard the run-level abort (`ctx.signal`). The body already gets\n // `childSignal` (= ctx.signal ∪ perCallAbort), but a body that IGNORES\n // its signal would otherwise wedge `drain()` forever on a full run-abort\n // — neither `bodyPromise` nor `cancellationPromise` (which only watches\n // perCallAbort) would ever settle.\n //\n // We do NOT reject immediately on abort: well-behaved tools observe\n // `childSignal` and finalize their own bookkeeping in an abort-driven\n // `finally` (e.g. `spawn` awaits its child's session-persisting block;\n // see `raceWithTimeout` in tools/spawn.ts). Cutting them off here would\n // orphan that cleanup. Instead we give the body `RUN_ABORT_GRACE_MS` to\n // settle; only a body still running after the grace hits the forced\n // rejection. The AbortError routes through the `isAbortError &&\n // ctx.signal.aborted` branch below → canonical interrupt/cascade message.\n // The detached `bodyPromise` keeps running but is harmless (its `.catch`\n // above swallows the late settle). NOTE: native path only — the durable\n // path's `started.start.promise` deliberately avoids extra subscriptions\n // (see executeDurable), and is untouched here.\n let removeRunAbortListener: (() => void) | undefined\n let runAbortTimer: ReturnType<typeof setTimeout> | undefined\n const runAbortPromise = new Promise<never>((_, reject) => {\n // Reject with an AbortError-named error so the catch branch below routes\n // it as an abort (the cascade-vs-interrupt choice reads `ctx.signal.reason`).\n const forceReject = () => reject(new DOMException('Aborted', 'AbortError'))\n const armGrace = () => {\n runAbortTimer = setTimeout(forceReject, runAbortGraceMs())\n // Don't keep the event loop alive solely for the forced rejection.\n runAbortTimer.unref?.()\n }\n if (ctx.signal.aborted) {\n armGrace()\n return\n }\n ctx.signal.addEventListener('abort', armGrace, { once: true })\n removeRunAbortListener = () => ctx.signal.removeEventListener('abort', armGrace)\n })\n\n try {\n output = await Promise.race([bodyPromise, cancellationPromise, runAbortPromise])\n }\n finally {\n // Detach both abort listeners + the grace timer on the happy path so a\n // subsequent cancel (race-condition-ish: body resolved, then user clicks\n // cancel before we've returned) doesn't reject a promise nobody awaits.\n // Disposing the composed child signal here removes the listener it placed\n // on the long-lived run signal — the body has settled, so nothing else\n // observes it.\n removeAbortListener?.()\n removeRunAbortListener?.()\n if (runAbortTimer !== undefined)\n clearTimeout(runAbortTimer)\n composedSignal.dispose()\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 if (isAbortError && ctx.signal.aborted) {\n // Run-level / sibling / shell-cascade abort: the body observed\n // `ctx.signal` (here the batch's `siblingAbort` signal) and unwound with\n // an AbortError. This is NOT a tool failure, so we must NOT fire\n // `tool:error` or surface `Tool error: AbortError` to the model —\n // a signal-unaware tool on the same abort gets the canonical interrupt\n // sentinel via the batch's `fillUnstarted`, and a signal-aware tool that\n // throws must land on the same wire result. Mirror the batch's\n // `cancelMessage()`: cascade → cascade message, otherwise interrupt.\n output = ctx.signal.reason === SHELL_CASCADE_REASON\n ? SHELL_CASCADE_CANCEL_MESSAGE\n : INTERRUPT_MESSAGE_FOR_TOOL_USE\n isError = true\n }\n else {\n const error = err instanceof Error ? err : new Error(String(err))\n if (ctx.shouldRethrowToolError?.(error))\n throw error\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 ...(reportedOutcome ? { outcome: reportedOutcome } : {}),\n })\n\n return {\n result: {\n id: callId,\n content: emitted.output,\n ...(emitted.isError ? { isError: true } : {}),\n },\n }\n}\n\n/**\n * Shared handling for calls the dispatcher cannot route to an executable\n * tool — a hallucinated / dangling tool name (model invented it, MCP\n * server dropped, alias removed), or in durable mode a tool that lacks a\n * `durableExecute` adapter. Gives 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 *\n * Fires `tool:before` with `unknown: true` FIRST so UI consumers\n * (`tool:before` → render a card) still see the model's attempt. Pre-\n * change, `tool:unknown` replaced `tool:before` entirely and hosts\n * had to synthesize the card from `tool:dispatched`. The pair is\n * `tool:before(unknown=true) → tool:unknown → tool:error` (the\n * first and third are guaranteed; `suppressError` skips the third).\n * See AgentHooks['tool:before'].unknown for the pairing contract.\n * `tool:dispatched` (outcome `'unknown'`) closes the sequence so the\n * \"every call closes with exactly one tool:dispatched\" invariant holds on\n * both the native and durable paths. `tool:after` / `tool:transform`\n * deliberately do NOT fire — see the gate-block branch in\n * {@link runSingleToolDispatch} for the backward-compat rationale.\n */\nasync function handleUndispatchableTool(\n ctx: LoopContext,\n params: {\n turnId: string\n callId: string\n name: string\n displayName: string\n input: Record<string, unknown>\n runToolCounts: Readonly<Record<string, number>>\n /** Human-readable cause — becomes the `tool:error` Error message and the `Tool error: …` result text. */\n reason: string\n },\n): Promise<ToolResult> {\n const { turnId, callId, name, displayName, input, runToolCounts, reason } = params\n\n await ctx.hooks.callHook('tool:before', {\n ...buildToolHookBase(ctx, turnId, callId, name, displayName, input),\n runToolCounts,\n unknown: true,\n })\n\n const unknownCtx: ToolHookContext & {\n result?: string | ToolResultContent[]\n suppressError: boolean\n } = {\n ...buildToolHookBase(ctx, turnId, callId, name, displayName, input),\n suppressError: false,\n }\n await ctx.hooks.callHook('tool:unknown', unknownCtx)\n\n const content = unknownCtx.result ?? `Tool error: ${reason}`\n // Treat the call 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 call always carries `Tool error: …`\n // text and should ride the `is_error` channel so the model sees the\n // wire-level error flag.\n const isError = unknownCtx.result === undefined\n\n if (!unknownCtx.suppressError) {\n const err = new Error(reason)\n await ctx.hooks.callHook('tool:error', {\n ...buildToolHookBase(ctx, turnId, callId, name, displayName, input),\n error: err,\n })\n }\n await fireDispatched(ctx, {\n turnId,\n callId,\n name,\n displayName,\n input,\n outcome: 'unknown',\n runToolCounts,\n })\n return { id: callId, content, ...(isError ? { isError: true } : {}) }\n}\n\n/**\n * Fire `tool:dispatched` with the resolved path discriminator. Every code\n * path in {@link executeSingleTool} that produces a tool_result calls this\n * exactly once — so a `tool:dispatched` precedes every terminal event. The\n * terminal that closes a dispatch depends on `outcome`: `execute` closes with\n * `tool:after` OR `tool:cancelled` (in-flight cancel), `gate-substitute` with\n * `tool:after`, and `gate-block`/`invalid-input`/`unknown` carry their result\n * on the dispatch path itself (no `tool:after`). See the `tool:dispatched`\n * hook doc in `agent.ts` for the full per-outcome pairing. The strict\n * direction holds unconditionally: no `tool:after` fires without a dispatch.\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 outcome?: ToolOutcome\n },\n): Promise<{ output: string | ToolResultContent[], isError: boolean }> {\n const { turnId, callId, name, displayName, input, runToolCounts, coercions, outcome } = 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 // Defensive coercion after `tool:transform`: transforms can flip `isError`\n // in either direction, so compute the final structured outcome only after\n // the final wire error bit is known. A success outcome on an error result\n // would make host loop guards read false progress.\n const finalOutcome: ToolOutcome | undefined\n = isError && outcome && outcome !== 'failed' ? 'failed' : outcome\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 writeBlob: executionBlobWriter(ctx),\n })\n if (outcome.kind === 'persisted') {\n output = outcome.output\n }\n // `skip` outcomes leave `output` untouched. On a write failure the helper\n // still returns a bounded failure stub: substitute it so an enabled\n // persistence strategy never lets the full payload ride the next request.\n // The tool itself succeeded, so `isError` stays as the tool reported it.\n else if (outcome.kind === 'error') {\n if (process.env.ZIDANE_DEBUG)\n process.stderr.write(`[zidane/loop] persistence write failed for ${name}/${callId}: ${outcome.error.message}\\n`)\n output = outcome.output\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 ...(finalOutcome ? { outcome: finalOutcome } : {}),\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\ntype DurableToolOutput = string | ToolResultContent[]\n\ninterface SerializedToolFailure {\n name: string\n message: string\n code?: number\n constructorName?: string\n}\n\ntype DurableToolSettlement\n = | { ok: true, value: DurableToolOutput }\n | { ok: false, error: SerializedToolFailure }\n\ninterface DurableToolStart {\n index: number\n call: ToolCall\n turnId: string\n callId: string\n name: string\n displayName: string\n input: Record<string, unknown>\n runToolCounts: Readonly<Record<string, number>>\n perCallAbort: AbortController\n /** Detach the composed child signal from the long-lived run signal — called once the durable call settles. */\n disposeSignal: () => void\n coercions?: readonly string[]\n promise: Promise<DurableToolOutput>\n reportedOutcome: () => ToolOutcome | undefined\n}\n\ntype MappedDurablePromise<T> = Promise<T> & {\n map: <U>(mapper: (value?: T, failure?: unknown) => U) => MappedDurablePromise<U>\n}\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 (ctx.toolBatchExecutor)\n return executeCustomToolBatch(ctx, toolCalls, turnId, ctx.toolBatchExecutor)\n return executeNativeToolBatch(ctx, toolCalls, turnId)\n}\n\nasync function executeCustomToolBatch(\n ctx: LoopContext,\n toolCalls: ToolCall[],\n turnId: string,\n executor: ToolBatchExecutor,\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 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 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 const childCtx: LoopContext = { ...ctx, signal: siblingAbort.signal, toolBatchExecutor: undefined }\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 execute = async (index: number): Promise<ToolResult> => {\n const call = toolCalls[index]\n try {\n const { result } = await executeSingleTool(childCtx, call, turnId)\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 return result\n }\n catch (err) {\n if (ctx.shouldRethrowToolError?.(err))\n throw err\n const isAbort = siblingAbort.signal.aborted\n || ctx.signal.aborted\n || (err instanceof Error && err.name === 'AbortError')\n return {\n id: call.id,\n content: isAbort ? cancelMessage() : `Error: ${errorMessage(err)}`,\n isError: true,\n }\n }\n }\n\n const executeDurable = async (\n indices: readonly number[],\n all: <T>(values: readonly Promise<T>[]) => Promise<readonly T[]>,\n ): Promise<ToolResult[]> => {\n const starts: DurableToolStart[] = []\n const results = new Map<number, ToolResult>()\n\n try {\n for (const index of indices) {\n const started = await startDurableToolExecution(childCtx, toolCalls[index], turnId, index)\n if (started.kind === 'result') {\n results.set(index, started.result)\n }\n else {\n // Deliberately NO `started.start.promise.catch(() => {})` here.\n // On a durable runtime promise (Restate's `RestatePromise`),\n // `.catch()` is not a passive subscription — it starts the SDK's\n // internal progress-polling loop for that promise. The combinator\n // `all()` below starts its own loop over the same journal handles,\n // and two concurrent loops collide on the SDK's single-consumer\n // progress channel (\"awaitNext already pending\"), failing the\n // invocation attempt deterministically on every replay.\n //\n // The unhandled-rejection guard the native path needs is also\n // unnecessary here: an unsubscribed durable promise is inert —\n // its underlying completion is only pumped once a consumer\n // subscribes, so a floating one cannot surface as an unhandled\n // rejection (run-closure failures are captured by the runtime's\n // own tracker).\n starts.push(started.start)\n }\n }\n\n if (starts.length > 0) {\n const settledPromises: MappedDurablePromise<DurableToolSettlement>[] = []\n for (const start of starts) {\n if (!hasDurableMap(start.promise)) {\n throw new TypeError(\n `Tool ${start.name} is marked durable but did not return a mappable durable promise. `\n + 'Wrap the tool with the runtime adapter before enabling durable parallel scheduling.',\n )\n }\n // Shell cascade must trip WHILE siblings are still in flight —\n // by the time `all()` resolves below, every sibling has already\n // settled and an abort there can cancel nothing. The cascade is\n // folded INTO the mapper (not a separate `settled.then(...)`\n // observer): the mapper runs at settlement time as part of the\n // combinator's own consumption, so no second subscription — and\n // therefore no second SDK polling loop — is ever created (see\n // the \"awaitNext already pending\" note above).\n const isShell = start.call.name === SHELL_TOOL_NAME\n const settled = start.promise.map((value?: DurableToolOutput, failure?: unknown): DurableToolSettlement => {\n if (failure !== undefined) {\n if (isShell && !siblingAbort.signal.aborted)\n siblingAbort.abort(SHELL_CASCADE_REASON)\n return { ok: false, error: serializeToolFailure(failure) }\n }\n return { ok: true, value: value ?? '' }\n })\n settledPromises.push(settled)\n }\n const settlements = await all(settledPromises)\n for (let i = 0; i < starts.length; i++) {\n const start = starts[i]\n const settlement = settlements[i]\n if (settlement.ok) {\n const result = await completeDurableToolExecution(childCtx, start, settlement.value)\n results.set(start.index, result)\n // Late cascade for errors that only surface during completion\n // (e.g. a `tool:transform` hook flipping `isError`). Every\n // sibling has settled by now, so this cannot cancel in-flight\n // work — it only stamps the cascade reason so later\n // `failDurableToolExecution` calls classify their AbortErrors\n // consistently. The live (in-flight) cascade is the mapper-side\n // cascade folded into `settled` above.\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 && start.call.name === SHELL_TOOL_NAME\n && !siblingAbort.signal.aborted\n ) {\n siblingAbort.abort(SHELL_CASCADE_REASON)\n }\n }\n else {\n const result = await failDurableToolExecution(\n childCtx,\n start,\n errorFromSerializedFailure(settlement.error),\n cancelMessage,\n )\n results.set(start.index, result)\n }\n }\n }\n\n return indices.map(index => results.get(index) ?? {\n id: toolCalls[index].id,\n content: 'Error: durable tool execution did not produce a result',\n isError: true,\n })\n }\n finally {\n for (const start of starts) {\n childCtx.toolCancels?.unregister(start.callId)\n start.disposeSignal()\n }\n }\n }\n\n const executionCtx: ToolBatchExecutionContext = {\n toolCalls,\n turnId,\n maxConcurrentTools: maxConcurrent,\n signal: ctx.signal,\n steeringQueue: ctx.steeringQueue,\n isConcurrencySafe: index => safe[index] === true,\n execute,\n canExecuteDurably: index => typeof ctx.tools[toolCalls[index].name]?.durableExecute === 'function',\n executeDurable,\n interruptedResult: index => ({\n id: toolCalls[index].id,\n content: INTERRUPT_MESSAGE_FOR_TOOL_USE,\n isError: true,\n }),\n skippedResult: index => ({\n id: toolCalls[index].id,\n content: TOOL_USE_SKIPPED_MESSAGE,\n isError: true,\n }),\n }\n\n const timeoutMs = typeof ctx.toolBatchTimeoutMs === 'number' && Number.isFinite(ctx.toolBatchTimeoutMs) && ctx.toolBatchTimeoutMs > 0\n ? ctx.toolBatchTimeoutMs\n : 0\n\n try {\n if (timeoutMs <= 0)\n return await executor(executionCtx)\n return await withTimeout(executor(executionCtx), {\n operation: 'custom tool batch executor',\n timeoutMs,\n onTimeout: () => siblingAbort.abort(new OperationTimeoutError('custom tool batch executor', timeoutMs)),\n message: `Custom tool batch executor timed out after ${timeoutMs}ms.`,\n })\n }\n catch (err) {\n if (!(err instanceof OperationTimeoutError))\n throw err\n return toolCalls.map(call => ({\n id: call.id,\n content: `Error: ${err.message}`,\n isError: true,\n }))\n }\n finally {\n if (parentAbortListener)\n ctx.signal.removeEventListener('abort', parentAbortListener)\n }\n}\n\nasync function startDurableToolExecution(\n ctx: LoopContext,\n call: ToolCall,\n turnId: string,\n index: number,\n): Promise<{ kind: 'result', result: ToolResult } | { kind: 'start', start: DurableToolStart }> {\n const toolDef = ctx.tools[call.name]\n const durableExecute = toolDef?.durableExecute\n const callId = call.id\n const displayName = toWireName(call.name, ctx.aliasMaps)\n const runToolCounts: Readonly<Record<string, number>> = Object.freeze({ ...ctx.runToolCounts })\n const perCallAbort = new AbortController()\n ctx.toolCancels?.register(callId, perCallAbort)\n\n const cleanupResult = (result: ToolResult): { kind: 'result', result: ToolResult } => {\n ctx.toolCancels?.unregister(callId)\n return { kind: 'result', result }\n }\n\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 if (gateCtx.block) {\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 cleanupResult({ id: callId, content: `Blocked: ${gateCtx.reason}`, isError: true })\n }\n\n ctx.runToolCounts[call.name] = (ctx.runToolCounts[call.name] ?? 0) + 1\n\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 cleanupResult({\n id: callId,\n content: emitted.output,\n ...(emitted.isError ? { isError: true } : {}),\n })\n }\n\n let effectiveInput = gateCtx.input\n\n if (!toolDef || !durableExecute) {\n // Same hook sequence as the native unknown-tool path — without it,\n // a durable unknown tool produced a tool_result with NO\n // tool:before/tool:unknown/tool:error/tool:dispatched, violating the\n // \"every call closes with exactly one tool:dispatched\" invariant.\n // A registered-but-not-durable tool is the same category from the\n // dispatcher's perspective: the call cannot be routed to a body.\n return cleanupResult(await handleUndispatchableTool(ctx, {\n turnId,\n callId,\n name: call.name,\n displayName,\n input: effectiveInput,\n runToolCounts,\n reason: toolDef\n ? `Tool is not durable-execution capable: ${call.name}`\n : `Unknown tool: ${call.name}`,\n }))\n }\n\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 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 cleanupResult({ id: callId, content: `Validation error: ${validation.error}`, isError: true })\n }\n\n effectiveInput = validation.coercedInput ?? effectiveInput\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 // Manual signal composition (not `AbortSignal.any`) — see\n // {@link composePerCallSignal}. Disposed in the durable batch's `finally`\n // once the call settles, so it never accumulates on the long-lived run signal.\n const composedSignal = composePerCallSignal(ctx.signal, perCallAbort.signal)\n const childSignal = composedSignal.signal\n let reportedOutcome: ToolOutcome | undefined\n const toolCtx: ToolContext = {\n provider: ctx.provider,\n model: ctx.model,\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 reportOutcome: (outcome: ToolOutcome) => {\n reportedOutcome = outcome\n },\n }\n\n return {\n kind: 'start',\n start: {\n index,\n call,\n turnId,\n callId,\n name: call.name,\n displayName,\n input: effectiveInput,\n runToolCounts,\n perCallAbort,\n disposeSignal: composedSignal.dispose,\n ...(coercions ? { coercions } : {}),\n promise: durableExecute(effectiveInput, toolCtx),\n reportedOutcome: () => reportedOutcome,\n },\n }\n}\n\nasync function completeDurableToolExecution(\n ctx: LoopContext,\n start: DurableToolStart,\n output: DurableToolOutput,\n): Promise<ToolResult> {\n const reportedOutcome = start.reportedOutcome()\n const emitted = await emitToolResult(ctx, {\n turnId: start.turnId,\n callId: start.callId,\n name: start.name,\n displayName: start.displayName,\n input: start.input,\n output,\n isError: false,\n runToolCounts: start.runToolCounts,\n ...(start.coercions ? { coercions: start.coercions } : {}),\n ...(reportedOutcome ? { outcome: reportedOutcome } : {}),\n })\n\n return {\n id: start.callId,\n content: emitted.output,\n ...(emitted.isError ? { isError: true } : {}),\n }\n}\n\nasync function failDurableToolExecution(\n ctx: LoopContext,\n start: DurableToolStart,\n err: unknown,\n cancelMessage: () => string,\n): Promise<ToolResult> {\n if (ctx.shouldRethrowToolError?.(err))\n throw err\n const isAbortError = err instanceof Error && err.name === 'AbortError'\n if (isAbortError && start.perCallAbort.signal.aborted) {\n const reason = typeof start.perCallAbort.signal.reason === 'string'\n ? start.perCallAbort.signal.reason\n : 'cancelled-by-user'\n await ctx.hooks.callHook('tool:cancelled', {\n ...buildToolHookBase(ctx, start.turnId, start.callId, start.name, start.displayName, start.input),\n reason,\n runToolCounts: start.runToolCounts,\n })\n return { id: start.callId, content: TOOL_USE_CANCELLED_MESSAGE, isError: true }\n }\n // Mirror executeSingleTool's classification: only an AbortError-shaped\n // failure observed while the batch/run signal is aborted is a\n // cancellation. A real tool failure must keep its own error text even\n // when the signal is already aborted — e.g. the shell whose thrown\n // failure TRIPPED the cascade settles with `siblingAbort` already\n // flipped (the mapper-side cascade in `executeDurable` fires first),\n // and rewriting its result to the cascade message would hide the root\n // cause from the model.\n if (isAbortError && ctx.signal.aborted) {\n return {\n id: start.callId,\n content: cancelMessage(),\n isError: true,\n }\n }\n const error = err instanceof Error ? err : new Error(String(err))\n const errorCtx: ToolHookContext & {\n error: Error\n result?: string | ToolResultContent[]\n } = {\n ...buildToolHookBase(ctx, start.turnId, start.callId, start.name, start.displayName, start.input),\n error,\n }\n await ctx.hooks.callHook('tool:error', errorCtx)\n const emitted = await emitToolResult(ctx, {\n turnId: start.turnId,\n callId: start.callId,\n name: start.name,\n displayName: start.displayName,\n input: start.input,\n output: errorCtx.result ?? `Tool error: ${error.message}`,\n isError: true,\n runToolCounts: start.runToolCounts,\n ...(start.coercions ? { coercions: start.coercions } : {}),\n })\n return {\n id: start.callId,\n content: emitted.output,\n ...(emitted.isError ? { isError: true } : {}),\n }\n}\n\nfunction serializeToolFailure(failure: unknown): SerializedToolFailure {\n if (failure instanceof Error) {\n const codeValue = Reflect.get(failure, 'code')\n const code = typeof codeValue === 'number'\n ? codeValue\n : undefined\n return {\n name: failure.name,\n message: failure.message,\n ...(code !== undefined ? { code } : {}),\n constructorName: failure.constructor?.name,\n }\n }\n return { name: 'Error', message: String(failure) }\n}\n\nfunction hasDurableMap<T>(promise: Promise<T>): promise is MappedDurablePromise<T> {\n return typeof Reflect.get(promise, 'map') === 'function'\n}\n\nfunction errorFromSerializedFailure(failure: SerializedToolFailure): Error {\n const error = new Error(failure.message)\n error.name = failure.name\n if (failure.code !== undefined)\n Object.defineProperty(error, 'code', { value: failure.code, enumerable: true })\n if (failure.constructorName && failure.constructorName !== failure.name)\n Object.defineProperty(error, 'constructorName', { value: failure.constructorName, enumerable: true })\n return error\n}\n\nasync function executeNativeToolBatch(\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 if (ctx.shouldRethrowToolError?.(err))\n throw err\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 /**\n * Free a SINGLE slot: wait for the first in-flight call to settle rather\n * than the whole fleet. Used when an all-safe fleet hits `maxConcurrent` —\n * a sliding window dispatches the next safe call the moment ANY sibling\n * finishes, instead of stalling the entire batch on the slowest one (which\n * a full `drain()` would do). `dispatch()` never rejects (it captures both\n * outcomes into `results[]`), so `Promise.race` here can't throw. Each\n * dispatched promise carries a `.finally` that deletes its own slot from\n * `inFlight` BEFORE the promise resolves, so on return `inFlight.size` has\n * dropped by at least one and a dispatch is guaranteed to fit under the cap.\n */\n const waitForSlot = async (): Promise<void> => {\n if (inFlight.size > 0)\n await Promise.race([...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 // flushes everything in-flight before dispatching. After the drain,\n // the fleet is empty and the unsafe call runs alone; subsequent safe\n // calls re-form a new fleet behind it.\n if (!safe[i] || !fleetAllSafe())\n await drain()\n // All-safe fleet at the cap: free just one slot (sliding window) so\n // the next safe call dispatches as soon as ANY sibling finishes,\n // rather than barriering on the slowest. Without this, N safe calls\n // with N > maxConcurrent run as fixed batches and each batch waits on\n // its slowest member — turning a parallel fan-out into staircased\n // latency. The barrier branch above already handled the unsafe cases.\n else if (inFlight.size >= maxConcurrent)\n await waitForSlot()\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 * `repeat-guard` middleware — consecutive-identical-call streak detector with\n * block-then-abort escalation, on top of the `tool:gate` writable-`block`\n * slot.\n *\n * Distinct from the two adjacent loop-breakers:\n *\n * - {@link installDedupToolsGate} keys on an argument hash and replays (or\n * blocks after N) per the consumer's hasher. It's per-tool opt-in and its\n * default mode replays — it never aborts the run.\n * - {@link installToolBudgetsGate} caps the *run-cumulative* call count for a\n * tool regardless of payload.\n *\n * The repeat guard instead watches a **run-local chain of back-to-back\n * identical normalized payloads** and escalates:\n *\n * - At `blockThreshold` (default 4; `read_file` default 6): either refuse\n * via `ctx.block` with a pivot suggestion (`thresholdAction: 'block'`,\n * default) or let the tool run and append that steering message to the result\n * (`thresholdAction: 'steer'`). Fires `repeat-guard:exceeded`\n * (`action: 'block' | 'steer'`).\n * - At `abortThreshold` (default 8; `read_file` default 12): block\n * the offending call AND call the injected `abort()` (bound to the agent's\n * `AbortController`). The loop's existing post-turn `signal.aborted` check\n * then fires `agent:abort` and terminates the run — the same path a\n * user-driven cancel takes. Fires `repeat-guard:exceeded`\n * (`action: 'abort'`) first so consumers can distinguish a guard abort from\n * a user abort. This is the only layer that stops a model which ignores the\n * `Blocked:` reason and keeps retrying the same payload.\n *\n * \"Consecutive\" means the next tool call in the run is the same tracked\n * tool with the same normalized payload. Any intervening tool call, tracked\n * or not, breaks the chain.\n *\n * `mode` selects the detector:\n * - `'chain'` (default) counts only an uninterrupted chain.\n * - `'window'` counts repeated payloads in a sliding window.\n * - `'both'` runs both and trips when either reaches the threshold.\n *\n * With `countBlockedCalls: true`, calls already **blocked by a prior\n * gate** (budget cap, skill refusal) also count toward the streak — a\n * model hammering a budget-blocked tool with identical args is the\n * canonical stuck loop, and skipping those calls makes the abort\n * escalation unreachable exactly when it's needed. The guard never\n * overwrites the prior gate's reason; it only escalates to `abort()` at\n * the abort threshold. Default off (historic behavior). Calls answered by\n * a prior gate's `result` substitute (dedup replay) are NEVER counted —\n * the model received a real payload, so it isn't loop evidence.\n *\n * State is run-local (closure-scoped, keyed by `runId`) and dies\n * with the run when `uninstall()` runs from the agent's `finally`. No session\n * is required — the in-memory streak lives entirely in the closure.\n */\n\nimport type { Hookable } from 'hookable'\nimport type { AgentHooks } from './agent'\nimport type {\n RepeatGuardConfig,\n RepeatGuardToolMatcher,\n ToolHookContext,\n ToolResultContent,\n} from './types'\n\nconst DEFAULT_BLOCK_THRESHOLD = 4\nconst DEFAULT_ABORT_THRESHOLD = 8\n\n/**\n * `read_file` is useful enough during normal scaffolding/debugging that the\n * generic 4/8 shell thresholds are too eager.\n */\nconst READ_FILE_TOOL = 'read_file'\nconst DEFAULT_READ_FILE_BLOCK_THRESHOLD = 6\nconst DEFAULT_READ_FILE_ABORT_THRESHOLD = 12\nconst DEFAULT_WINDOW_SIZE = 40\n\n/**\n * Universal repeat ceiling: the last-resort chain breaker. Independent of the\n * tracked-tool set, it catches exact back-to-back `(tool, args)` chains across\n * EVERY tool. Set high enough that legitimate retries don't trip, but a genuine\n * spin does.\n */\nconst DEFAULT_GLOBAL_CEILING = 12\n\n/**\n * Default tracked-tool predicate: the built-in `shell` and `read_file` tools.\n * `shell` is an auto-approved, side-effect-light retry magnet; `read_file` is\n * the re-read loop magnet. Both are the loop shapes the abort escalation exists\n * for. Consumers that want to track additional tools (e.g. an MCP query runner)\n * pass them via\n * {@link RepeatGuardConfig.tools}.\n */\nexport function defaultRepeatGuardTracked(name: string): boolean {\n return name === 'shell' || name === READ_FILE_TOOL\n}\n\nfunction compileMatchers(\n tools: readonly RepeatGuardToolMatcher[] | undefined,\n): (name: string) => boolean {\n if (tools === undefined)\n return defaultRepeatGuardTracked\n if (tools.length === 0)\n return () => false\n const exact = new Set<string>()\n const fns: ((name: string) => boolean)[] = []\n for (const m of tools) {\n if (typeof m === 'string')\n exact.add(m)\n else\n fns.push(m)\n }\n return (name: string) => {\n if (exact.has(name))\n return true\n for (const fn of fns) {\n try {\n if (fn(name))\n return true\n }\n catch {\n // A throwing matcher excludes the tool rather than poisoning the run.\n }\n }\n return false\n }\n}\n\n/**\n * Canonicalize a shell command so semantically-identical invocations collapse\n * to one streak key:\n *\n * - strip a leading `cd <path> &&` (or `cd <path>;`) prefix — the model often\n * re-prefixes the same command with a `cd` into the project root.\n * - strip a trailing verify-tail of the shape `& sleep N ; <anything>` or\n * `; sleep N && <anything>` that the model appends to poll a just-started\n * process (`… & sleep 2 ; curl localhost`).\n * - collapse runs of whitespace to single spaces and trim.\n *\n * Conservative on purpose: it does NOT reorder flags or parse the command —\n * it only removes the two wrappers we actually saw inflate distinct-looking\n * retries of the same core command, plus whitespace noise.\n */\nexport function normalizeShellCommand(command: string): string {\n let s = command.trim()\n\n // Strip a leading `cd <path> && ` / `cd <path> ; ` prefix (repeatable).\n // The path may be quoted or contain no spaces; stop at the first `&&`/`;`.\n for (;;) {\n const m = /^cd\\s+(?:\"[^\"]*\"|'[^']*'|\\S+)\\s*(?:&&|;)\\s*/.exec(s)\n if (!m)\n break\n s = s.slice(m[0].length)\n }\n\n // Strip a trailing verify-tail: a `sleep N` followed by anything, joined by\n // `&`, `&&`, or `;`. Matches `… & sleep 2 ; curl …`, `… ; sleep 1 && wget …`.\n s = s.replace(/\\s*(?:&&|&|;)\\s*sleep\\s+\\d+(?:\\.\\d+)?\\s*(?:&&|&|;)\\s.*$/i, '')\n // Also a bare trailing `& sleep N` with no follow-up command.\n s = s.replace(/\\s*(?:&&|&|;)\\s*sleep\\s+\\d+(?:\\.\\d+)?\\s*$/i, '')\n\n // Collapse whitespace.\n s = s.replace(/\\s+/g, ' ').trim()\n return s\n}\n\n/**\n * Built-in normalizer used when the consumer doesn't supply one. Shell-shaped\n * tools get {@link normalizeShellCommand}; everything else falls back to a\n * stable JSON encoding. Returns `undefined` when there's no meaningful payload\n * to key on (so the call is excluded from streak tracking rather than keyed on\n * `'{}'`). Consumers needing semantic keying for their own tools (e.g.\n * collapsing a query's whitespace) supply a normalizer via\n * {@link RepeatGuardConfig.normalize}.\n */\nexport function defaultRepeatGuardNormalize(\n name: string,\n input: Record<string, unknown>,\n): string | undefined {\n if (name === 'shell') {\n const cmd = input.command\n if (typeof cmd !== 'string' || cmd.trim().length === 0)\n return undefined\n return `shell:${normalizeShellCommand(cmd)}`\n }\n try {\n return `json:${stableStringify(input)}`\n }\n catch {\n return undefined\n }\n}\n\n/** Deterministic JSON with sorted keys so key order doesn't split a streak. */\nexport function stableStringify(value: unknown): string {\n return JSON.stringify(value, (_k, v) => {\n if (v && typeof v === 'object' && !Array.isArray(v)) {\n const sorted: Record<string, unknown> = {}\n for (const k of Object.keys(v as Record<string, unknown>).sort())\n sorted[k] = (v as Record<string, unknown>)[k]\n return sorted\n }\n return v\n })\n}\n\nfunction formatSteeringMessage(\n message: RepeatGuardConfig['steeringMessage'] | RepeatGuardConfig['blockReason'],\n name: string,\n count: number,\n): string {\n if (typeof message === 'string')\n return message\n if (typeof message === 'function') {\n try {\n const out = message(name, count)\n if (typeof out === 'string' && out.length > 0)\n return out\n }\n catch {\n // Fall through to default.\n }\n }\n return (\n `Identical \\`${name}\\` call repeated ${count} times. `\n + `This is not making progress — do NOT retry the same payload. `\n + `Change the command/arguments, inspect why it's failing first, or move on to a different step.`\n )\n}\n\nfunction appendSteeringMessage(\n result: string | ToolResultContent[],\n message: string,\n): string | ToolResultContent[] {\n if (typeof result === 'string')\n return `${result}\\n\\n${message}`\n return [...result, { type: 'text', text: `\\n\\n${message}` }]\n}\n\ninterface StreakEntry {\n key: string\n count: number\n}\n\n/**\n * Install the consecutive-identical repeat guard on a hook bus.\n *\n * `getConfig` returns the resolved `behavior.repeatGuard` (run override merged\n * with agent defaults), called lazily so a config attached after install\n * still takes effect. `abort` is bound to the agent's `AbortController` — the\n * gate calls it at the abort threshold and the loop's existing post-turn\n * `signal.aborted` check terminates the run (mirrors how `toolBudgets`\n * receives `enqueueSteer` rather than reaching into the loop).\n *\n * Returns an `uninstall` fn — the agent calls it in `finally` so handlers and\n * streak state never leak across runs.\n */\nexport function installRepeatGuard(\n hooks: Hookable<AgentHooks>,\n getConfig: () => RepeatGuardConfig | undefined,\n abort: () => void,\n): () => void {\n // Per-run chain state. Run-scoped so a parent and its subagents never share a\n // streak. Closure-local — dies with the run on uninstall.\n const streaks = new Map<string, StreakEntry>()\n // Window-mode state: the tool's last `windowSize` normalized keys, in call\n // order. Only populated when explicit window counting is enabled.\n const windows = new Map<string, string[]>()\n const pendingSteering = new Map<string, string>()\n // Universal-ceiling chain state. Separate from `streaks` so the last-resort\n // ceiling can cover tools outside the tracked set without changing the\n // tracked-tool thresholds.\n const globalStreaks = new Map<string, StreakEntry>()\n\n function streakKey(runId: string | undefined, name: string): string {\n return `${runId ?? '-'}::${name}`\n }\n\n function runKey(runId: string | undefined): string {\n return runId ?? '-'\n }\n\n function resolve(config: RepeatGuardConfig): {\n isTracked: (name: string) => boolean\n normalize: (name: string, input: Record<string, unknown>) => string | undefined\n blockThreshold: number\n abortThreshold: number\n hasCustomBlockThreshold: boolean\n hasCustomAbortThreshold: boolean\n countBlockedCalls: boolean\n mode: 'chain' | 'window' | 'both'\n thresholdAction: 'block' | 'steer'\n windowSize: number | undefined\n globalCeiling: number\n isGloballyExcluded: (name: string) => boolean\n } {\n const hasCustomBlockThreshold = typeof config.blockThreshold === 'number' && Number.isFinite(config.blockThreshold)\n const rawBlockThreshold = hasCustomBlockThreshold ? config.blockThreshold as number : DEFAULT_BLOCK_THRESHOLD\n const blockThreshold = Math.max(\n 2,\n Math.floor(rawBlockThreshold),\n )\n // Abort must sit strictly above block to be reachable. A non-positive /\n // non-finite value disables the abort escalation (block-only).\n const hasCustomAbortThreshold = typeof config.abortThreshold === 'number' && Number.isFinite(config.abortThreshold)\n const rawAbort\n = hasCustomAbortThreshold\n ? config.abortThreshold as number\n : DEFAULT_ABORT_THRESHOLD\n const abortThreshold = rawAbort > 0 ? Math.max(blockThreshold + 1, Math.floor(rawAbort)) : Infinity\n const hasExplicitWindowSize = typeof config.windowSize === 'number' && Number.isFinite(config.windowSize)\n const mode = config.mode === 'window' || config.mode === 'both' || config.mode === 'chain'\n ? config.mode\n // Back-compat: historical configs enabled window mode by setting windowSize.\n : (hasExplicitWindowSize ? 'window' : 'chain')\n const thresholdAction = config.thresholdAction === 'steer' ? 'steer' : 'block'\n // Window mode: clamp the window up to the reachable thresholds — a window\n // smaller than the abort threshold could never accumulate enough\n // occurrences to trip it. If mode asks for a window without an explicit\n // size, use a conservative default.\n let windowSize: number | undefined\n const rawWindowSize = hasExplicitWindowSize\n ? Math.max(2, Math.floor(config.windowSize as number))\n : (mode === 'window' || mode === 'both' ? DEFAULT_WINDOW_SIZE : undefined)\n if (typeof rawWindowSize === 'number' && rawWindowSize >= 2) {\n const reachable = Number.isFinite(abortThreshold) ? abortThreshold : blockThreshold\n windowSize = Math.max(rawWindowSize, reachable)\n }\n // Universal ceiling: default ON (DEFAULT_GLOBAL_CEILING). A non-positive /\n // non-finite value disables it. It also folds with the abort opt-out — the\n // ceiling aborts, so when abort is disabled (block-only, abortThreshold<=0)\n // it disables too rather than silently overriding the consumer's choice.\n const rawGlobalCeiling\n = typeof config.globalCeiling === 'number' && Number.isFinite(config.globalCeiling)\n ? Math.floor(config.globalCeiling)\n : DEFAULT_GLOBAL_CEILING\n // Never below `abortThreshold`: the per-tool tracked abort should fire at\n // the consumer's configured threshold rather than be pre-empted by the\n // universal ceiling (which would otherwise undercut a raised abortThreshold\n // for tracked tools).\n const globalCeiling = rawGlobalCeiling > 0 && Number.isFinite(abortThreshold)\n ? Math.max(2, rawGlobalCeiling, abortThreshold)\n : 0\n // `globalCeilingExclude` undefined → exclude nothing (NOT the default\n // tracked set that `compileMatchers(undefined)` would give).\n const isGloballyExcluded = config.globalCeilingExclude === undefined\n ? () => false\n : compileMatchers(config.globalCeilingExclude)\n return {\n isTracked: compileMatchers(config.tools),\n normalize: config.normalize ?? defaultRepeatGuardNormalize,\n blockThreshold,\n abortThreshold,\n hasCustomBlockThreshold,\n hasCustomAbortThreshold,\n countBlockedCalls: config.countBlockedCalls === true,\n mode,\n thresholdAction,\n windowSize,\n globalCeiling,\n isGloballyExcluded,\n }\n }\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 const config = getConfig()\n if (!config)\n return\n\n const resolved = resolve(config)\n const { isTracked, normalize, blockThreshold, abortThreshold, countBlockedCalls, mode, thresholdAction, windowSize } = resolved\n const runSlot = runKey(ctx.runId)\n\n // A prior gate's `result` substitute answers the call without running\n // the tool (dedup replay, consumer cache). The model gets a real\n // payload back, so it isn't loop evidence, but it is still a tool call\n // boundary and therefore breaks any active chain.\n if (ctx.result !== undefined) {\n streaks.delete(runSlot)\n globalStreaks.delete(runSlot)\n return\n }\n\n // --- Universal ceiling: last-resort chain breaker for EVERY tool --------\n // Runs before the tracked-tool gate so it also covers untracked tools.\n // Keyed on exact (tool, args), but strictly consecutive: any intervening\n // call with a different exact key resets the chain. Counts blocked attempts\n // too (a model re-emitting a blocked call IS the loop), but never a\n // `result` substitute (handled above — that's a real answer, not a loop).\n if (resolved.globalCeiling > 0 && !resolved.isGloballyExcluded(ctx.name)) {\n let gkey: string\n try {\n gkey = `${ctx.name}\\u0000${stableStringify(ctx.input)}`\n }\n catch {\n gkey = ''\n }\n if (gkey.length > 0) {\n const prior = globalStreaks.get(runSlot)\n const gcount = prior && prior.key === gkey ? prior.count + 1 : 1\n globalStreaks.set(runSlot, { key: gkey, count: gcount })\n if (gcount >= resolved.globalCeiling) {\n if (!ctx.block) {\n ctx.block = true\n ctx.reason = formatSteeringMessage(config.steeringMessage ?? config.blockReason, ctx.name, gcount)\n }\n await hooks.callHook('repeat-guard:exceeded', {\n tool: ctx.name,\n count: gcount,\n threshold: resolved.globalCeiling,\n turnId: ctx.turnId,\n action: 'abort',\n })\n abort()\n return\n }\n }\n else {\n globalStreaks.delete(runSlot)\n }\n }\n else {\n globalStreaks.delete(runSlot)\n }\n\n if (!isTracked(ctx.name)) {\n streaks.delete(runSlot)\n return\n }\n\n // A prior gate's `block` (budget cap, skill allowed-tools refusal):\n // with `countBlockedCalls` on, the attempt still counts — the model\n // emitted yet another identical payload AND got refused, which is\n // exactly the stuck-loop evidence the abort escalation exists for. We\n // never overwrite the prior gate's `reason`, and only the abort\n // escalation acts on such calls. Default off (historic behavior:\n // blocked calls don't touch the streak).\n if (ctx.block && !countBlockedCalls) {\n streaks.delete(runSlot)\n return\n }\n\n let key: string | undefined\n try {\n key = normalize(ctx.name, ctx.input)\n }\n catch {\n // A throwing normalizer disables guarding for this call only.\n streaks.delete(runSlot)\n return\n }\n if (typeof key !== 'string' || key.length === 0) {\n streaks.delete(runSlot)\n return\n }\n\n const blockedByPriorGate = ctx.block\n\n const isReadFile = ctx.name === READ_FILE_TOOL\n const effectiveBlockThreshold = isReadFile && !resolved.hasCustomBlockThreshold\n ? DEFAULT_READ_FILE_BLOCK_THRESHOLD\n : blockThreshold\n const effectiveAbortThreshold = Number.isFinite(abortThreshold)\n ? Math.max(effectiveBlockThreshold + 1, isReadFile && !resolved.hasCustomAbortThreshold\n ? DEFAULT_READ_FILE_ABORT_THRESHOLD\n : abortThreshold)\n : Infinity\n const useChain = mode === 'chain' || mode === 'both'\n const useWindow = (mode === 'window' || mode === 'both') && windowSize !== undefined\n\n let chainCount = 0\n if (useChain) {\n const chainKey = `${ctx.name}\\u0000${key}`\n const prior = streaks.get(runSlot)\n chainCount = prior && prior.key === chainKey ? prior.count + 1 : 1\n streaks.set(runSlot, { key: chainKey, count: chainCount })\n }\n else {\n streaks.delete(runSlot)\n }\n\n let windowCount = 0\n if (useWindow && windowSize !== undefined) {\n // Sliding window: count occurrences of this key among the tool's\n // last `effectiveWindow` calls. Interleaved different payloads dilute\n // but never reset. This is explicit opt-in behavior; defaults use\n // strict chain counting.\n const slot = streakKey(ctx.runId, ctx.name)\n const recent = windows.get(slot) ?? []\n recent.push(key)\n if (recent.length > windowSize)\n recent.shift()\n windows.set(slot, recent)\n for (const k of recent) {\n if (k === key)\n windowCount++\n }\n }\n\n const count = Math.max(chainCount, windowCount)\n if (count < effectiveBlockThreshold)\n return\n\n // At/over the abort threshold: block this call AND trigger the run abort.\n // We fire `repeat-guard:exceeded` (so consumers can distinguish a guard\n // abort from a user cancel) BEFORE calling `abort()`, then the loop's\n // post-turn `signal.aborted` check fires `agent:abort` and breaks. Still\n // block the offending call so it doesn't execute on the way out (a\n // prior gate's block + reason are left untouched — the call is already\n // refused).\n if (count >= effectiveAbortThreshold) {\n if (!blockedByPriorGate) {\n ctx.block = true\n ctx.reason = formatSteeringMessage(config.steeringMessage ?? config.blockReason, ctx.name, count)\n }\n await hooks.callHook('repeat-guard:exceeded', {\n tool: ctx.name,\n count,\n threshold: effectiveAbortThreshold,\n turnId: ctx.turnId,\n action: 'abort',\n })\n abort()\n return\n }\n\n // Block threshold with a prior gate already refusing: nothing to add — the\n // model already sees a `Blocked:` result, and there is no tool output to\n // append steering to. The streak increment above keeps marching toward the\n // abort threshold.\n if (blockedByPriorGate)\n return\n\n const message = formatSteeringMessage(config.steeringMessage ?? config.blockReason, ctx.name, count)\n if (thresholdAction === 'steer') {\n pendingSteering.set(ctx.callId, message)\n await hooks.callHook('repeat-guard:exceeded', {\n tool: ctx.name,\n count,\n threshold: effectiveBlockThreshold,\n turnId: ctx.turnId,\n action: 'steer',\n })\n return\n }\n\n // Block threshold: refuse with a pivot suggestion. Sticky — every further\n // identical call keeps blocking (and keeps incrementing toward abort).\n ctx.block = true\n ctx.reason = message\n await hooks.callHook('repeat-guard:exceeded', {\n tool: ctx.name,\n count,\n threshold: effectiveBlockThreshold,\n turnId: ctx.turnId,\n action: 'block',\n })\n }\n\n const unregister = hooks.hook('tool:gate', gateHandler)\n const unregisterTransform = hooks.hook('tool:transform', (ctx) => {\n const message = pendingSteering.get(ctx.callId)\n if (message === undefined)\n return\n pendingSteering.delete(ctx.callId)\n ctx.result = appendSteeringMessage(ctx.result, message)\n })\n const unregisterError = hooks.hook('tool:error', ctx => pendingSteering.delete(ctx.callId))\n\n return function uninstall() {\n unregister()\n unregisterTransform()\n unregisterError()\n streaks.clear()\n windows.clear()\n globalStreaks.clear()\n pendingSteering.clear()\n }\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. Rides its own queue (the loop's `toolBudgetNudgeQueue`),\n * NOT the user `steeringQueue` — the batch schedulers skip remaining\n * sibling tool calls when the steering queue is non-empty, and a budget\n * nudge must never kill the over-budget call's siblings.\n *\n * `hardMax` adds a hard ceiling to steer mode: steer-mode approvals keep\n * counting past `max`, and once the count reaches `hardMax` the gate\n * force-blocks — the escape hatch for models that ignore the nudge and\n * keep calling the tool until an external cap kills the run.\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 `toolBudgetNudgeQueue` so the loop drains it at the next turn\n * boundary (without superseding in-flight sibling tool calls).\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 // Hard ceiling for steer mode: steer is advisory and models can ignore\n // the nudge indefinitely (the call keeps executing). Past `hardMax`,\n // force-block regardless of how the mode resolved above. Counts keep\n // semantics: `count` is approved (executed) calls so far, so steer-mode\n // approvals march it from `max` toward `hardMax`.\n const hardMax = budget.hardMax\n if (\n mode === 'steer'\n && typeof hardMax === 'number' && Number.isFinite(hardMax) && hardMax > 0\n && count >= hardMax\n ) {\n mode = 'block'\n message = defaultHardBlockMessage(ctx.name, count, hardMax)\n }\n else if (mode === 'steer') {\n // Steer lets the call run — it still counts as an approval so the\n // hard ceiling (and `runToolCounts`) keep advancing.\n approvedCounts[ctx.name] = count + 1\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\nfunction defaultHardBlockMessage(tool: string, count: number, hardMax: number): string {\n return `Tool '${tool}' has been called ${count} times this run, past its hard ceiling of ${hardMax} — `\n + `the earlier budget warning was not heeded. Further invocations are refused; `\n + `finish the task with the information you already have.`\n}\n","/**\n * Per-call tool-cancellation registry shared between the agent and the loop.\n *\n * The loop registers an `AbortController` for each tool call right before it\n * dispatches (gate → execute), and unregisters it in a `finally`, so the live\n * map only ever holds *currently dispatching* calls. `agent.cancelTool(callId)`\n * flips the matching call's controller without unwinding the rest of the run.\n *\n * Two subtleties this registry owns so callers don't have to:\n *\n * - **Pre-registration race.** A host can learn a call's id from the streamed\n * assistant turn and ask to cancel it BEFORE the loop has dispatched it\n * (very reachable under the sequential `maxConcurrentTools: 1` default —\n * later calls in a batch register only when their turn comes). A cancel for\n * an un-registered id is parked in a bounded FIFO and applied the instant\n * the call registers, instead of being silently dropped.\n *\n * - **Bounded parking.** Parked kills for ids that never dispatch (a\n * hallucinated id, a call the model retracted) can't accumulate: the FIFO\n * evicts oldest past its cap, and the agent clears it at run boundaries so a\n * stale kill never leaks across runs onto a later call that happens to reuse\n * the same id.\n */\n\n/** Fallback reason stamped on the abort signal when `cancel` gets no explicit reason. */\nconst DEFAULT_KILL_MESSAGE = 'user-cancelled-tool'\n/** Upper bound on parked pre-registration kills — FIFO-evicted past this. */\nconst DEFAULT_MAX_PENDING_KILLS = 64\n\nexport interface ToolCancelRegistry {\n /**\n * Bind `controller` to `callId` (loop, at dispatch). If a kill for `callId`\n * was parked before it registered, it is applied immediately.\n */\n register: (callId: string, controller: AbortController) => void\n /** Drop the live binding for `callId` (loop, in `finally`). */\n unregister: (callId: string) => void\n /**\n * Cancel the in-flight call `callId`. Returns `true` when a live, not-yet-\n * aborted controller was flipped; `false` otherwise — including when the\n * cancel is parked for the pre-registration race (the call hasn't dispatched\n * yet). Idempotent: a second cancel of an already-aborted call returns\n * `false`.\n */\n cancel: (callId: string, reason?: string) => boolean\n /** Abort every live controller (agent teardown) and drop all state. */\n abortAll: (reason: string) => void\n /** Drop parked pre-registration kills (agent, at run boundaries). */\n clearPending: () => void\n}\n\nexport interface ToolCancelRegistryOptions {\n /** Reason stamped on the abort signal when `cancel` is called without one. */\n defaultMessage?: string\n /** Cap on parked pre-registration kills. Defaults to 64. */\n maxPendingKills?: number\n}\n\nexport function createToolCancelRegistry(options: ToolCancelRegistryOptions = {}): ToolCancelRegistry {\n const defaultMessage = options.defaultMessage ?? DEFAULT_KILL_MESSAGE\n const maxPending = typeof options.maxPendingKills === 'number' && options.maxPendingKills > 0\n ? Math.floor(options.maxPendingKills)\n : DEFAULT_MAX_PENDING_KILLS\n\n // Currently-dispatching calls, keyed by callId.\n const live = new Map<string, AbortController>()\n // Kills that arrived before their call registered: callId → reason. A plain\n // Map preserves insertion order, so the first key is the FIFO eviction\n // target.\n const pending = new Map<string, string>()\n\n function park(callId: string, reason: string): void {\n // Re-set to refresh insertion order (most-recent kill wins the slot).\n pending.delete(callId)\n pending.set(callId, reason)\n while (pending.size > maxPending) {\n const oldest = pending.keys().next().value\n if (oldest === undefined)\n break\n pending.delete(oldest)\n }\n }\n\n function applyParked(callId: string, controller: AbortController): void {\n const reason = pending.get(callId)\n if (reason === undefined)\n return\n pending.delete(callId)\n if (!controller.signal.aborted)\n controller.abort(reason)\n }\n\n return {\n register(callId, controller) {\n live.set(callId, controller)\n applyParked(callId, controller)\n },\n unregister(callId) {\n live.delete(callId)\n },\n cancel(callId, reason) {\n const message = reason ?? defaultMessage\n const controller = live.get(callId)\n if (controller) {\n if (controller.signal.aborted)\n return false\n // `reason` rides the abort signal so a signal-aware tool body that\n // surfaces `signal.reason` sees the same string the `tool:cancelled`\n // hook receives.\n controller.abort(message)\n return true\n }\n park(callId, message)\n return false\n },\n abortAll(reason) {\n for (const controller of live.values()) {\n if (!controller.signal.aborted)\n controller.abort(reason)\n }\n live.clear()\n pending.clear()\n },\n clearPending() {\n pending.clear()\n },\n }\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","/**\n * Tail-priority, byte-budgeted truncation shared across tools.\n *\n * \"Tail-priority\" because the most useful signal in command / job output\n * (errors, exit summaries, the last thing that happened) lives at the END.\n * When `text` exceeds the byte budget the HEAD is dropped and replaced with a\n * marker, keeping as much of the tail as fits.\n *\n * The budget is a UTF-8 *byte* count, not a character count — wire/output\n * accounting is bytes, and a naive `String.prototype.slice` (UTF-16 code\n * units) under-counts multibyte text badly. The cut always lands on a whole\n * code point: the walk steps by code point (surrogate pairs included), so an\n * emoji or CJK glyph is never split into a lone surrogate. As a final safety\n * net against callers that hand us text already sliced mid-codepoint at a raw\n * byte boundary (the classic `Buffer.subarray(...).toString()` pattern, which\n * decodes a partial lead/continuation byte to U+FFFD), a leading run of\n * replacement characters is stripped from the kept tail.\n */\n\nimport { Buffer } from 'node:buffer'\n\nexport interface TailTruncateOptions {\n /**\n * Build the marker inserted before the kept tail, given the number of\n * UTF-8 bytes dropped from the head. Defaults to\n * `…(<n> bytes truncated from head)…\\n`. Return an empty string to keep\n * the tail with no marker at all.\n */\n marker?: (droppedBytes: number) => string\n}\n\nfunction defaultMarker(droppedBytes: number): string {\n return `…(${droppedBytes} bytes truncated from head)…\\n`\n}\n\n/**\n * Keep the last `maxBytes` UTF-8 bytes of `text`, dropping the head and\n * prefixing a marker. A non-positive `maxBytes` disables truncation and\n * returns `text` unchanged.\n *\n * `maxBytes` budgets the kept tail only; the marker is added on top and may\n * push the returned string slightly past `maxBytes`. That tradeoff is\n * intentional — folding the marker into the budget would shrink the content\n * actually shown.\n */\nexport function tailTruncate(text: string, maxBytes: number, options: TailTruncateOptions = {}): string {\n if (!Number.isFinite(maxBytes) || maxBytes <= 0)\n return text\n\n const totalBytes = Buffer.byteLength(text)\n if (totalBytes <= maxBytes)\n return text\n\n // Walk backwards a whole code point at a time so the cut never splits a\n // surrogate pair, summing UTF-8 byte cost until the next code point would\n // overflow the budget.\n let bytes = 0\n let cut = text.length\n while (cut > 0) {\n let start = cut - 1\n const code = text.charCodeAt(start)\n // Low surrogate at the boundary → step back to include its high\n // surrogate so the pair stays intact.\n if (code >= 0xDC00 && code <= 0xDFFF && start > 0) {\n const high = text.charCodeAt(start - 1)\n if (high >= 0xD800 && high <= 0xDBFF)\n start -= 1\n }\n const codePointBytes = Buffer.byteLength(text.slice(start, cut))\n if (bytes + codePointBytes > maxBytes)\n break\n bytes += codePointBytes\n cut = start\n }\n\n // Drop any leading replacement characters left by an upstream raw-byte\n // slice that landed mid-codepoint, so the tail starts on clean text.\n const tail = text.slice(cut).replace(/^\\uFFFD+/, '')\n const droppedBytes = totalBytes - Buffer.byteLength(tail)\n const marker = options.marker ?? defaultMarker\n return `${marker(droppedBytes)}${tail}`\n}\n","import type { TaskStallInfo } from '../contexts'\nimport type { ToolContext, ToolDef } from './types'\nimport { previewLine } from '../chat/format'\nimport { interpretShellResult } from './shell-semantics'\nimport { tailTruncate } from './truncate'\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 'printenv',\n 'which',\n 'type',\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 // `env` and `command` are also NOT here: they are wrappers that execute\n // their trailing argument (`env rm -rf x`, `command rm foo`), so listing\n // them would launder mutating commands through the read-only fleet. They\n // are handled as wrappers in {@link isReadOnlyShellCommand} instead.\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 * - Unwraps the `env` / `command` wrappers and classifies the wrapped\n * verb instead (`env rm -rf x` is NOT read-only; bare `env` is).\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 while (i < tokens.length) {\n // Skip leading VAR=value env assignments (e.g. `FOO=bar ls`,\n // `env 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\n // `env` / `command` execute their trailing argument — classify by the\n // wrapped verb, not the wrapper (`env rm -rf x` mutates). Skip the\n // wrapper plus its flags (`env -i`, `command -p`) and loop on what's\n // left. A bare `env` with no trailing command just prints the\n // environment — read-only. Anything else (`command -v rm` etc.) falls\n // through to the conservative verb check.\n if (base === 'env' || base === 'command') {\n i++\n while (i < tokens.length && tokens[i].startsWith('-'))\n i++\n if (i >= tokens.length)\n return base === 'env'\n continue\n }\n\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 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 'Pass a short `description` (5-10 words) explaining the intent of the command — it surfaces in the user-facing transcript so the human can follow your reasoning at a glance. Skip it only for trivial reads where the command speaks for itself.',\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 })`. If — and only if — your very next step depends on the task\\'s outcome, block on it with `wait_task({ task_id })` instead of polling.',\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 description: { type: 'string', description: 'Short (5-10 words) human-readable intent of the command, e.g. \"list TypeScript sources in src\" or \"install project dependencies\". Surfaced in the transcript next to the command so the user can follow the agent\\'s reasoning.' },\n timeout: { type: 'integer', description: `Per-call timeout in milliseconds. Enforced at whole-second granularity: values are rounded UP to the next second (e.g. 500 → 1s).${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 // Long-timeout steering — see `behavior.shellLongTimeoutMs`. A\n // blocking call that pins the turn to a multi-minute transport\n // timeout is almost always a background task the model failed to\n // recognize as one. Refuse with a re-issue instruction, but ONLY\n // when background mode is actually available — otherwise the\n // blocking call is the model's only option and refusing it strands\n // the task.\n const steerThresholdMs = ctx.behavior?.shellLongTimeoutMs\n if (\n typeof steerThresholdMs === 'number' && Number.isFinite(steerThresholdMs) && steerThresholdMs > 0\n && typeof timeout === 'number' && Number.isFinite(timeout) && timeout > steerThresholdMs\n && allowBackground\n && typeof ctx.behavior?.tasksDir === 'string' && ctx.behavior.tasksDir.length > 0\n && ctx.execution.execBackground\n ) {\n return `shell error: this blocking call requested a ${timeout}ms timeout, above the agent's ${steerThresholdMs}ms foreground ceiling. Re-issue with \\`run_in_background: true\\` (drop \\`timeout\\`): you'll get a task_id + output log immediately, and a <task-notification> when it finishes. If your very next step depends on the result, follow up with \\`wait_task({ task_id })\\` instead of polling.`\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 = tailTruncate(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${tailTruncate(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 = tailTruncate(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${tailTruncate(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 outputCap = ctx.behavior?.backgroundOutputCap\n const stallWatchdogMs = ctx.behavior?.backgroundStallWatchdogMs\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 ...(typeof outputCap === 'number' && outputCap > 0 ? { maxOutputBytes: outputCap } : {}),\n ...(typeof stallWatchdogMs === 'number' && stallWatchdogMs > 0\n ? {\n stallTimeoutMs: stallWatchdogMs,\n onStall: (info: TaskStallInfo) => {\n // Same fire-and-forget contract as onExit above.\n Promise.resolve(ctx.hooks.callHook('background:stall', info))\n .catch((err: unknown) => {\n if (process.env.ZIDANE_DEBUG)\n process.stderr.write(`[zidane/shell] background:stall hook rejected: ${err instanceof Error ? err.message : String(err)}\\n`)\n })\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 * 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 { validateResourcePathReal } 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 = await validateResourcePathReal(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 * `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 { validateResourcePathReal } 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 = await validateResourcePathReal(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 { ShellInterpolationApproval, SkillConfig } from '../skills/types'\nimport type { ToolContext, ToolDef } from './types'\nimport { interpolateShellCommands, stripShellInterpolations } 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 * Execute `!\\`cmd\\`` shell interpolation on activation. Mirrors\n * `SkillsConfig.allowShellInterpolation`. Default `true`. Interpolation\n * runs arbitrary shell from (possibly third-party) skill content, outside\n * the allowed-tools gate — set `false` for locked-down hosts. When off,\n * patterns are replaced with a `[shell interpolation disabled]`\n * placeholder.\n */\n allowShellInterpolation?: boolean\n /**\n * Optional approval gate for `!\\`cmd\\`` shell interpolation. Invoked once\n * per (skill, command) the first time a skill with embedded shell is\n * activated, BEFORE the command runs. Return `false` to skip that command\n * (it is replaced with a `[shell interpolation not approved]` placeholder).\n * When omitted, interpolation runs without prompting (back-compat). Hosts\n * (TUI/GUI) can wire this to an interactive confirmation.\n */\n approveShellInterpolation?: (request: ShellInterpolationApproval) => boolean | Promise<boolean>\n}\n\nexport type { ShellInterpolationApproval } from '../skills/types'\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 // Interpolation runs unless `allowShellInterpolation: false`; when\n // disabled the patterns are neutralized with a placeholder. An optional\n // `approveShellInterpolation` gate can veto individual commands.\n let body = interpolatedBodyCache.get(skillName)\n if (body === undefined) {\n if (!skill.instructions.includes('!`')) {\n body = skill.instructions\n }\n else if (options.allowShellInterpolation !== false) {\n const failures: string[] = []\n const approve = options.approveShellInterpolation\n body = await interpolateShellCommands(skill.instructions, ctx.execution, ctx.handle, {\n onFailure: command => failures.push(command),\n ...(approve\n ? { approve: (command: string) => approve({ skillName, source: skill.source, command }) }\n : {}),\n })\n // Surface partial interpolation failure in the activation result —\n // the inline `[command failed …]` markers alone are easy to read\n // past, and the model should know the content may be incomplete.\n if (failures.length > 0) {\n body += `\\n\\nWarning: ${failures.length} shell interpolation command${failures.length === 1 ? '' : 's'} failed during activation (${failures.map(c => `\\`${c}\\``).join(', ')}). The instructions above may be missing dynamic content — see the inline [command failed …] markers.`\n }\n }\n else {\n body = stripShellInterpolations(skill.instructions)\n }\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 `"` 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 * Prefer `addUnlock` for cache-stable wire-tool ordering: writes through a\n * Set lose unlock order, so the wire-level rebuild that filters by `unlocked`\n * has to fall back to registry iteration order — which moves entries every\n * time a lazy tool earlier in the registry is unlocked, breaking provider\n * prompt-cache breakpoints. The agent passes both when it owns the unlock\n * tracker, with `addUnlock` mirroring writes into an ordered log.\n */\n unlocked: Set<string>\n /**\n * Optional callback fired for every canonical name the tool unlocks. When\n * set, the agent uses this to maintain an append-only `dynamicUnlockOrder`\n * so the wire-level tool list emits new unlocks at the tail and keeps the\n * provider prefix cache warm. Idempotent on repeat unlocks of the same\n * name — callers may dedupe internally.\n *\n * Invoked **in addition to** the `unlocked.add` (which still happens for\n * back-compat with callers that only watch the Set).\n */\n addUnlock?: (canonical: string) => void\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 addUnlock?: (canonical: string) => void,\n): void {\n const { shown } = selectToolSearchMatches(catalog, input, defaultLimit ?? DEFAULT_LIMIT)\n for (const entry of shown) {\n unlocked.add(entry.canonicalName)\n addUnlock?.(entry.canonicalName)\n }\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. The\n // `addUnlock` callback (when wired by the agent) also records the\n // canonical into the per-run dynamic-unlock log so the wire-level\n // rebuild can emit fresh unlocks at the tail rather than at their\n // registry position, keeping the provider prefix cache warm.\n for (const entry of shown) {\n options.unlocked.add(entry.canonicalName)\n options.addUnlock?.(entry.canonicalName)\n }\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 * `wait_task` — block until a background task exits (or a timeout elapses).\n *\n * Companion to `shell({ run_in_background: true })` for the cases where\n * the model genuinely needs the result before it can continue (a build\n * that gates a deploy, a migration that gates a test run) — without\n * burning turns polling the output file.\n *\n * The wait itself is delegated to `ExecutionContext.waitBackground`, an\n * injectable seam: the in-process context parks on its own `onExit`\n * machinery; durable hosts implement it as an awakeable park so a\n * crashed worker re-attaches to the same wait on replay.\n *\n * A successful wait ALSO suppresses the would-be `<task-notification>`\n * on the next turn — the agent's `tool:after` listener treats the\n * returned exit info as \"the model knows\". Timeouts do NOT suppress\n * (the task is still running; its eventual exit is still news).\n */\n\nimport type { ToolContext, ToolDef } from './types'\nimport { formatDuration, formatTaskStatus, previewLine } from '../chat/format'\nimport { resolveDetachedTasksCapability } from '../contexts/types'\n\n/**\n * Stable prefix on the timed-out result. The agent's notification\n * suppression keys on it: results carrying this prefix mean the task is\n * still running, so the pending `<task-notification>` must survive.\n */\nexport const WAIT_TASK_TIMED_OUT_PREFIX = 'wait_task: timed out'\n\nconst DEFAULT_TIMEOUT_MS = 120_000\nconst MAX_TIMEOUT_MS = 30 * 60_000\n\nexport const waitTask: ToolDef = {\n // NOT concurrency-safe: a parked wait can occupy a scheduler slot for\n // minutes; barrier semantics keep it from starving a sibling fleet.\n spec: {\n name: 'wait_task',\n description: [\n 'Wait for a background task started by `shell({ run_in_background: true })` to exit, then return its final status.',\n 'Returns immediately when the task has already terminated. On timeout the task keeps running and you can wait again, keep working, or `shell_kill` it.',\n 'Prefer ending your turn and receiving the `<task-notification>` when you have other useful work or nothing depends on the result; use wait_task only when the next step genuinely needs the task\\'s outcome.',\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 timeout_ms: { type: 'integer', description: `Max milliseconds to wait before giving up (task keeps running). Default: ${DEFAULT_TIMEOUT_MS}. Capped at ${MAX_TIMEOUT_MS}.` },\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.waitBackground) {\n return `wait_task error: the active execution context (${ctx.execution.type}) does not support waiting on background tasks.`\n }\n\n // Distinguish \"unknown task\" from \"still running\" up front —\n // `waitBackground` collapses both into `null` on its miss path.\n //\n // Skipped on DURABLE (replay) contexts. `wait_task` is excluded from\n // journaling (its awakeable park can't nest in a journaled `ctx.run`), so\n // its body re-runs in full on every Restate replay. This `listBackground`\n // read is un-journaled, and branching on it gates whether the *journaled*\n // `waitBackground` (e.g. `restateWaitBackground`'s register/probe/settle\n // entries) runs — if the runner's registry flips across attempts (a restart\n // mid-park drops the task), the branch flips and the journal diverges\n // (`JournalMismatchError`). Durable hosts get the existence decision from\n // `waitBackground`'s own journaled probe instead (missing → `null` → the\n // timeout branch below). On non-durable contexts there's no journal to\n // diverge, so the more precise \"no such task\" message is kept.\n if (resolveDetachedTasksCapability(ctx.execution) !== 'durable' && ctx.execution.listBackground) {\n const entries = await ctx.execution.listBackground(ctx.handle)\n if (!entries.some(e => e.taskId === taskId)) {\n return `wait_task: no such task \"${taskId}\". It may have already exited and been cleaned up, or it was never started in this session.`\n }\n }\n\n const requested = typeof input.timeout_ms === 'number' && Number.isFinite(input.timeout_ms) && input.timeout_ms > 0\n ? input.timeout_ms\n : DEFAULT_TIMEOUT_MS\n const timeoutMs = Math.min(requested, MAX_TIMEOUT_MS)\n\n const info = await ctx.execution.waitBackground(ctx.handle, taskId, {\n timeoutMs,\n signal: ctx.signal,\n })\n\n if (!info) {\n return [\n `${WAIT_TASK_TIMED_OUT_PREFIX} after ${formatDuration(timeoutMs)} — ${taskId} is still running.`,\n 'You can wait again with a longer timeout, continue with other work (a `<task-notification>` arrives when it exits), or terminate it with `shell_kill`.',\n ].join('\\n')\n }\n\n return [\n `Task ${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 * Agent creation and state management.\n */\n\nimport type { Hookable } from 'hookable'\n\nimport type { ContextBreakdown, ContextBreakdownOptions, ContextExactCounts, ContextMcpGroup } from './chat/context-breakdown'\nimport type { ExecutionContext, ExecutionHandle, TaskExitInfo, TaskStallInfo } from './contexts'\nimport type { RunUsageMirror } from './loop'\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, CompactEndHookContext, CompactStartHookContext, McpServerConfig, McpToolHookContext, McpToolSchema, OAuthRefreshHookContext, ResolveContentRefBlock, SessionContentBlock, SessionEndStatus, SessionHookContext, SessionMessage, SessionTurn, SpawnHookContext, StreamHookContext, ToolHookContext, ToolOutcome, ToolResultContent, TurnUsage } from './types'\nimport { resolve as pathResolve } from 'node:path'\nimport { createHooks } from 'hookable'\nimport { augmentMcpDoubleUnderscoreAliases, buildAliasMaps, buildSanitizedWireAliases } from './aliasing'\nimport { buildContextBreakdown } from './chat/context-breakdown'\nimport { formatTaskSummary } from './chat/format'\nimport { OUTPUT_RESERVE_TOKENS } from './compact/policy'\nimport { createProcessContext, resolveDetachedTasksCapability } from './contexts'\nimport { installDedupToolsGate } from './dedup-tools'\nimport { AgentBudgetExceededError, errorMessage } from './errors'\nimport { DEFAULT_HOOK_TIMEOUT_MS, resolveAutoCompact, runLoop } from './loop'\nimport { connectMcpServers, wrapDiscoveredMcpTools } from './mcp'\nimport { buildPromptMessage, canonicalizePrompt } from './prompt'\nimport { installRepeatGuard } from './repeat-guard'\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 { effectiveInputFromTurn } from './stats'\nimport { appendStaticSection, renderSystemForWire } from './system-prompt'\nimport { resolveTimeoutMs, settleWithinTimeout, withTimeout } from './timeout'\nimport { installToolBudgetsGate } from './tool-budgets'\nimport { createToolCancelRegistry } from './tool-cancel'\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 { WAIT_TASK_TIMED_OUT_PREFIX } from './tools/wait-task'\nimport { DEFAULT_AGENT_CLOCK, toolResultToText } from './types'\nimport { escapeXml } from './xml'\n\n// ---------------------------------------------------------------------------\n// Context assembly snapshot\n// ---------------------------------------------------------------------------\n\n/**\n * A disclosed tool's raw inputs, captured for the context breakdown. The\n * JSON byte-sizing is deferred (see {@link materializeAssemblyTools}) so the\n * per-run snapshot stays off the hot path — only `getContextBreakdown()`\n * pays the `JSON.stringify`.\n */\ninterface DisclosedToolInput {\n /** Wire (alias-rewritten) name — the bytes the provider sends. */\n name: string\n description: string\n inputSchema: unknown\n /** MCP group key when this tool came from an MCP server; absent for native tools. */\n mcpServer?: string\n}\n\n/** A deferred (lazy, not-yet-disclosed) tool's raw inputs. */\ninterface DeferredToolInput {\n name: string\n description: string\n inputSchema: unknown\n /** Source MCP server, when applicable. */\n server?: string\n}\n\n/**\n * Snapshot of a run's assembled prompt pieces (system, wire tools, deferred\n * tools, MCP groups, skills/subagent catalogs) + the model used. Captured in\n * `run()` right after the prompt is finalized; read on demand by\n * `getContextBreakdown()`.\n *\n * Tool byte-sizing (`toolsJson` / `mcpGroups` / deferred variants) is NOT\n * computed here — only the raw `disclosedTools` / `deferredEntries` inputs are\n * stored. {@link materializeAssemblyTools} does the `JSON.stringify` lazily so\n * a normal run/subagent never pays for breakdown serialization it won't read.\n */\nexport interface ContextAssembly {\n modelId: string\n /** Full rendered system prompt (base + all appended catalogs), wire form. */\n system: string\n /**\n * Host-composed base system BEFORE the agent's appends (doctrine + rules\n * + env). The breakdown subtracts the rules block from this to get the\n * \"Base system\" bucket.\n */\n baseSystem: string\n /** Rendered AGENTS.md/CLAUDE.md block + its source files (the \"Rules\" bucket). */\n rulesBlock?: string\n rulesFiles?: { path: string, source: string }[]\n /** Disclosed tools (raw); stringified lazily by {@link materializeAssemblyTools}. */\n disclosedTools: DisclosedToolInput[]\n /** Deferred (lazy) tools (raw); stringified lazily. */\n deferredEntries: DeferredToolInput[]\n mcpInstructions?: string\n skillsCatalog?: string\n /** Searchable / subagent (spawn) tool catalog text appended to system. */\n subagentDefs?: string\n /**\n * The disclosed tools in PROVIDER WIRE SHAPE (`provider.formatTools`\n * output) — what `countTokens` must receive for an exact count. Distinct\n * from `disclosedTools`, which carries the internal shape used only for\n * heuristic byte-sizing + categorization.\n */\n wireTools: unknown[]\n}\n\n/**\n * Single builder for a {@link ContextAssembly}, shared by the per-run snapshot\n * and the pre-run assembly so the two can't drift on optionality / field\n * ordering. Callers pass every piece (optional fields may be `undefined`); this\n * normalizes the spread-conditional shape both producers previously inlined.\n */\nfunction assembleContextSnapshot(input: {\n modelId: string\n system: string\n baseSystem: string\n rulesBlock?: string | null\n rulesFiles?: { path: string, source: string }[]\n disclosedTools: DisclosedToolInput[]\n deferredEntries: DeferredToolInput[]\n mcpInstructions?: string | null\n skillsCatalog?: string | null\n subagentDefs?: string | null\n wireTools: unknown[]\n}): ContextAssembly {\n return {\n modelId: input.modelId,\n system: input.system,\n baseSystem: input.baseSystem,\n ...(input.rulesBlock ? { rulesBlock: input.rulesBlock } : {}),\n ...(input.rulesFiles?.length ? { rulesFiles: input.rulesFiles } : {}),\n disclosedTools: input.disclosedTools,\n deferredEntries: input.deferredEntries,\n ...(input.mcpInstructions ? { mcpInstructions: input.mcpInstructions } : {}),\n ...(input.skillsCatalog ? { skillsCatalog: input.skillsCatalog } : {}),\n ...(input.subagentDefs ? { subagentDefs: input.subagentDefs } : {}),\n wireTools: input.wireTools,\n }\n}\n\n/**\n * Lazily serialize an assembly's tool inputs into the byte-sized buckets the\n * context breakdown consumes. Deferred so the per-run snapshot stays cheap —\n * the `JSON.stringify` here only runs when `getContextBreakdown()` is called.\n *\n * MCP grouping preserves first-seen server order; native tools stay flat. The\n * output is byte-identical to the eager partition it replaced.\n */\nfunction materializeAssemblyTools(assembly: ContextAssembly): {\n toolsJson: string[]\n deferredToolsJson: string[]\n mcpGroups: ContextMcpGroup[]\n deferredMcpGroups: ContextMcpGroup[]\n} {\n const toolsJson: string[] = []\n const disclosedMcp = new Map<string, { name: string, json: string }[]>()\n for (const t of assembly.disclosedTools) {\n const json = JSON.stringify({ name: t.name, description: t.description, inputSchema: t.inputSchema })\n if (t.mcpServer !== undefined) {\n const bucket = disclosedMcp.get(t.mcpServer) ?? []\n bucket.push({ name: t.name, json })\n disclosedMcp.set(t.mcpServer, bucket)\n }\n else {\n toolsJson.push(json)\n }\n }\n\n const deferredToolsJson: string[] = []\n const deferredMcp = new Map<string, { name: string, json: string }[]>()\n for (const entry of assembly.deferredEntries) {\n const json = JSON.stringify({ name: entry.name, description: entry.description, inputSchema: entry.inputSchema })\n if (entry.server !== undefined) {\n const bucket = deferredMcp.get(entry.server) ?? []\n bucket.push({ name: entry.name, json })\n deferredMcp.set(entry.server, bucket)\n }\n else {\n deferredToolsJson.push(json)\n }\n }\n\n const toGroups = (m: Map<string, { name: string, json: string }[]>): ContextMcpGroup[] =>\n [...m.entries()].map(([server, list]) => ({ server, tools: list }))\n\n return {\n toolsJson,\n deferredToolsJson,\n mcpGroups: toGroups(disclosedMcp),\n deferredMcpGroups: toGroups(deferredMcp),\n }\n}\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 /**\n * Fires immediately before each turn's `provider.stream()` call, after\n * `context:transform` + pairing repair + `system:transform`. The\n * `options` object IS mutable as a last-mile escape hatch (per-turn\n * reminders, dynamic tags, audit wrapping), but prefer\n * {@link AgentHooks['context:transform']} for any mutation that touches\n * `messages` — it runs upstream of the defensive pairing pass and stays\n * cheaper. Loop re-runs `ensureToolResultPairing` + `ensureEndsWithUserMessage`\n * after this hook so a `messages` mutation here can't leak a\n * `tool_result must be preceded by a tool_call` 400 to the wire, but you\n * pay one extra walk for the privilege.\n */\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 * `aborted` — runtime emissions always include this boolean; it is optional\n * on the type only so tests/adapters that manually synthesize `turn:after`\n * payloads do not break (treat absent as `false`). `true` when the run's\n * `AbortController` was already tripped at the moment this `turn:after`\n * fired. This is the case to branch on instead of `usage.finishReason` when\n * emulating Stop-hook semantics: an aborted turn can still carry\n * `finishReason: 'stop'` (e.g. a final text turn the user cancelled\n * mid-stream), so `finishReason` alone cannot distinguish \"model finished\"\n * from \"we killed it\".\n *\n * Covers the streaming-abort path (cancel landed while the turn was\n * streaming) and any abort that landed before this fired. NOTE: a\n * `tool:gate` / `repeat-guard` abort trips DURING the tool batch, which runs\n * after this `turn:after` — so the offending turn reports `aborted: false`\n * here (its `finishReason` is `'tool-calls'`, never `'stop'`, so a\n * `'stop'`-keyed consumer won't misfire). That abort surfaces on the\n * subsequent `agent:abort`. For the post-completion window (host queues a\n * follow-up after a clean `turn:after`, then the run is aborted), rely on\n * {@link Agent.steer}/{@link Agent.followUp} returning `false` once aborted.\n * `false` on every normal turn boundary.\n */\n 'turn:after': (ctx: {\n turn: number\n turnId: string\n usage: TurnUsage\n message: SessionTurn\n aborted?: boolean\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 mid-stream when a provider that executes a tool server-side\n * (currently: Anthropic's `web_search_20250305`) closes the\n * server-tool-invocation content block. Symmetric with `tool:before` for\n * function tools — surface the call into the transcript at the moment the\n * model issued it, without waiting for the assistant message to finalize.\n *\n * Shape is intentionally generic so future server tools reuse the same\n * hook. `id` is the block's tool-use id (echoed back to the provider on\n * follow-up turns); pair with `stream:server_tool_result` via that id.\n *\n * Observational — handlers cannot block or substitute, since the call\n * has already executed server-side by the time we see it.\n */\n 'stream:server_tool_use': (ctx: StreamHookContext & {\n id: string\n name: string\n input: Record<string, unknown>\n }) => void\n /**\n * Pairs with {@link AgentHooks['stream:server_tool_use']} — fires when the\n * provider closes the matching server-tool-result block. `content` is the\n * raw block payload (either an error object or an array of result entries\n * carrying `encrypted_content` / `encrypted_index`); consumers format it\n * for display.\n */\n 'stream:server_tool_result': (ctx: StreamHookContext & {\n toolUseId: string\n toolName: string\n content: unknown\n }) => 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 /**\n * Fires when the loop catches a `retryable` provider error and is about\n * to sleep before re-issuing `provider.stream()`. Does NOT fire on the\n * terminal failure (the attempt the loop gives up on) — that lands on\n * `stream:error` instead, same as today.\n *\n * Use this hook to surface \"Anthropic overloaded — retrying in Ns\" in\n * the TUI, count retries in observability, or short-circuit further\n * retry logic from an outer harness.\n *\n * - `attempt` — 1-indexed number of the attempt that just failed.\n * - `nextAttempt` — `attempt + 1`, the attempt that will run after the sleep.\n * - `delayMs` — the wait the loop is about to take. Already accounts for\n * any `retry-after` / `retry-after-ms` header the server returned and\n * the configured `maxDelayMs` cap.\n * - `err` — the raw thrown value (same as `stream:error.err`).\n * - `statusCode` / `requestId` — extracted via the same helper as\n * `stream:error` when available.\n *\n * Observational — handlers cannot cancel the retry. To disable retry,\n * set `behavior.retry.maxAttempts: 1`.\n */\n 'stream:retry': (ctx: StreamHookContext & {\n attempt: number\n nextAttempt: number\n delayMs: number\n err: unknown\n statusCode?: number\n requestId?: string\n }) => void\n /**\n * Fires when the loop catches a `context_exceeded` provider error and, before\n * giving up, performs an emergency in-loop compaction (aggressive tail\n * elision of older `tool_result` bodies) and re-issues `provider.stream()`.\n * Does NOT fire when compaction can't shrink the request any further — that\n * lands on `stream:error` (the terminal failure) like before.\n *\n * Runs independently of `behavior.compactStrategy`: the recovery calls the\n * tail compactor directly, so it protects runs that never opted into\n * client-side compaction. It can only shrink tool-result-heavy histories — a\n * request whose mandatory text alone overflows still surfaces the error.\n *\n * - `attempt` — 1-indexed recovery pass (each pass keeps fewer trailing turns\n * intact and elides more; bounded).\n * - `err` — the raw `context_exceeded` error that triggered recovery.\n *\n * Observational — handlers cannot cancel the recovery.\n */\n 'context:overflow': (ctx: StreamHookContext & {\n attempt: number\n err: unknown\n }) => void\n /**\n * Fires when loop-native auto-compaction (`behavior.autoCompact`) crosses\n * its threshold and is about to run the summary call, BEFORE the provider\n * round-trip. Distinct from `context:overflow` (the reactive, wire-only\n * `context_exceeded` net) — this is the proactive heavy compaction that\n * summarizes older turns and mutates the session.\n *\n * Use it to surface a \"Compacting…\" banner / footer state. Observational\n * — handlers cannot cancel the compaction. Always paired with exactly one\n * `compact:end`.\n */\n 'compact:start': (ctx: CompactStartHookContext) => void\n /**\n * Fires exactly once after every `compact:start`, on success and failure\n * (`ctx.status`). On success it carries the replaced/dropped/restored\n * counts and the post-compact `effectiveTokens` estimate so hosts can drop\n * their context indicator immediately. On failure it carries `error`; the\n * loop continues with the un-compacted history (the reactive overflow net\n * still applies) and the circuit breaker counts the failure.\n */\n 'compact:end': (ctx: CompactEndHookContext) => void\n 'oauth:refresh': (ctx: OAuthRefreshHookContext) => void\n\n /**\n * Resolves persisted content refs into provider-bound payload bytes.\n *\n * Fires only when wire assembly sees a block carrying `ref`. Handlers set\n * `ctx.data` to the resolved payload. The bytes are wire-only and are never\n * written back to `session.turns`.\n */\n 'content-ref:resolve': (ctx: {\n block: ResolveContentRefBlock\n data?: string\n childId?: string\n depth?: number\n }) => 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 `tool_result` block with `content` = `Blocked: <reason>` and\n * `is_error: true`. `tool:before` / `tool:after` do **not** fire;\n * `tool:dispatched` does (with `outcome: 'gate-block'`) so trace /\n * audit consumers see the refusal. The exact wire payload is:\n *\n * ```jsonc\n * {\n * \"type\": \"tool_result\",\n * \"tool_use_id\": \"<call id>\",\n * \"content\": \"Blocked: <reason>\", // verbatim, no wrapping XML\n * \"is_error\": true\n * }\n * ```\n *\n * The `Blocked: ` prefix is fixed (single space, no colon variations);\n * the `<reason>` is whatever string the gate handler assigned to\n * `ctx.reason` (defaults to `'Tool execution was blocked'` if no\n * handler set it). Pinned by `test/dedup-tools.test.ts` so SDK\n * consumers can safely string-match for telemetry / eval scoring.\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 /**\n * Gate a tool call before it runs. Handlers share ONE mutable `ctx` and run\n * sequentially in registration order (the order they were `hook()`-ed; the\n * built-in guards — dedup, tool-budgets, repeat-guard — register at agent\n * construction, so host handlers added later run after them). The framework\n * reads the final state once all handlers (including async ones, which are\n * awaited) have run.\n *\n * Precedence across the resolved state:\n * - `block` beats `result`: if any handler left `block === true`, the call\n * is refused (`Blocked: <reason>`, `isError: true`) and any `result`\n * substitute is dropped. Security/refusal beats convenience/substitution.\n * - Among handlers, it is effectively last-writer-wins per field (each just\n * mutates the shared `ctx`). Cooperative built-ins self-defer — e.g.\n * `repeat-guard` early-returns when `ctx.block || ctx.result` is already\n * set, so it never double-penalizes a call another guard already broke.\n * Host handlers composing with the built-ins should do the same\n * (check `ctx.block`/`ctx.result` before acting) to keep precedence\n * predictable.\n *\n * Set `block = true` (+ `reason`) to refuse, or assign `result` to\n * short-circuit with a synthetic successful tool_result (the body never\n * runs; `tool:after` still fires). Mutating `ctx.input` rewrites the args\n * the body and downstream hooks see.\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 /**\n * `true` when the call's name has no matching registered tool — the\n * model invented it (hallucination) or referenced a tool whose\n * registration is gone (an MCP server dropped, an alias removed).\n *\n * Fires immediately BEFORE the `tool:unknown` substitute path so UI\n * consumers using `tool:before` to render a tool card still see\n * the model's attempt — otherwise the call was silently swallowed\n * and the model's intent never surfaced in the transcript.\n *\n * Pairing contract for `unknown: true` events:\n * - `tool:after` does NOT fire (the body never executed).\n * - `tool:error` fires next (unless the `tool:unknown` handler\n * sets `suppressError`).\n * - `tool:dispatched` fires with `outcome: 'unknown'`.\n *\n * Mutations to `ctx.input` / `ctx.coercions` on this path are\n * observational only — there's no body to dispatch them into.\n */\n unknown?: boolean\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 * Every call that produces a tool_result closes with exactly one\n * terminal event, so pair `tool:dispatched` with that terminal to\n * reconstruct message history / close per-call spans:\n * - `outcome: 'execute'` → closes with `tool:after` OR, if the call was\n * cancelled in-flight via `agent.cancelTool`, with `tool:cancelled`\n * (NOT `tool:after`). A span keyed on `dispatched(execute)→after`\n * will leak on cancel — also listen for `tool:cancelled`.\n * - `outcome: 'gate-substitute'` → closes with `tool:after`.\n * - `outcome: 'gate-block' | 'invalid-input'` → no `tool:after` (the\n * body never ran); the dispatch event itself carries the result.\n * - `outcome: 'unknown'` → no `tool:after`; closes after\n * `tool:unknown` (+ optional `tool:error`).\n * The strict direction always holds: no `tool:after` fires without a\n * preceding `tool:dispatched`.\n *\n * Distinct from `tool:before`: `tool:before` fires when a tool's body\n * is about to execute (the legacy \"I'm running this tool\" notification)\n * OR — as a special case — once with `unknown: true` for hallucinated\n * tool names, so UI consumers rendering tool cards still see the\n * model's attempt. It still skips gate-block / gate-substitute /\n * validation-reject paths. `tool:dispatched` fires on all five paths\n * with an `outcome` discriminator so live consumers never see a\n * `tool:after` whose 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 /**\n * Structured side-effect outcome for mutating tools that reported one via\n * `ToolContext.reportOutcome` (built-in `write_file` / `edit` /\n * `multi_edit` do). `'noop'` means the call succeeded but changed zero\n * bytes — the signal hosts need to break no-progress loops without\n * string-matching the result text. Absent for read-only tools and any\n * tool that didn't report. See {@link ToolOutcome}.\n */\n outcome?: ToolOutcome\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_<server>_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:server_tool_use` from a subagent. See {@link AgentHooks['stream:server_tool_use']}. */\n 'child:stream:server_tool_use': (ctx: StreamHookContext & {\n id: string\n name: string\n input: Record<string, unknown>\n childId: string\n depth: number\n }) => void\n /** Bubbled `stream:server_tool_result` from a subagent. See {@link AgentHooks['stream:server_tool_result']}. */\n 'child:stream:server_tool_result': (ctx: StreamHookContext & {\n toolUseId: string\n toolName: string\n content: unknown\n childId: string\n depth: number\n }) => 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 /** See `tool:after.outcome` — structured side-effect outcome for mutating tools. */\n outcome?: ToolOutcome\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 /** See `turn:after.aborted` — runtime emissions include it; treat absent as `false`. */\n aborted?: boolean\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 /** Diagnostic line from a server. For fatal failures hook `mcp:error`. */\n 'mcp:warn': (ctx: { name: string, message: string }) => 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 exactly ONCE per bootstrap batch, after every server's attempt\n * has settled. `results` is sorted by server name — a deterministic\n * aggregate, unlike the per-server `mcp:bootstrap:end` events which\n * fire in network-completion order inside the parallel bootstrap.\n * Durable-execution hosts that journal bootstrap outcomes should hook\n * this instead of buffering + sorting the per-server stream.\n */\n 'mcp:bootstrap:settled': (ctx: {\n results: ReadonlyArray<{ name: string, transport: string, durationMs: number } & (\n | { ok: true, toolCount: number, lazy?: boolean }\n | { ok: false, skipped?: boolean, error?: Error }\n )>\n }) => 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 /**\n * The stall watchdog ({@link AgentBehavior.backgroundStallWatchdogMs})\n * detected a background task with no output for the configured window.\n * One-shot per quiet period — re-arms after fresh output. The agent\n * queues a `<task-stall>` notification for the model's next turn;\n * hosts can additionally render a \"task may be stuck\" hint.\n */\n 'background:stall': (ctx: {\n taskId: string\n command: string\n outputPath: string\n stalledForMs: number\n bytesWritten: number\n }) => void\n\n /**\n * Fires at the start of an `agent.run()` that has background exits to\n * deliver, BEFORE they are rendered into `<task-notification>`\n * blocks (runs with nothing to deliver skip the hook). `exits` merges\n * the push-fed queue (`background:exit` arrivals since the last run)\n * with a pull-based reconcile against\n * `ExecutionContext.listBackground` — so exits land even when the\n * context never calls `onExit` (remote / durable contexts can't push\n * from a timer into a host journal).\n *\n * This is the journalable seam for durable-execution hosts: the hook\n * runs inside the `run()` await chain, so a handler may replace\n * `ctx.exits` with a journaled copy (e.g.\n * `ctx.exits = await restateCtx.run('background-reconcile', () => ctx.exits)`)\n * making the wall-clock-derived `durationMs` — and therefore the\n * whole `<task-notification>` block — replay-stable.\n *\n * Mutable: handlers may reorder, filter, or substitute entries. The\n * post-hook array is what the model sees.\n */\n 'background:reconcile': (ctx: { exits: TaskExitInfo[] }) => void\n\n /**\n * Fires exactly once per agent, the first time the execution handle is bound\n * — right after the lazy `execution.spawn()` (or immediately on the first run\n * when a handle was injected via {@link AgentOptions.handle}). `spawned` is\n * `true` when the agent minted the handle, `false` for an injected one.\n *\n * The seam durable hosts use to record the minted handle id (e.g. a sandbox\n * id) for re-attach on replay/resume — journal it here, then re-attach via\n * `AgentOptions.handle` + the provider's reconnect option on the next\n * invocation. Observational; the handle is not mutable.\n */\n 'execution:ready': (ctx: { handle: ExecutionHandle, spawned: boolean }) => 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 * Fires when the consecutive-identical {@link AgentBehavior.repeatGuard}\n * escalates on a tracked tool. In chain mode, `count` is the number of\n * back-to-back identical calls including the offending one; in window mode it\n * is the number of occurrences inside the configured window. `action`\n * reflects the escalation: `'block'` refused this call via `tool:gate`;\n * `'steer'` let it execute and appended a steering message to the result;\n * `'abort'` additionally aborts the run via the agent's `AbortController`\n * (the loop then fires `agent:abort`). The `'abort'` event fires BEFORE the\n * abort so consumers can distinguish a guard abort from a user-driven cancel.\n */\n 'repeat-guard:exceeded': (ctx: {\n tool: string\n count: number\n threshold: number\n turnId: string\n action: 'block' | 'steer' | 'abort'\n }) => void\n\n // Agent lifecycle\n /**\n * The run was aborted — fires exactly once per aborted run, on every abort\n * exit: a between-turn cancel (the loop's `signal.aborted` check), a\n * `repeat-guard` abort, AND a cancel that lands mid-stream (which throws out\n * of the turn and is caught at the run boundary). For a guard abort a\n * `repeat-guard:exceeded` (`action: 'abort'`) event fired first, so consumers\n * can attribute the cause.\n *\n * Does NOT fire on graceful non-cancel terminations that still finalize the\n * session run as `'aborted'`: `maxConsecutivePauseTurns` exhaustion and the\n * `maxCostUsd`/`maxTotalTokens` budget breaker (the latter throws\n * `AgentBudgetExceededError`). Branch on those via their own signals\n * (`finishReason: 'pause'`, the typed error) rather than `agent:abort`.\n *\n * `runId` — the aborted run, for correlating with `turn:after` / telemetry\n * spans. `reason` — the string passed to `AbortController.abort(reason)` when\n * one was supplied (mirrors `signal.reason`); absent for a bare `abort()`.\n */\n 'agent:abort': (ctx: { runId?: string, reason?: string }) => void\n /**\n * Run finished — fires on all exit paths (completion, maxTurns, abort, error).\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 * Durable-execution replay finished — the invocation transitioned from\n * journal replay to live execution (the first non-journaled provider\n * stream or tool body just started).\n *\n * The framework core never fires this: it is emitted by durable-runtime\n * adapters (see `createReplayTracker({ hooks })` in `zidane/restate`)\n * once per handler invocation. Hosts that previously inferred liveness\n * from side channels (counting live provider calls, watching stream\n * hooks) should key on this instead. Fires at most once per invocation;\n * never fires when the journal replays to completion without going\n * live (nothing left to execute — and nothing left to abort).\n */\n 'replay:done': (ctx: Record<string, never>) => 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\n/**\n * Acknowledgment evidence recoverable from persisted turn history —\n * which background tasks the model has ALREADY been told about (or\n * learned about itself) in prior runs of this session.\n *\n * The agent's in-memory `notifiedTaskIds` latch only lives as long as\n * the agent instance. Durable-execution hosts (Restate virtual objects)\n * reconstruct the agent on every handler invocation while the remote\n * context's task registry — and its terminated entries — survive. Without\n * a durable record, every new invocation's pull-based reconcile would\n * re-notify the same exits. The session history IS that durable record:\n * the drained `<task-notification>` blocks, `shell_kill` calls,\n * successful `wait_task` results, and `read_file` calls against a task's\n * output path are all persisted turns. Scanning them re-derives the\n * latch with zero extra state.\n *\n * Callers pass the FULL persisted history (`session.turns`), not the\n * runtime-filtered view: subagent-run turns (filtered from the resumed\n * conversation) still carry acks, and the built-in compaction contract\n * is marker-based (`compact-summary` elides turns at the wire level\n * only) so the evidence is append-only by design. Hosts that rewrite\n * `session.turns` destructively forfeit this dedupe — same assumption\n * the lazy-disclosure replay and read-state hydration already make.\n */\ninterface TaskAckHistory {\n /** Task ids notified, killed, or successfully waited on in prior turns. */\n taskIds: Set<string>\n /**\n * `path.resolve`-normalized paths the model read via `read_file` /\n * `read`, mapped to the LATEST `turn.createdAt` of such a read. A read\n * only acknowledges a task's exit when it happened AFTER the task\n * ended (`readAt >= entry.endedAt + skew margin`) — reading a\n * still-running task's log (progress check, stall inspection) must not\n * swallow the exit notification that hasn't happened yet. Mirrors the\n * live `tool:after` listener, which only deletes PENDING (i.e.\n * already-exited) entries.\n */\n outputPaths: Map<string, number>\n}\n\n/**\n * Safety margin for the read-ack ordering comparison, which crosses two\n * clock domains: `turn.createdAt` comes from the host's `AgentClock`\n * (possibly a journaled clock) while `TaskEntry.endedAt` comes from the\n * execution context's wall clock (possibly a remote runner). The turn\n * timestamp already UNDERSTATES the actual read time (the assistant turn\n * is created before its tool executes), so the only false-suppression\n * window is the runner clock lagging the host clock. Requiring the read\n * to land this many ms after the exit absorbs that skew: within the\n * margin we fail open (redundant notification — harmless), and false\n * suppression needs cross-machine skew beyond it (broken NTP territory).\n */\nconst READ_ACK_CLOCK_SKEW_MARGIN_MS = 5_000\n\nfunction collectTaskAcksFromTurns(turns: readonly SessionTurn[]): TaskAckHistory {\n const taskIds = new Set<string>()\n const outputPaths = new Map<string, number>()\n // wait_task call ids → task ids, resolved against their tool_result:\n // only a wait that RETURNED exit info counts as an acknowledgment\n // (timeouts leave the eventual exit un-delivered — see the live\n // `tool:after` suppression listener, whose semantics this mirrors).\n const pendingWaits = new Map<string, string>()\n for (const turn of turns) {\n for (const block of turn.content) {\n if (block.type === 'text') {\n if (block.text.startsWith('<task-notification>')) {\n const match = block.text.match(/<task-id>([^<]+)<\\/task-id>/)\n if (match)\n taskIds.add(match[1]!)\n }\n continue\n }\n if (block.type === 'tool_call') {\n if (block.name === 'shell_kill' && typeof block.input.task_id === 'string') {\n taskIds.add(block.input.task_id)\n }\n else if (block.name === 'wait_task' && typeof block.input.task_id === 'string') {\n pendingWaits.set(block.id, block.input.task_id)\n }\n else if ((block.name === 'read_file' || block.name === 'read') && typeof block.input.path === 'string' && block.input.path.length > 0) {\n const path = pathResolve(block.input.path)\n outputPaths.set(path, Math.max(outputPaths.get(path) ?? 0, turn.createdAt))\n }\n continue\n }\n if (block.type === 'tool_result' && !block.isError) {\n const waitedTaskId = pendingWaits.get(block.callId)\n if (waitedTaskId !== undefined) {\n const text = typeof block.output === 'string' ? block.output : toolResultToText(block.output)\n if (text.startsWith('Task '))\n taskIds.add(waitedTaskId)\n }\n }\n }\n }\n return { taskIds, outputPaths }\n}\n\n/**\n * Wire format for a stall-watchdog signal. Distinct outer tag from\n * `<task-notification>` so the model can tell \"still running, but quiet\"\n * apart from \"terminated\". Fields mirror {@link TaskStallInfo}.\n */\nfunction renderTaskStallXml(info: TaskStallInfo): string {\n return [\n '<task-stall>',\n ` <task-id>${escapeXml(info.taskId)}</task-id>`,\n ` <command>${escapeXml(info.command)}</command>`,\n ` <output-file>${escapeXml(info.outputPath)}</output-file>`,\n ` <stalled-for-ms>${info.stalledForMs}</stalled-for-ms>`,\n ` <bytes-written>${info.bytesWritten}</bytes-written>`,\n ` <hint>The task is still running but has produced no output for the reported window. It may be stuck at an interactive prompt — read the output file tail; if it ends in a question, kill it with shell_kill and re-run with piped input or a --yes flag.</hint>`,\n '</task-stall>',\n ].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:server_tool_use',\n 'stream:server_tool_result',\n 'stream:error',\n 'stream:retry',\n 'context:overflow',\n 'compact:start',\n 'compact:end',\n 'oauth:refresh',\n 'content-ref:resolve',\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:server_tool_use',\n 'child:stream:server_tool_result',\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:warn',\n 'mcp:close',\n 'mcp:bootstrap:start',\n 'mcp:bootstrap:end',\n 'mcp:bootstrap:settled',\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 'background:stall',\n 'background:reconcile',\n 'execution:ready',\n 'skills:resolve',\n 'skills:catalog',\n 'skills:activate',\n 'skills:deactivate',\n 'usage',\n 'output',\n 'budget:exceeded',\n 'tool-budget:exceeded',\n 'repeat-guard: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\n/**\n * Custom MCP connector — the seam that owns \"connect `mcpServers`, discover\n * their tools, hand them back\". Supply one via {@link AgentOptions.mcpConnector}\n * to inject a custom transport / auth provider (e.g. OAuth via\n * `connectMcpServers(configs, undefined, hooks, { buildAuthProvider })`).\n *\n * To journal discovered tools for a durable runtime, use the separate\n * {@link AgentOptions.mcpToolWrap} hook (e.g. `restateMcpToolWrap` from\n * `zidane/restate`) — it composes with whatever connector is in play.\n *\n * The agent passes its own `hooks` so a connector that delegates to\n * {@link connectMcpServers} can forward them and keep every `mcp:*` event\n * firing. A connector is responsible for whatever the default path does that\n * it cares about (config normalization, namespacing, per-server\n * `instructions`) — delegating to `connectMcpServers` preserves all of it.\n */\nexport type McpConnector = (\n configs: McpServerConfig[],\n hooks: Hookable<AgentHooks>,\n) => Promise<McpConnection>\n\n/** Identity of a discovered MCP tool, passed to {@link McpToolWrap}. */\nexport interface McpToolMeta {\n /** Server name from `mcpServers` config (the `<server>` in `mcp_<server>_<tool>`). */\n server: string\n /** The server-side tool name, un-namespaced. */\n toolName: string\n}\n\n/**\n * Wrap each discovered MCP tool right after bootstrap, before it's registered\n * into the agent's tool map — see {@link AgentOptions.mcpToolWrap}. The MCP\n * counterpart to wrapping native tools yourself before `createAgent`.\n */\nexport type McpToolWrap = (tool: ToolDef, meta: McpToolMeta) => ToolDef\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 /**\n * Pre-bound execution handle. When set, the agent uses it verbatim and does\n * NOT call `execution.spawn()` — and does NOT `destroy()` it on teardown,\n * since the host owns its lifecycle.\n *\n * The durable-execution use case: a host that needs the spawn to be a\n * journaled, replay-stable side effect drives the spawn itself\n * (`const handle = await ctx.run('spawn', () => execution.spawn())`) and hands\n * the resulting handle here. On replay the journaled handle comes back\n * identical, so the agent re-attaches to the same sandbox/container instead of\n * minting a fresh one (which the internal lazy spawn, being un-journaled,\n * cannot guarantee). Pair with a context that can re-attach by handle id (e.g.\n * `createSandboxContext`, whose handle id IS the sandbox id) and a provider\n * `sandboxId` for the underlying backend.\n *\n * For host-observation without driving spawn yourself, see the\n * `execution:ready` hook, which fires once with the bound handle.\n */\n handle?: ExecutionHandle\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 * Override how `mcpServers` are connected and their tools discovered —\n * see {@link McpConnector}. The default connects via\n * {@link connectMcpServers}; supply your own to inject a custom transport /\n * auth provider. To journal discovered tools for a durable runtime, use\n * {@link mcpToolWrap} instead — it composes with any connector.\n *\n * Invoked lazily on the first `run()` / `warmup()`, with the agent's\n * hooks. A connector that bypasses {@link connectMcpServers} also bypasses\n * its config normalization, so prefer delegating to it.\n */\n mcpConnector?: McpConnector\n /**\n * Wrap every discovered MCP tool right after bootstrap, before it's\n * registered into the agent's tool map — see {@link McpToolWrap}. This is\n * the seam native tools have (you wrap them before `createAgent`) that\n * discovered MCP tools lack: the agent owns their discovery, so it applies\n * the wrap on your behalf, for both eager and lazily-connected servers and\n * regardless of which {@link mcpConnector} produced them.\n *\n * The primary use is durable execution: under Restate, pass\n * `mcpToolWrap: restateMcpToolWrap(ctx)` (from `zidane/restate`) to journal\n * each MCP tool's result the way `wrapAgentTools` journals native tools.\n * Discovery, `mcp_<server>_<tool>` namespacing, per-server `instructions`,\n * hooks, and OAuth all stay owned by the agent — the wrap only re-shapes\n * `execute`.\n */\n mcpToolWrap?: McpToolWrap\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 * Tune {@link Agent.cancelTool}'s per-call kill registry.\n *\n * - `defaultMessage` — reason stamped on the abort signal (and surfaced to\n * the `tool:cancelled` hook) when `cancelTool(callId)` is called without an\n * explicit reason. Defaults to `'user-cancelled-tool'`.\n *\n * The registry also handles the pre-registration race: a `cancelTool` issued\n * for a call the loop hasn't dispatched yet (the host saw the id in the\n * streamed turn) is parked and applied the moment the call registers, rather\n * than dropped. No configuration needed for that — it's always on.\n */\n toolKill?: {\n defaultMessage?: string\n }\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 /**\n * `true` while at least one background-task exit is queued for delivery\n * on the next `run()` (the push-fed `background:exit` queue; pending\n * stall signals don't count). Exits already acknowledged by the model\n * (`shell_kill`, a successful `wait_task`, reading the output file)\n * never surface here.\n *\n * This is the host-side \"auto-wake\" signal: when it flips true while\n * the agent is idle, a drain-only `run({ prompt: '' })` delivers the\n * `<task-notification>` block without waiting for the user's next\n * prompt — prompt-less runs are explicitly legal in that state (see\n * `validateAndPrepareResume`).\n */\n readonly hasPendingTaskNotifications: boolean\n /**\n * Queue a mid-run nudge injected as a user turn at the next tool/turn\n * boundary. Returns `false` (and queues nothing) when no run is in\n * flight or when the run's `AbortController` is already tripped — a\n * steer can never revive or confuse a dead run, and a message queued\n * between runs would otherwise leak into the NEXT run and skip its\n * first turn's tool calls. Returns `true` when queued.\n *\n * Hosts emulating Stop-hook semantics (queueing a follow-up on\n * `turn:after`) should rely on this: a guard/user abort can fire mid-turn\n * while `turn:after` still reports `finishReason: 'stop'`, so an\n * unconditional enqueue would otherwise leak a message into a dead run.\n */\n steer: (message: string) => boolean\n /**\n * Queue a message delivered after the run would otherwise end (the model\n * returned a final answer). Same abort contract as {@link steer}: no-ops\n * and returns `false` once the run is aborted, `true` when queued.\n */\n followUp: (message: string) => boolean\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 * Categorized context-window usage — system prompt, rules, skills, MCP\n * tools/instructions, subagent defs, tool definitions, conversation, deferred\n * buckets, autocompact buffer, and free space. Computed on demand (the host\n * opens a panel / popover). Available even before the first run: when no run\n * snapshot exists yet it assembles a skills-only pre-run view (no MCP connect),\n * so it never returns `null` for a live agent — only after `destroy()`.\n *\n * Per-category counts are heuristic estimates reconciled so the live\n * categories sum to the real last-turn total; when the provider exposes\n * {@link Provider.countTokens} (Anthropic, OpenAI), the system/tools buckets\n * use exact counts and drop their `estimated` flag. One network round-trip\n * on the exact path; the host decides when to call it.\n *\n * The host supplies `effectiveWindow` (the model's `rawWindow -\n * outputReserve`, resolved via the chat model registry which the agent core\n * deliberately doesn't depend on). When omitted, free-space / fraction are\n * computed against `used` only (bar still renders, just without headroom).\n */\n getContextBreakdown: (opts?: ContextBreakdownOptions) => Promise<ContextBreakdown | null>\n /**\n * TC39 explicit-resource-management `await using` sink — alias for\n * {@link Agent.destroy}. Lets hosts write:\n *\n * ```ts\n * await using agent = createAgent({ ... })\n * await agent.run({ prompt: 'go' })\n * // agent.destroy() runs automatically at scope exit\n * ```\n *\n * Requires Node 20+ (or any runtime that ships the TC39\n * explicit-resource-management proposal). Older runtimes simply ignore\n * the symbol; nothing breaks. Idempotent because `destroy()` is.\n */\n [Symbol.asyncDispose]: () => Promise<void>\n}\n\n// ---------------------------------------------------------------------------\n// Behavior resolution\n// ---------------------------------------------------------------------------\n\nconst DEFAULT_CUSTOM_MCP_CONNECT_TIMEOUT_MS = 2 * 60 * 1000\n/** Default watchdog for the first `executionContext.spawn()`. See `AgentBehavior.spawnTimeoutMs`. */\nconst DEFAULT_SPAWN_TIMEOUT_MS = 5 * 60 * 1000\n/** Default watchdog for `agent.destroy()` teardown awaits. See `AgentBehavior.destroyTimeoutMs`. */\nconst DEFAULT_DESTROY_TIMEOUT_MS = 30 * 1000\n\n/**\n * Result of merging agent-level and per-run {@link AgentBehavior}. Mirrors the\n * subset of `AgentBehavior` fields the loop reads; the five fields with\n * built-in defaults (`cache`, `compactStrategy`, `toolDisclosure`,\n * `mcpToolNameSeparator`, `surfaceMcpInstructions`, `strictToolPairing`) are\n * always present.\n */\ntype DefaultedBehaviorKeys = 'cache' | 'compactStrategy' | 'toolDisclosure' | 'mcpToolNameSeparator' | 'surfaceMcpInstructions' | 'strictToolPairing'\ntype ResolvedBehaviorKeys\n = | 'maxConcurrentTools' | 'maxTurns' | 'maxCostUsd' | 'retry' | 'maxTotalTokens' | 'maxWallMs'\n | 'providerStreamStartTimeoutMs' | 'providerStreamTimeoutMs' | 'mcpConnectTimeoutMs'\n | 'hookTimeoutMs' | 'spawnTimeoutMs' | 'destroyTimeoutMs'\n | 'maxTokens' | 'thinkingBudget' | 'modelOptions' | 'schema' | 'toolOutputBudget'\n | 'toolOutputBudgetExcludeTools' | 'compactThreshold' | 'compactKeepTurns'\n | 'thinkingDecay' | 'dedupReads' | 'dedupTools' | 'requireReadBeforeEdit'\n | 'toolBudgets' | 'repeatGuard' | 'readLineNumbers' | 'elideStaleReads' | 'mediaKeepTurns' | 'autoCompact' | 'toolSearch'\n | 'persistThreshold' | 'persistExcludeTools' | 'persistDir' | 'persistMaxBytes'\n | 'tasksDir' | 'shellLongTimeoutMs' | 'disableBackgroundTasks' | 'maxConsecutivePauseTurns' | 'persistTurns'\n | 'maxPairingRepairsPerTurn' | 'maxTurnsWarning'\n | 'backgroundOutputCap' | 'backgroundStallWatchdogMs' | 'extensions'\n | 'toolBatchExecutor' | 'toolBatchTimeoutMs' | 'shouldRethrowToolError'\n | DefaultedBehaviorKeys\n\nexport type ResolvedBehavior\n = & Pick<AgentBehavior, Exclude<ResolvedBehaviorKeys, DefaultedBehaviorKeys>>\n & Required<Pick<AgentBehavior, DefaultedBehaviorKeys>>\n\nfunction resolveBehavior(\n agentBehavior?: AgentBehavior,\n runBehavior?: AgentBehavior,\n): ResolvedBehavior {\n return {\n maxConcurrentTools: runBehavior?.maxConcurrentTools ?? agentBehavior?.maxConcurrentTools,\n toolBatchExecutor: runBehavior?.toolBatchExecutor ?? agentBehavior?.toolBatchExecutor,\n toolBatchTimeoutMs: runBehavior?.toolBatchTimeoutMs ?? agentBehavior?.toolBatchTimeoutMs,\n shouldRethrowToolError: runBehavior?.shouldRethrowToolError ?? agentBehavior?.shouldRethrowToolError,\n maxTurns: runBehavior?.maxTurns ?? agentBehavior?.maxTurns,\n maxTurnsWarning: runBehavior?.maxTurnsWarning ?? agentBehavior?.maxTurnsWarning,\n maxCostUsd: runBehavior?.maxCostUsd ?? agentBehavior?.maxCostUsd,\n retry: runBehavior?.retry ?? agentBehavior?.retry,\n maxTotalTokens: runBehavior?.maxTotalTokens ?? agentBehavior?.maxTotalTokens,\n maxWallMs: runBehavior?.maxWallMs ?? agentBehavior?.maxWallMs,\n providerStreamStartTimeoutMs: runBehavior?.providerStreamStartTimeoutMs ?? agentBehavior?.providerStreamStartTimeoutMs,\n providerStreamTimeoutMs: runBehavior?.providerStreamTimeoutMs ?? agentBehavior?.providerStreamTimeoutMs,\n mcpConnectTimeoutMs: runBehavior?.mcpConnectTimeoutMs ?? agentBehavior?.mcpConnectTimeoutMs,\n hookTimeoutMs: runBehavior?.hookTimeoutMs ?? agentBehavior?.hookTimeoutMs,\n spawnTimeoutMs: runBehavior?.spawnTimeoutMs ?? agentBehavior?.spawnTimeoutMs,\n destroyTimeoutMs: runBehavior?.destroyTimeoutMs ?? agentBehavior?.destroyTimeoutMs,\n maxTokens: runBehavior?.maxTokens ?? agentBehavior?.maxTokens,\n thinkingBudget: runBehavior?.thinkingBudget ?? agentBehavior?.thinkingBudget,\n modelOptions: runBehavior?.modelOptions ?? agentBehavior?.modelOptions,\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 repeatGuard: runBehavior?.repeatGuard ?? agentBehavior?.repeatGuard,\n readLineNumbers: runBehavior?.readLineNumbers ?? agentBehavior?.readLineNumbers,\n elideStaleReads: runBehavior?.elideStaleReads ?? agentBehavior?.elideStaleReads,\n mediaKeepTurns: runBehavior?.mediaKeepTurns ?? agentBehavior?.mediaKeepTurns,\n autoCompact: runBehavior?.autoCompact ?? agentBehavior?.autoCompact,\n toolDisclosure: runBehavior?.toolDisclosure ?? agentBehavior?.toolDisclosure ?? 'eager' as const,\n mcpToolNameSeparator: runBehavior?.mcpToolNameSeparator ?? agentBehavior?.mcpToolNameSeparator ?? '_' as const,\n toolSearch: runBehavior?.toolSearch ?? agentBehavior?.toolSearch,\n surfaceMcpInstructions: runBehavior?.surfaceMcpInstructions ?? agentBehavior?.surfaceMcpInstructions ?? true,\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 shellLongTimeoutMs: runBehavior?.shellLongTimeoutMs ?? agentBehavior?.shellLongTimeoutMs,\n disableBackgroundTasks: runBehavior?.disableBackgroundTasks ?? agentBehavior?.disableBackgroundTasks,\n backgroundOutputCap: runBehavior?.backgroundOutputCap ?? agentBehavior?.backgroundOutputCap,\n backgroundStallWatchdogMs: runBehavior?.backgroundStallWatchdogMs ?? agentBehavior?.backgroundStallWatchdogMs,\n // Shallow merge (not last-writer-wins on the whole bag) so a run can\n // override ONE host extension without re-declaring the rest.\n extensions: (runBehavior?.extensions || agentBehavior?.extensions)\n ? { ...agentBehavior?.extensions, ...runBehavior?.extensions }\n : undefined,\n strictToolPairing: runBehavior?.strictToolPairing ?? agentBehavior?.strictToolPairing ?? false,\n maxPairingRepairsPerTurn: runBehavior?.maxPairingRepairsPerTurn ?? agentBehavior?.maxPairingRepairsPerTurn,\n maxConsecutivePauseTurns: runBehavior?.maxConsecutivePauseTurns ?? agentBehavior?.maxConsecutivePauseTurns,\n persistTurns: runBehavior?.persistTurns ?? agentBehavior?.persistTurns,\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\nfunction resolveMcpToolParts(\n canonicalName: string,\n servers: readonly McpServerConfig[] | undefined,\n): { server: string, tool: string } | undefined {\n const server = resolveServerForTool(canonicalName, servers)\n if (server) {\n const prefix = `mcp_${server.name}_`\n return { server: server.name, tool: canonicalName.slice(prefix.length) }\n }\n\n if (!canonicalName.startsWith('mcp_'))\n return undefined\n const tail = canonicalName.slice(4)\n const sep = tail.indexOf('_')\n if (sep <= 0 || sep >= tail.length - 1)\n return undefined\n return { server: tail.slice(0, sep), tool: tail.slice(sep + 1) }\n}\n\nfunction buildMcpToolNameSeparatorAliases(\n mcpToolNames: Iterable<string>,\n servers: readonly McpServerConfig[] | undefined,\n separator: NonNullable<AgentBehavior['mcpToolNameSeparator']>,\n): Record<string, string> | undefined {\n if (separator === '_')\n return undefined\n\n const aliases: Record<string, string> = {}\n for (const canonicalName of mcpToolNames) {\n const parts = resolveMcpToolParts(canonicalName, servers)\n if (!parts)\n continue\n const wireName = `mcp${separator}${parts.server}${separator}${parts.tool}`\n if (wireName !== canonicalName)\n aliases[canonicalName] = wireName\n }\n return Object.keys(aliases).length > 0 ? aliases : undefined\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 * Render the per-server MCP `instructions` payloads into a single\n * `# MCP Server Instructions` section. Matches the shape the Claude Code\n * SDK emits so eval traces stay comparable.\n *\n * Empty / whitespace-only entries are filtered out upstream (see\n * `bootstrapServer`); this function only renders what survived. Callers\n * that pass an empty map get an empty string back — no stub heading.\n *\n * Output shape:\n *\n * # MCP Server Instructions\n *\n * ## acme\n * The project is provisioned. Use `apply_migration` directly.\n *\n * ## linear\n * …\n *\n * Server names are emitted in Map iteration order (config order), which\n * is byte-stable across runs.\n */\nfunction renderMcpInstructionsSection(instructions: ReadonlyMap<string, string>): string {\n if (instructions.size === 0)\n return ''\n const parts: string[] = ['# MCP Server Instructions', '']\n let first = true\n for (const [name, body] of instructions) {\n if (!first)\n parts.push('')\n first = false\n parts.push(`## ${name}`)\n parts.push(body.trim())\n }\n return parts.join('\\n')\n}\n\n// ---------------------------------------------------------------------------\n// createAgent\n// ---------------------------------------------------------------------------\n\n/**\n * Set of runIds belonging to subagent (depth > 0) runs in a session. Used to\n * filter child-run turns out of the resumed conversation and out of\n * last-turn-usage accounting — both need \"everything a subagent produced\",\n * keyed by `runId`. Returns an empty set for a sessionless agent.\n */\nfunction childRunIdSet(session: Session | null | undefined): Set<string> {\n const ids = new Set<string>()\n for (const r of session?.runs ?? []) {\n if ((r.depth ?? 0) > 0)\n ids.add(r.id)\n }\n return ids\n}\n\n/**\n * Validate `agent.run()`'s prompt/resume preconditions and compute the\n * unresolved-tool-filtered resume view (returned for reuse in the seed block).\n *\n * `prompt` is required unless resuming a session that already has turns. The\n * resume guard inspects the trailing turn AFTER filtering unresolved tool_uses\n * (L1) — a session that ended on an orphan assistant turn (process death\n * mid-tool, `kill -9`) gets its trailing assistant dropped before the\n * \"trailing must be user\" check, so a recoverable session doesn't get a\n * spurious `cannot resume without prompt` error.\n *\n * Throws on an unrecoverable resume request; otherwise returns the filtered\n * turns (or `undefined` when the session has no turns).\n */\nfunction validateAndPrepareResume(\n session: Session | null | undefined,\n prompt: AgentRunOptions['prompt'],\n hasPendingTaskNotifications = false,\n): SessionTurn[] | undefined {\n const hasSessionTurns = !!session && session.turns.length > 0\n if (!prompt && !hasSessionTurns && !hasPendingTaskNotifications)\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\n // A prompt-less run is also legal when background-task exits are queued:\n // the run-start drain seeds a user turn carrying the `<task-notification>`\n // blocks, so the conversation stays well-formed even though the trailing\n // persisted turn is a completed assistant message. This is the \"auto-wake\"\n // path — hosts call `run({ prompt: '' })` when `hasPendingTaskNotifications`\n // flips true while the agent is idle.\n if (!prompt && resumeFilteredTurns && !hasPendingTaskNotifications) {\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 return resumeFilteredTurns\n}\n\nexport function createAgent({ provider, name: agentName, system: agentSystem, tools: agentTools, toolAliases, behavior: agentBehavior, execution, handle: injectedHandle, mcpServers, session, readState: agentReadState, skills: agentSkills, mcpConnector, mcpToolWrap, eager, hooks: initialHooks, clock: agentClock, toolKill }: 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 // Reset the run-scoped liveness state (running flag, abort controller, idle\n // gate). Funnels the defensive `finally` resets in `run()` through one place\n // so the inner (happy/abort/throw) and outer (setup-threw) cleanup paths\n // can't drift. Idempotent: re-clearing already-cleared state is a no-op,\n // matching the original double-finally semantics. Resolving `idleResolve`\n // wakes any `waitForIdle()` awaiters before dropping the promise.\n function resetRunScope(): void {\n running = false\n abortController = undefined\n idleResolve?.()\n idlePromise = undefined\n idleResolve = undefined\n }\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 // One-shot stall signals from the background watchdog, keyed by taskId.\n // Same drain/suppression lifecycle as `pendingTaskNotifications`; an\n // exit for the same task supersedes its pending stall (the\n // `background:exit` listener deletes the matching entry).\n const pendingStallNotifications = new Map<string, TaskStallInfo>()\n // Task ids the model has already been told about (drained notification,\n // shell_kill result, read of the output file, wait_task return). The\n // pull-based reconcile at run start consults this so re-listing a\n // terminated task on every subsequent run doesn't re-notify. Grows by\n // one small string per background task over the agent's lifetime —\n // bounded; cleared on reset()/destroy().\n const notifiedTaskIds = new Set<string>()\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 registers the controller before\n // gate/execute and unregisters it in a finally, so the live map only\n // ever holds currently dispatching calls. The registry also parks kills\n // that arrive before a call dispatches (the pre-registration race) and\n // applies them at registration. Lives on the agent (not the run scope)\n // so the same instance survives across the loop's per-batch reuses;\n // parked kills are cleared at each run start and on destroy.\n const toolCancels = createToolCancelRegistry(\n toolKill?.defaultMessage !== undefined ? { defaultMessage: toolKill.defaultMessage } : {},\n )\n // A host-injected handle is used verbatim and never spawned/destroyed by the\n // agent (the host owns its lifecycle). `executionReadyFired` gates the\n // one-shot `execution:ready` hook.\n let executionHandle: ExecutionHandle | null = injectedHandle ?? null\n const executionHandleInjected = injectedHandle !== undefined\n let executionReadyFired = false\n let mcpConnection: McpConnection | null = null\n // Snapshot of the last run's assembled prompt pieces (system, wire tools,\n // deferred tools, MCP groups, skills/subagent catalogs) + the model used.\n // Captured in `run()` right after the prompt is finalized; read on demand by\n // `getContextBreakdown()`. `null` until the first run assembles a prompt.\n let lastContextAssembly: ContextAssembly | 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 // Tool-budget 'steer' nudges. Deliberately NOT `steeringQueue`: the batch\n // schedulers skip the remaining sibling tool calls when the steering\n // queue is non-empty mid-batch, and a gate-time budget nudge must never\n // have that effect. Drained by the loop at the turn boundary instead.\n const toolBudgetNudgeQueue: 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 `run_N` id ever observed in EITHER `runs` or `turns[].runId`\n // so the next mint is guaranteed unique within this session.\n //\n // `syncRunCounter()` scans incrementally: it remembers how far it has\n // walked `runs` and `turns` and only visits the appended tail on each\n // re-sync (P4). Append-only semantics make the tail scan sound; a\n // shrunk/replaced array (reset, fork re-seed) falls back to a full\n // rescan from offset 0. The regex matches the canonical `run_<int>`\n // shape minted by this module; caller-supplied custom id schemes are\n // ignored (they don't conflict with `run_N`).\n let runCounter = 0\n let scannedRuns = 0\n let scannedTurns = 0\n const considerRunId = (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 > runCounter)\n runCounter = n\n }\n function syncRunCounter(): void {\n if (!session)\n return\n if (session.runs.length < scannedRuns)\n scannedRuns = 0\n if (session.turns.length < scannedTurns)\n scannedTurns = 0\n for (let i = scannedRuns; i < session.runs.length; i++)\n considerRunId(session.runs[i].id)\n for (let i = scannedTurns; i < session.turns.length; i++)\n considerRunId(session.turns[i].runId)\n scannedRuns = session.runs.length\n scannedTurns = session.turns.length\n }\n syncRunCounter()\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 the prompt/resume preconditions and compute the filtered resume\n // view once (reused below in the seed block). Throws on an unrecoverable\n // resume request; see {@link validateAndPrepareResume}.\n const resumeFilteredTurns = validateAndPrepareResume(session, options.prompt, pendingTaskNotifications.size > 0)\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 // Drop any kills parked before this run began (the pre-registration FIFO).\n // They could only target a previous run's call ids; clearing here keeps a\n // stale kill from landing on a fresh call that happens to reuse an id.\n toolCancels.clearPending()\n try {\n abortController = new AbortController()\n // Create the idle gate BEFORE the first await below. `running` is\n // already true, so a `waitForIdle()` issued in this setup window\n // (session bookkeeping, `agent:start` handlers are all async) must\n // get a promise pinned to THIS run — previously the gate was created\n // much later and `waitForIdle()` returned an already-resolved promise\n // while `isRunning()` reported true.\n idlePromise = new Promise<void>((resolve) => {\n idleResolve = resolve\n })\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 // `syncRunCounter` scans BOTH session.runs and\n // session.turns[].runId (incrementally), so any sibling agent's mint\n // that has either landed in runs[] OR persisted a turn is visible.\n syncRunCounter()\n const runId = `run_${++runCounter}`\n\n const runStartedAt = await clock.now()\n const mintTurnId = async () => session ? await session.generateTurnId() : clock.randomUUID()\n\n // Resolve behavior up-front (was computed lazily right before the loop)\n // so the run-setup + teardown hook bounds, the spawn watchdog, and the\n // loop all read one merged view. Pure field-merge — no side effects.\n const resolvedBehavior = resolveBehavior(agentBehavior, options.behavior)\n // Generous anti-hang bound for every awaited hook in the run lifecycle.\n // A host handler that never resolves would otherwise hang the first\n // turn (run-setup hooks) or strand the `running` gate (teardown hooks).\n const hookTimeoutMs = resolveTimeoutMs(resolvedBehavior.hookTimeoutMs, DEFAULT_HOOK_TIMEOUT_MS)\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 const sessionRun = session?.runs.find(r => r.id === runId)\n if (sessionRun)\n sessionRun.startedAt = runStartedAt\n if (session) {\n await session.updateStatus('running')\n await settleWithinTimeout('hook session:start', hooks.callHook('session:start', { sessionId: session.id, runId, prompt: promptLabel }), hookTimeoutMs)\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 await settleWithinTimeout('hook agent:start', 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 }), hookTimeoutMs)\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(externalSignal.reason)\n }\n else {\n externalAbortListener = () => abortController?.abort(externalSignal.reason)\n externalSignal.addEventListener('abort', externalAbortListener, { once: true })\n }\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. Bounded on the first run: docker / sandbox /\n // e2b / daytona contexts do daemon + network round-trips here with no\n // inherent timeout, so a hung daemon would leave `agent.run()` pending\n // forever (the provider watchdog hasn't started yet). The process\n // context spawns synchronously, so the generous default never bites it.\n const spawnedThisCall = !executionHandle\n if (!executionHandle) {\n const spawnTimeoutMs = resolveTimeoutMs(resolvedBehavior.spawnTimeoutMs, DEFAULT_SPAWN_TIMEOUT_MS, resolvedBehavior.maxWallMs)\n executionHandle = await withTimeout(executionContext.spawn(), {\n operation: 'execution context spawn',\n timeoutMs: spawnTimeoutMs,\n message: `Execution context spawn timed out after ${spawnTimeoutMs}ms (configurable via \\`spawnTimeoutMs\\`).`,\n })\n }\n\n // Surface the bound handle ONCE — the journalable seam for durable hosts\n // that want to record the minted id (or just observe it for telemetry).\n // `spawned` distinguishes an agent-minted handle from a host-injected one.\n if (!executionReadyFired) {\n executionReadyFired = true\n await hooks.callHook('execution:ready', { handle: executionHandle, spawned: spawnedThisCall })\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({ mcpConnectTimeoutMs: resolvedBehavior.mcpConnectTimeoutMs, maxWallMs: resolvedBehavior.maxWallMs })\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 // When the provider clears tool results server-side (Anthropic's\n // context-management beta), the client can't tell when a read's body\n // left the wire — so the read_file dedup would replay its \"unchanged\"\n // stub for content the model can no longer see, stranding it. Force\n // dedup off in that mode; server-side clearing bounds the context\n // without it. Honors an explicit `dedupReads: false` already.\n if ((provider.meta as { clearsContextServerSide?: boolean } | undefined)?.clearsContextServerSide)\n resolvedBehavior.dedupReads = false\n const { maxConcurrentTools, toolBatchExecutor, toolBatchTimeoutMs, shouldRethrowToolError, maxTurns, maxTurnsWarning, maxCostUsd, maxTotalTokens, maxWallMs, providerStreamStartTimeoutMs, providerStreamTimeoutMs, maxTokens, retry, thinkingBudget, modelOptions: behaviorModelOptions, schema, cache, toolOutputBudget, toolOutputBudgetExcludeTools, compactStrategy, compactThreshold, compactKeepTurns, thinkingDecay, dedupTools, toolBudgets, repeatGuard, elideStaleReads, mediaKeepTurns, autoCompact, toolDisclosure, mcpToolNameSeparator, toolSearch, surfaceMcpInstructions, persistThreshold, persistExcludeTools, persistDir, persistMaxBytes, strictToolPairing, maxPairingRepairsPerTurn, maxConsecutivePauseTurns, persistTurns } = resolvedBehavior\n // Per-run options win over the behavior-level default outright (no merge —\n // a run that passes `modelOptions` fully replaces the agent default).\n const modelOptions = options.modelOptions ?? behaviorModelOptions\n\n // System prompt: run-time option > agent default > fallback\n let system = options.system || agentSystem || 'You are a helpful assistant.'\n // Snapshot the host-composed base (doctrine + rules + env) BEFORE the\n // agent appends skills / searchable-tools / MCP-instructions catalogs, so\n // the context breakdown can attribute each appended section separately.\n const baseSystemForBreakdown = renderSystemForWire(system)\n\n // Append skills catalog to the STATIC half of the system prompt. The\n // catalog is built once per run and stays byte-stable for the duration,\n // so it belongs in the cached prefix. When the caller embedded a\n // `SYSTEM_PROMPT_BOUNDARY` marker, the catalog lands above it; without\n // a marker, the append is a plain string concat (current behavior).\n if (skillsCatalog) {\n system = appendStaticSection(system, 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 const generatedMcpToolAliases = buildMcpToolNameSeparatorAliases(mcpToolNames, mcpServers, mcpToolNameSeparator)\n const baseToolAliases = generatedMcpToolAliases\n ? { ...generatedMcpToolAliases, ...(toolAliases ?? {}) }\n : toolAliases\n // Coerce any MCP tool name that violates the provider name constraint\n // (e.g. a server configured as \"SSH Ipseity\" → `mcp_SSH Ipseity_exec`)\n // into a valid wire name. Injected into the alias record so the\n // `tool_search` catalog AND the `tools` array see the same sanitized\n // name — the model never gets an invalid name to echo, and the request\n // never 400s on `tools.N.custom.name`. Sanitized entries win over a\n // host/separator alias only when that alias is itself invalid.\n const sanitizedWireAliases = buildSanitizedWireAliases(mcpToolNames, baseToolAliases)\n const effectiveToolAliases = sanitizedWireAliases\n ? { ...(baseToolAliases ?? {}), ...sanitizedWireAliases }\n : baseToolAliases\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 // Gate for `!`cmd`` shell interpolation in skill bodies. On by\n // default; `allowShellInterpolation: false` neutralizes the\n // patterns with a placeholder. `approveShellInterpolation`, when\n // provided, gates each command through host confirmation.\n allowShellInterpolation: skillsConfig?.allowShellInterpolation,\n approveShellInterpolation: skillsConfig?.approveShellInterpolation,\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), OR\n // - `behavior.disableBackgroundTasks: 'non-durable'` and the\n // execution context's `detachedTasks` capability is below\n // `'durable'` (durable-runtime hosts), OR\n // - the context's capability is `'none'` (no backgrounding\n // at all — schema would advertise a dead flag).\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 detachedTasks = resolveDetachedTasksCapability(executionContext)\n const bgDisabled = resolvedBehavior?.disableBackgroundTasks === true\n || (resolvedBehavior?.disableBackgroundTasks === 'non-durable' && detachedTasks !== 'durable')\n const bgAllowed = typeof resolvedBehavior?.tasksDir === 'string'\n && resolvedBehavior.tasksDir.length > 0\n && !bgDisabled\n && detachedTasks !== 'none'\n toolsPreSearch.shell = createShellTool({\n allowBackground: bgAllowed,\n registeredCanonicals: new Set(Object.keys(toolsPreSearch)),\n ...(effectiveToolAliases ? { toolAliases: effectiveToolAliases } : {}),\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 the effective aliases so lazy entries carry their wire-level\n // display name — what the catalog and `tool_search` results show to\n // the model — while keeping the canonical name on the entry for\n // unlock-set membership and dispatch-map lookup.\n const disclosure = partitionToolDisclosure(toolsPreSearch, mcpToolNames, mcpServers, toolDisclosure, effectiveToolAliases)\n const unlocked = new Set<string>(disclosure.eagerCanonicalNames)\n // Snapshot of the eager seed — never mutated. `buildFormattedTools`\n // walks the registry filtered by this set for the cache-stable prefix\n // (phase 1) and then appends entries from `dynamicUnlockOrder` (phase\n // 2). Splitting the seed from the live `unlocked` set is what lets us\n // emit dynamic unlocks at the END of the wire-level tools array\n // regardless of where they sit in registry insertion order — without\n // it, an MCP tool surfaced by `tool_search` would land at its\n // registry position and shift every subsequent tool's byte offset,\n // invalidating the provider's tool-list prefix cache.\n const initialUnlocked = new Set<string>(disclosure.eagerCanonicalNames)\n const dynamicUnlockOrder: string[] = []\n // Separate membership Set keyed on names already in the dynamic log.\n // `unlocked.has()` is NOT a reliable repeat-check because callers\n // (`tool_search.execute`, `applyToolSearchToUnlocked`) write into\n // `unlocked` BEFORE calling `addUnlock` for back-compat with hosts\n // that only pass a Set — using `unlocked.has()` here would short-\n // circuit every first call and the dynamic log would stay empty.\n const dynamicUnlockSeen = new Set<string>()\n // Centralised unlock recorder. Idempotent on repeats. Used by both\n // the `tool_search` runtime path and the resume-replay path so the\n // log reflects the EFFECTIVE unlock order regardless of source.\n function recordDynamicUnlock(canonical: string): void {\n if (initialUnlocked.has(canonical))\n return\n if (dynamicUnlockSeen.has(canonical))\n return\n dynamicUnlockSeen.add(canonical)\n unlocked.add(canonical)\n dynamicUnlockOrder.push(canonical)\n }\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 addUnlock: recordDynamicUnlock,\n ...(toolSearch?.limit !== undefined ? { defaultLimit: toolSearch.limit } : {}),\n })\n tools = { ...toolsPreSearch, [toolSearchTool.spec.name]: toolSearchTool }\n unlocked.add(toolSearchTool.spec.name)\n // `tool_search` itself is part of the cache-stable prefix — it\n // never gets unlocked dynamically and we want its position to stay\n // fixed at the end of the registry-order block, before any dynamic\n // unlocks tail.\n initialUnlocked.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 ? (effectiveToolAliases?.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. Lands in the STATIC half when a\n // boundary marker is present (so per-turn dynamic content sits below).\n let searchableCatalogText: string | undefined\n if (disclosure.lazyEntries.length > 0) {\n searchableCatalogText = buildSearchableCatalog(disclosure.lazyEntries, { discoveryToolName })\n system = appendStaticSection(system, searchableCatalogText)\n }\n\n // MCP server `instructions` block. Each server returns this field on\n // the `initialize` handshake and the SDK exposes it via\n // `client.getInstructions()`; we render it as `# MCP Server\n // Instructions` here so the model sees the server's own guidance\n // (e.g. \"the database is provisioned, use `apply_migration` directly\")\n // before it commits to a tool call. Without this, OSS models routinely\n // try to bootstrap state the server already advertises as ready.\n //\n // Appended via `appendStaticSection` so the block lands on the\n // CACHED half of the system prompt (above any `SYSTEM_PROMPT_BOUNDARY`\n // marker the host injected for env / cwd / mtimes). Server insertion\n // order in the Map matches `mcpServers` config order, which keeps the\n // rendered text byte-stable across runs and rides the system-prompt\n // cache breakpoint alongside the skills + searchable-tools catalogs.\n if (surfaceMcpInstructions && mcpConnection?.instructions && mcpConnection.instructions.size > 0) {\n const section = renderMcpInstructionsSection(mcpConnection.instructions)\n if (section.length > 0)\n system = appendStaticSection(system, section)\n }\n\n // Build alias maps once per run. Throws on alias collisions.\n const aliasMaps = buildAliasMaps(effectiveToolAliases, Object.keys(tools))\n // Auto-accept the Claude Code `mcp__server__tool` double-underscore\n // naming form as an inbound alias for every MCP tool. Models trained\n // on Anthropic's SDK routinely emit the double form even when the\n // tool catalog advertises single — without this, every such call\n // tripped `tool:unknown` and burnt a turn on a correction round-trip.\n // Inbound-only by design: outbound wire still shows the canonical\n // single form, so tool-list bytes (and the prompt cache) are\n // unaffected. See `augmentMcpDoubleUnderscoreAliases` for the rule.\n augmentMcpDoubleUnderscoreAliases(aliasMaps, 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 // unlock state. Called once up-front for the seed value and again\n // per-iteration in the loop when lazy disclosure may have grown the\n // dynamic-unlock log. Safe to call repeatedly — `provider.formatTools`\n // is a pure mapping.\n //\n // Cache-stable emission: phase 1 walks the registry in insertion\n // order, emitting tools in `initialUnlocked` (every eager native +\n // skill_* + tool_search). This block is byte-stable across the entire\n // run — no element is added or moved, so providers that cache on the\n // tools-array bytes (Anthropic ephemeral cache, OpenRouter passthrough)\n // hit cache turn after turn for the prefix.\n //\n // Phase 2 appends entries from `dynamicUnlockOrder` (tools surfaced\n // by `tool_search` or resume-replay) in the order they were unlocked.\n // The log only grows — entries are never reordered or removed — so\n // every successful `tool_search` invalidates exactly the tool-list\n // suffix from the first new schema onwards, NOT the whole array. The\n // prefix (every tool the model already had) stays cache-hit.\n //\n // The previous implementation iterated `Object.values(tools)` filtered\n // by `unlocked` — that follows registry insertion order, which puts a\n // newly-unlocked tool at its REGISTRY POSITION (somewhere in the\n // middle of the MCP block), shifting every later entry's bytes and\n // invalidating the full tool-list cache on every discovery wave. Two\n // phases fixes that without changing model-observable behavior.\n // Memoize across turns keyed by `dynamicUnlockOrder.length`. The eager\n // prefix is byte-stable for the whole run and the dynamic tail is\n // append-only (never reordered/removed), so the formatted array is fully\n // determined by the tail length. Most turns unlock nothing, so this\n // skips the full registry walk + `provider.formatTools` on the hot path\n // (P3) while still recomputing the entire array — never splicing — when\n // a `tool_search` grows the tail, which keeps providers with non-mapping\n // `formatTools` (e.g. Anthropic's `web_search` rewrite) correct.\n let formattedToolsCache: { tailLen: number, value: unknown[] } | null = null\n function buildFormattedTools(): unknown[] {\n if (formattedToolsCache && formattedToolsCache.tailLen === dynamicUnlockOrder.length)\n return formattedToolsCache.value\n const specs: ToolSpec[] = []\n for (const t of Object.values(tools)) {\n if (!initialUnlocked.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 for (const canonical of dynamicUnlockOrder) {\n const t = tools[canonical]\n if (!t)\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 const value = specs.length > 0 ? provider.formatTools(specs) : []\n formattedToolsCache = { tailLen: dynamicUnlockOrder.length, value }\n return value\n }\n const formattedTools = buildFormattedTools()\n\n // Capture a context snapshot for `getContextBreakdown()`. Split the\n // disclosed wire tools into native vs MCP (by `mcpToolNames`) and group\n // the MCP ones; the deferred (lazy, not-yet-disclosed) entries come from\n // `disclosure.lazyEntries` and carry their own `server`. Each tool is\n // sized by its JSON byte footprint — the same bytes the provider sends.\n {\n // Capture raw tool inputs only — the JSON byte-sizing is deferred to\n // `materializeAssemblyTools` inside `getContextBreakdown()`, so a normal\n // run never pays the per-tool `JSON.stringify` (P1). Disclosed-tool order\n // matches the registry walk filtered by `unlocked`; deferred order\n // matches `disclosure.lazyEntries`.\n const disclosedTools: DisclosedToolInput[] = []\n for (const t of Object.values(tools)) {\n if (!unlocked.has(t.spec.name))\n continue\n const wireName = aliasMaps.aliasByCanonical.get(t.spec.name) ?? t.spec.name\n // Attribute MCP tools to their actual server (canonical names are\n // `mcp_<server>_<tool>`); 'mcp' only as a last-resort fallback for\n // names no configured server matches.\n disclosedTools.push({\n name: wireName,\n description: t.spec.description || '',\n inputSchema: t.spec.inputSchema,\n ...(mcpToolNames.has(t.spec.name)\n ? { mcpServer: resolveMcpToolParts(t.spec.name, mcpServers)?.server ?? 'mcp' }\n : {}),\n })\n }\n\n const deferredEntries: DeferredToolInput[] = disclosure.lazyEntries.map(entry => ({\n name: entry.name,\n description: entry.description || '',\n inputSchema: entry.inputSchema,\n ...(entry.server ? { server: entry.server } : {}),\n }))\n\n const mcpInstructionsText = (surfaceMcpInstructions && mcpConnection?.instructions && mcpConnection.instructions.size > 0)\n ? renderMcpInstructionsSection(mcpConnection.instructions)\n : undefined\n\n const rulesBlock = options.contextSections?.rulesBlock\n const rulesFiles = options.contextSections?.rulesFiles\n\n lastContextAssembly = assembleContextSnapshot({\n modelId: model,\n // Mirror the wire bytes — strip the cache boundary marker.\n system: renderSystemForWire(system),\n baseSystem: baseSystemForBreakdown,\n rulesBlock,\n rulesFiles,\n disclosedTools,\n deferredEntries,\n mcpInstructions: mcpInstructionsText,\n skillsCatalog,\n subagentDefs: searchableCatalogText,\n // Provider-wire tool specs for the exact `countTokens` path.\n wireTools: formattedTools,\n })\n }\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 = childRunIdSet(session)\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 recordDynamicUnlock,\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 // Reconcile + drain pending background-task notifications. Each\n // exit becomes a `<task-notification>` text block prepended to the\n // user turn so the model sees task completions on its very next\n // prompt, without polling. Two sources, merged by taskId:\n //\n // 1. PUSH — the agent-scoped queue populated by `background:exit`\n // since the last `run()` (in-process contexts fire `onExit`).\n // 2. PULL — a `listBackground` sweep for terminated tasks the\n // model hasn't been told about. This is what makes remote /\n // durable contexts work: they have no legal way to push from\n // a timer into the host (journal rules), so the loop polls at\n // the run boundary instead. `endedAt - startedAt` reproduces\n // `durationMs` without depending on sweep timing.\n //\n // The `background:reconcile` hook fires on the merged list INSIDE\n // the run() await chain — the journalable seam for durable hosts\n // (see the hook's doc). Entries suppressed earlier (`shell_kill`,\n // `read_file`, `wait_task`) are absent from both sources via the\n // `notifiedTaskIds` latch. See `docs/RUN_IN_BACKGROUND.md`\n // §Completion notification.\n //\n // Durable hosts wrap the live `listBackground` poll in\n // `options.journalReconcile` so it records as one journaled side effect\n // (replay returns the recorded snapshot, keeping `exits.length` — and\n // therefore the conditional hook's journal position — stable across\n // attempts). Everything downstream of the snapshot is pure over journaled\n // / replayed inputs (session-turn acks, duration derivation), so\n // journaling the poll is enough to make the whole reconcile deterministic.\n const reconciled = new Map<string, TaskExitInfo>(pendingTaskNotifications)\n pendingTaskNotifications.clear()\n if (executionContext.listBackground && executionHandle\n && typeof resolvedBehavior?.tasksDir === 'string' && resolvedBehavior.tasksDir.length > 0) {\n try {\n const listBackground = executionContext.listBackground\n const handle = executionHandle\n const entries = options.journalReconcile\n ? await options.journalReconcile(() => listBackground(handle))\n : await listBackground(handle)\n // Durable dedupe: the in-memory `notifiedTaskIds` latch dies\n // with this agent instance, but durable hosts reconstruct the\n // agent per invocation while the remote registry keeps its\n // terminated entries. The persisted turn history carries the\n // acknowledgments (notification blocks, kills, waits, reads)\n // — re-derive the latch from it so a fresh agent on a resumed\n // session never re-notifies. See {@link collectTaskAcksFromTurns}.\n //\n // Scan the FULL persisted history (`session.turns`), not the\n // runtime-filtered `turns` view: the runtime view drops\n // subagent-run turns (a subagent's `shell_kill` still counts\n // as an ack) and unresolved tool_uses, and the built-in\n // compaction is marker-based (`compact-summary` replaces turns\n // at the WIRE level only — originals stay persisted), so the\n // evidence survives compaction by contract.\n const acks = entries.some(e => e.status !== 'running')\n ? collectTaskAcksFromTurns(session ? session.turns : turns)\n : undefined\n for (const entry of entries) {\n if (entry.status === 'running' || notifiedTaskIds.has(entry.taskId) || reconciled.has(entry.taskId))\n continue\n if (acks) {\n if (acks.taskIds.has(entry.taskId))\n continue\n // A historical read of the output file acks the exit only\n // when it happened after the task ended, with a skew\n // margin because the two timestamps come from different\n // clock domains (see READ_ACK_CLOCK_SKEW_MARGIN_MS).\n // Without `endedAt` we can't order the two — fail open\n // (redundant notification beats false suppression).\n const readAt = acks.outputPaths.get(pathResolve(entry.outputPath))\n if (readAt !== undefined && entry.endedAt !== undefined\n && readAt >= entry.endedAt + READ_ACK_CLOCK_SKEW_MARGIN_MS) {\n continue\n }\n }\n reconciled.set(entry.taskId, {\n taskId: entry.taskId,\n status: entry.status,\n exitCode: entry.exitCode ?? 0,\n ...(entry.signal ? { signal: entry.signal } : {}),\n outputPath: entry.outputPath,\n durationMs: Math.max(0, (entry.endedAt ?? entry.startedAt) - entry.startedAt),\n command: entry.command,\n })\n }\n }\n catch (err) {\n // A failing sweep must not block the run — push-fed entries\n // still drain; the pull side retries next run.\n if (process.env.ZIDANE_DEBUG)\n process.stderr.write(`[zidane/agent] background reconcile failed: ${err instanceof Error ? err.message : String(err)}\\n`)\n }\n }\n const reconcileCtx = { exits: [...reconciled.values()] }\n if (reconcileCtx.exits.length > 0)\n await hooks.callHook('background:reconcile', reconcileCtx)\n // Latch on the PRE-hook set: a handler that filters an exit out is\n // deciding the model never sees it — without the latch the pull\n // sweep would resurface the same exit on every subsequent run.\n for (const taskId of reconciled.keys()) {\n notifiedTaskIds.add(taskId)\n // An exit supersedes any still-pending stall for the same task.\n pendingStallNotifications.delete(taskId)\n }\n\n const drainedNotifications: SessionContentBlock[] = []\n for (const notif of reconcileCtx.exits) {\n drainedNotifications.push({\n type: 'text',\n text: renderTaskNotificationXml(notif),\n })\n }\n if (pendingStallNotifications.size > 0) {\n for (const stall of pendingStallNotifications.values()) {\n drainedNotifications.push({\n type: 'text',\n text: renderTaskStallXml(stall),\n })\n }\n pendingStallNotifications.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: await mintTurnId(),\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 // `persistTurns: false` drops the incremental per-turn append hooks; the\n // run-end `flushTurns()` (every exit path) still persists the whole run\n // in one append. Trades mid-run crash granularity for O(1) store writes\n // per run. The store stays attached — resume / `session.save()` unchanged.\n const perTurnSync = session && persistTurns !== false\n const unregisterTurnSync = perTurnSync ? hooks.hook('turn:after', persistPendingTurns) : undefined\n const unregisterToolResultsSync = perTurnSync ? 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 mintTurnId()\n await synthesizeMissingToolResults(turns, turnId, runId, provider, hooks, clock)\n }\n const remaining = turns.slice(lastPersistedTurnCount)\n if (remaining.length > 0) {\n // Bounded so a hung custom session store can't strand the run-scope\n // reset that runs after this in the exit paths / finally.\n await settleWithinTimeout('session.appendTurns', session.appendTurns(remaining), hookTimeoutMs)\n lastPersistedTurnCount = turns.length\n await settleWithinTimeout('hook session:turns', hooks.callHook('session:turns', { sessionId: session.id, turns: remaining, count: turns.length }), hookTimeoutMs)\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 //\n // Runs in the inner `finally`, RIGHT before `resetRunScope()` clears the\n // `running` gate — so it must neither hang nor throw, or the gate strands\n // (`agent.run()` then throws \"already running\" forever and TUI/GUI hosts\n // enqueue submits silently). Each `skills:deactivate` is bounded AND its\n // throw is caught so cleanup always reaches the reset. (#2)\n async function deactivateAllSkills() {\n for (const record of skillActivationState.clear()) {\n try {\n await settleWithinTimeout('hook skills:deactivate', hooks.callHook('skills:deactivate', { skill: record.skill, reason: 'run-end' }), hookTimeoutMs)\n }\n catch (err) {\n if (process.env.ZIDANE_DEBUG)\n console.error('[zidane] skills:deactivate handler threw during run-end cleanup:', err)\n }\n }\n }\n\n // Finalize session: persist run status and fire session:end. Each await\n // is bounded so a hung store / `session:end` handler can't block the\n // run-scope reset that follows in the exit paths. (#2)\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 settleWithinTimeout('session.updateRun', session.updateRun(run), hookTimeoutMs)\n await settleWithinTimeout('session.updateStatus', session.updateStatus(status === 'aborted' ? 'idle' : status), hookTimeoutMs)\n await settleWithinTimeout('hook session:end', hooks.callHook('session:end', { sessionId: session.id, runId, status, turnRange: [runTurnStart, turns.length - 1] }), hookTimeoutMs)\n }\n\n async function stampSessionRunEnd() {\n const run = session?.runs.find(r => r.id === runId)\n if (run)\n run.endedAt = await clock.now()\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 => toolBudgetNudgeQueue.push(msg),\n )\n\n // Consecutive-identical repeat guard. Installed AFTER budgets (a budget\n // block wins) and BEFORE dedup (so a streak block beats a dedup replay\n // short-circuit — otherwise a model spamming a dedup-eligible payload\n // would replay forever and never trip the abort). At the abort threshold\n // the gate calls `abortController.abort()`; the loop's existing post-turn\n // `signal.aborted` check then fires `agent:abort` and ends the run — the\n // same path a user-driven cancel takes.\n const uninstallRepeatGuard = installRepeatGuard(\n hooks,\n () => repeatGuard,\n () => abortController?.abort(),\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 = runStartedAt\n\n const runDepth = typeof options.depth === 'number' ? options.depth : 0\n\n // Live mirror of the loop's run-cumulative usage. `runLoop` returns\n // stats only on clean exits — when it THROWS (mid-stream abort,\n // budget breach) the catch paths below read this mirror so\n // `agent:done` / `session.abortRun` report the tokens actually\n // burned instead of all-zeros. Wall-clock start is captured\n // separately from `runStartedAt` (which may come from a journaled /\n // mock clock) so `elapsed` stays a real-duration measurement,\n // matching `runLoop`'s own `Date.now()`-based accounting.\n const usageMirror: RunUsageMirror = {\n totalIn: 0,\n totalOut: 0,\n totalCacheRead: 0,\n totalCacheCreation: 0,\n turns: 0,\n cost: 0,\n turnUsage: [],\n }\n const wallStartMs = Date.now()\n\n // Normalize loop-native auto-compaction once per run. Window source:\n // explicit `autoCompact.contextWindow` → `provider.meta.contextWindow`\n // fallback (chat hosts inject the picked model's window via the former).\n // `undefined` when disabled, so the LoopContext field stays absent.\n const providerContextWindow = typeof provider.meta.contextWindow === 'number'\n ? provider.meta.contextWindow\n : undefined\n const resolvedAutoCompact = resolveAutoCompact(autoCompact, {\n ...(providerContextWindow !== undefined ? { fallbackContextWindow: providerContextWindow } : {}),\n ...(compactKeepTurns !== undefined ? { compactKeepTurns } : {}),\n })\n\n try {\n const stats = await runLoop({\n provider,\n hooks,\n agentName,\n agentSystem,\n agentTools: sourceTools,\n agentToolAliases: effectiveToolAliases,\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 ...(toolBatchExecutor !== undefined ? { toolBatchExecutor } : {}),\n ...(toolBatchTimeoutMs !== undefined ? { toolBatchTimeoutMs } : {}),\n ...(shouldRethrowToolError !== undefined ? { shouldRethrowToolError } : {}),\n signal: abortController.signal,\n execution: executionContext,\n handle: executionHandle!,\n steeringQueue,\n toolBudgetNudgeQueue,\n followUpQueue,\n turns,\n runId,\n generateTurnId: mintTurnId,\n clock,\n maxTurns,\n ...(maxTurnsWarning !== undefined ? { maxTurnsWarning } : {}),\n ...(maxCostUsd !== undefined ? { maxCostUsd } : {}),\n ...(maxTotalTokens !== undefined ? { maxTotalTokens } : {}),\n ...(maxWallMs !== undefined ? { maxWallMs } : {}),\n ...(providerStreamStartTimeoutMs !== undefined ? { providerStreamStartTimeoutMs } : {}),\n ...(providerStreamTimeoutMs !== undefined ? { providerStreamTimeoutMs } : {}),\n ...(resolvedBehavior.hookTimeoutMs !== undefined ? { hookTimeoutMs: resolvedBehavior.hookTimeoutMs } : {}),\n ...(retry !== undefined ? { retry } : {}),\n maxTokens,\n ...(session ? { session } : {}),\n ...(agentReadState ? { readState: agentReadState } : {}),\n ...(options.parentRunId ? { parentRunId: options.parentRunId } : {}),\n depth: runDepth,\n thinkingBudget,\n ...(modelOptions ? { modelOptions } : {}),\n schema,\n cache,\n toolOutputBudget,\n ...(toolOutputBudgetExcludeTools !== undefined ? { toolOutputBudgetExcludeTools } : {}),\n compactStrategy,\n compactThreshold,\n compactKeepTurns,\n ...(elideStaleReads !== undefined ? { elideStaleReads } : {}),\n ...(mediaKeepTurns !== undefined ? { mediaKeepTurns } : {}),\n ...(resolvedAutoCompact ? { autoCompact: resolvedAutoCompact } : {}),\n // Persist a mid-run compaction marker promptly (+ fire session:turns\n // so hosts repaint) — only when per-turn sync is on; batch mode lets\n // the run-end flush cover it.\n ...(perTurnSync ? { persistTurns: persistPendingTurns } : {}),\n // Active-skill snapshot for post-compact restoration.\n getActiveSkills: () => skillActivationState.active(),\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 ...(maxPairingRepairsPerTurn !== undefined ? { maxPairingRepairsPerTurn } : {}),\n ...(maxConsecutivePauseTurns !== undefined ? { maxConsecutivePauseTurns } : {}),\n providerName: provider.name,\n runStartMs,\n runToolCounts: {},\n toolCancels,\n usageMirror,\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 stampSessionRunEnd()\n await finalizeSession('aborted')\n await settleWithinTimeout('hook agent:done', hooks.callHook('agent:done', finalStats), hookTimeoutMs)\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 stampSessionRunEnd()\n await finalizeSession('completed')\n\n await settleWithinTimeout('hook agent:done', hooks.callHook('agent:done', finalStats), hookTimeoutMs)\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. Fire\n // `agent:abort` here: a cancel that lands MID-STREAM throws straight\n // out of `executeTurn` and skips the loop's between-turn\n // `signal.aborted` checks (the only place the loop fires it), so\n // without this the most common interactive cancel would never reach an\n // `agent:abort` consumer. The clean-loop-break abort path (above) is\n // already covered by the loop's fire and must NOT fire again here —\n // these two branches are mutually exclusive (throw vs clean return), so\n // there's no double-fire.\n if (abortController.signal.aborted) {\n const abortReason = abortController.signal.reason\n await hooks.callHook('agent:abort', {\n ...(runId ? { runId } : {}),\n ...(typeof abortReason === 'string' && abortReason.length > 0 ? { reason: abortReason } : {}),\n })\n // The mirror carries the usage of every COMPLETED turn before the\n // abort landed — pass it through so the persisted run record and\n // `agent:done` reflect real spend, matching the clean-break abort\n // path above (which has the loop's returned `stats` in hand).\n session?.abortRun(runId, {\n turns: usageMirror.turns,\n tokensIn: usageMirror.totalIn,\n tokensOut: usageMirror.totalOut,\n turnUsage: usageMirror.turnUsage,\n cost: usageMirror.cost > 0 ? usageMirror.cost : undefined,\n })\n await stampSessionRunEnd()\n await finalizeSession('aborted')\n const stats: AgentStats = {\n totalIn: usageMirror.totalIn,\n totalOut: usageMirror.totalOut,\n totalCacheRead: usageMirror.totalCacheRead,\n totalCacheCreation: usageMirror.totalCacheCreation,\n turns: usageMirror.turns,\n elapsed: Date.now() - wallStartMs,\n turnUsage: usageMirror.turnUsage,\n ...(usageMirror.cost > 0 ? { cost: usageMirror.cost } : {}),\n }\n await settleWithinTimeout('hook agent:done', hooks.callHook('agent:done', stats), hookTimeoutMs)\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 // breaching spend comes from `usageMirror` — the loop updates it\n // after every completed turn (the budget predicate fires\n // post-turn), so the persisted record and `agent:done` carry the\n // tokens / cost that actually tripped the cap.\n if (err instanceof AgentBudgetExceededError) {\n session?.abortRun(runId, {\n turns: usageMirror.turns,\n tokensIn: usageMirror.totalIn,\n tokensOut: usageMirror.totalOut,\n turnUsage: usageMirror.turnUsage,\n cost: usageMirror.cost > 0 ? usageMirror.cost : undefined,\n })\n await stampSessionRunEnd()\n await finalizeSession('aborted')\n await settleWithinTimeout('hook agent:done', hooks.callHook('agent:done', {\n totalIn: usageMirror.totalIn,\n totalOut: usageMirror.totalOut,\n totalCacheRead: usageMirror.totalCacheRead,\n totalCacheCreation: usageMirror.totalCacheCreation,\n turns: usageMirror.turns,\n elapsed: Date.now() - wallStartMs,\n turnUsage: usageMirror.turnUsage,\n ...(usageMirror.cost > 0 ? { cost: usageMirror.cost } : {}),\n }), hookTimeoutMs)\n throw err\n }\n\n session?.errorRun(runId, errorMessage(err))\n await stampSessionRunEnd()\n await finalizeSession('error')\n // Fire `agent:done` on the error path too — symmetric with the abort /\n // budget branches above and the documented \"all exit paths\" contract.\n // Without it a hard failure (provider 402, network tear-down, …) skips\n // every run-boundary consumer: the terminal session summary (incl. the\n // session id) never prints, metrics' `runs.active` gauge never\n // decrements, and tracing run spans leak. Stats come from `usageMirror`\n // (real spend up to the throw); the re-throw still surfaces the error.\n await settleWithinTimeout('hook agent:done', hooks.callHook('agent:done', {\n totalIn: usageMirror.totalIn,\n totalOut: usageMirror.totalOut,\n totalCacheRead: usageMirror.totalCacheRead,\n totalCacheCreation: usageMirror.totalCacheCreation,\n turns: usageMirror.turns,\n elapsed: Date.now() - wallStartMs,\n turnUsage: usageMirror.turnUsage,\n ...(usageMirror.cost > 0 ? { cost: usageMirror.cost } : {}),\n }), hookTimeoutMs)\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 uninstallRepeatGuard()\n uninstallToolBudgets()\n uninstallLazyDisclosureGate()\n\n unregisterSpawnHook()\n unregisterTurnSync?.()\n unregisterToolResultsSync?.()\n for (const unregister of perRunUnregisters)\n unregister()\n steeringQueue.length = 0\n toolBudgetNudgeQueue.length = 0\n followUpQueue.length = 0\n resetRunScope()\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-clearing already-cleared state is a no-op, and\n // `removeEventListener` of a never-registered listener is also a no-op.\n resetRunScope()\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 return toolCancels.cancel(callId, reason)\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): boolean {\n // Refuse while idle: the queue is only drained inside a live run and is\n // cleared in `run()`'s finally — but only at the END of a run. A message\n // queued BETWEEN runs would silently survive into the next `run()` and,\n // because the batch scheduler treats a populated steering queue as\n // \"superseded by user message\", skip the entire first turn's tool calls.\n // Callers wanting to influence the next run should put the content in\n // its prompt instead.\n if (!running)\n return false\n // Refuse once aborted: the loop checks `signal.aborted` before it drains\n // either queue, so a post-abort enqueue would never be delivered anyway —\n // returning false makes that explicit and stops a Stop-hook host from\n // leaking a message into a dead run.\n if (abortController?.signal.aborted)\n return false\n steeringQueue.push(message)\n return true\n }\n\n function followUpFn(message: string): boolean {\n if (abortController?.signal.aborted)\n return false\n followUpQueue.push(message)\n return true\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 toolBudgetNudgeQueue.length = 0\n followUpQueue.length = 0\n // Drop the cached context-breakdown snapshot — it describes the prior\n // session's system/tools/MCP and would otherwise be served stale (the\n // `?? buildPreRunAssembly()` fallback only fires when this is null).\n lastContextAssembly = null\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 //\n // `notifiedTaskIds` is deliberately NOT cleared: terminated tasks\n // stay in the execution context's registry until the context is\n // destroyed, so wiping the latch here would let the next run's\n // pull-based reconcile resurrect notifications for exits the model\n // (or a prior conversation) was already told about. Running tasks\n // are unaffected — their eventual exits aren't latched yet and\n // notify normally. The latch is cleared in `destroy()`, where the\n // registry it mirrors is torn down too.\n pendingTaskNotifications.clear()\n pendingStallNotifications.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 // The exit supersedes a pending \"no output for a while\" signal —\n // showing both would read as contradictory state to the model.\n pendingStallNotifications.delete(ctx.taskId)\n })\n hooks.hook('background:stall', (ctx) => {\n pendingStallNotifications.set(ctx.taskId, {\n taskId: ctx.taskId,\n command: ctx.command,\n outputPath: ctx.outputPath,\n stalledForMs: ctx.stalledForMs,\n bytesWritten: ctx.bytesWritten,\n })\n })\n // Suppression marks the task as notified IN ADDITION to deleting the\n // pending entry — the pull-based reconcile at the next run start would\n // otherwise re-discover the terminated task and re-notify.\n const suppressTaskNotification = (taskId: string): void => {\n pendingTaskNotifications.delete(taskId)\n pendingStallNotifications.delete(taskId)\n notifiedTaskIds.add(taskId)\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 suppressTaskNotification(taskId)\n return\n }\n if (ctx.name === 'wait_task') {\n // Only a wait that RETURNED exit info means the model knows the\n // outcome. Timeouts leave the task running — its eventual exit is\n // still news, so the pending entry must survive.\n const taskId = ctx.input?.task_id\n const timedOut = typeof ctx.result === 'string' && ctx.result.startsWith(WAIT_TASK_TIMED_OUT_PREFIX)\n if (typeof taskId === 'string' && !timedOut && typeof ctx.result === 'string' && ctx.result.startsWith('Task '))\n suppressTaskNotification(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 suppressTaskNotification(notif.taskId)\n return\n }\n }\n // Reading a STALLED (still-running) task's log clears the stall\n // signal only — no `notifiedTaskIds` latch, the task's eventual\n // exit is still news the model must receive.\n for (const stall of pendingStallNotifications.values()) {\n if (pathResolve(stall.outputPath) === requested) {\n pendingStallNotifications.delete(stall.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(options: { mcpConnectTimeoutMs?: number, maxWallMs?: number } = {}): Promise<void> {\n if (mcpConnection || allMcpServers.length === 0)\n return\n if (mcpWarmupPromise)\n return mcpWarmupPromise\n\n mcpWarmupPromise = (async () => {\n // Outer connect watchdog. The custom connector has no internal bound;\n // the built-in `connectMcpServers` bounds each server's bootstrap but\n // lacked an aggregate guard (B3) — a hang in discovery aggregation / a\n // bootstrap that ignores its own timeout would leave the first run\n // pending forever. Both paths share this generous bound + late-close.\n const timeoutMs = resolveTimeoutMs(\n options.mcpConnectTimeoutMs ?? agentBehavior?.mcpConnectTimeoutMs,\n DEFAULT_CUSTOM_MCP_CONNECT_TIMEOUT_MS,\n options.maxWallMs,\n )\n let timedOut = false\n const pending = mcpConnector\n ? mcpConnector(allMcpServers, hooks)\n : connectMcpServers(allMcpServers, undefined, hooks)\n // A connection that resolves after we stopped awaiting it (timeout) or\n // after destroy() must be closed, not leaked.\n pending.then((lateConnection) => {\n if (timedOut || destroyed)\n void lateConnection.close().catch(() => {})\n }, () => {})\n const connection = await withTimeout(pending, {\n operation: mcpConnector ? 'custom MCP connector' : 'MCP connect',\n timeoutMs,\n onTimeout: () => { timedOut = true },\n message: mcpConnector\n ? `Custom MCP connector timed out after ${timeoutMs}ms.`\n : `MCP server connection timed out after ${timeoutMs}ms (configurable via \\`mcpConnectTimeoutMs\\`).`,\n })\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 // Post-discovery wrap (e.g. durable journaling). Applied here so it\n // covers BOTH the default and a custom `mcpConnector`, and every\n // discovered tool — eager or lazily-connected. Only `tools` is\n // re-shaped; `instructions` / `close` are shared with `connection`. A\n // throwing wrap (consumer code) must close the open connection, not leak it.\n try {\n mcpConnection = mcpToolWrap\n ? { ...connection, tools: wrapDiscoveredMcpTools(connection.tools, mcpToolWrap, allMcpServers) }\n : connection\n }\n catch (err) {\n await connection.close().catch(() => {})\n throw err\n }\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(options: { mcpConnectTimeoutMs?: number, maxWallMs?: number } = {}): 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(options), 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 pendingStallNotifications.clear()\n\n // Run-level abort FIRST: if destroy races a live run, flip the run's\n // own signal so the in-flight LLM stream and the loop start unwinding —\n // the per-tool cancels below only stop tool bodies, not the streaming\n // request (which listens on the run signal). No-op when idle\n // (`abortController` is undefined between runs) or already aborted.\n //\n // Do NOT `await waitForIdle()` here: destroy can be called from a hook\n // during the run. Awaiting idle from that hook would deadlock (the run is\n // waiting for the hook to return while destroy is waiting for the run).\n if (abortController && !abortController.signal.aborted)\n abortController.abort('agent-destroyed')\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. `abortAll` also drops any parked pre-registration kills.\n toolCancels.abortAll('agent-destroyed')\n\n // Each teardown await below is bounded by `destroyTimeoutMs` (B4): hosts\n // await `destroy()` BEFORE releasing their own run gate (TUI session\n // switch), so a custom connector / sandbox whose `close()` never settles\n // would strand that gate and block session switching. A timed-out step is\n // abandoned (logged under ZIDANE_DEBUG) and teardown continues.\n const destroyTimeoutMs = resolveTimeoutMs(agentBehavior?.destroyTimeoutMs, DEFAULT_DESTROY_TIMEOUT_MS)\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 withTimeout(mcpWarmupPromise, { operation: 'destroy: mcp warmup', timeoutMs: destroyTimeoutMs })\n }\n catch {\n // Bootstrap failure / timeout already surfaced to the warmup caller;\n // destroy 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 settleWithinTimeout('destroy: mcp close', mcpConnection.close(), destroyTimeoutMs)\n mcpConnection = null\n }\n if (executionHandle && !executionHandleInjected) {\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 //\n // Skipped for a host-injected handle: the host owns that lifecycle\n // (it drove the spawn and re-attaches across invocations), so the\n // agent must not tear down a sandbox/container the host still needs.\n await settleWithinTimeout('destroy: execution destroy', executionContext.destroy(executionHandle), destroyTimeoutMs)\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 pendingStallNotifications.clear()\n notifiedTaskIds.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 // Drop the cached context-breakdown snapshot so a post-destroy\n // `getContextBreakdown()` can't serve stale data (it's also `destroyed`-\n // guarded below as a belt-and-suspenders).\n lastContextAssembly = null\n }\n\n /**\n * Last parent-run `TurnUsage` from the in-memory conversation — mirrors\n * `lastContextSizeFromTurns` (chat/store) but reads the live agent turns and\n * returns the full usage so callers can break out cache read/write/input.\n * Skips subagent (depth > 0) and zero-usage turns.\n */\n function lastTurnUsage(): TurnUsage | null {\n const childRunIds = childRunIdSet(session)\n for (let i = conversationTurns.length - 1; i >= 0; i--) {\n const turn = conversationTurns[i]\n if (turn.role !== 'assistant' || !turn.usage)\n continue\n if (turn.runId && childRunIds.has(turn.runId))\n continue\n const size = effectiveInputFromTurn(turn.usage)\n if (size === 0)\n continue\n return turn.usage\n }\n return null\n }\n\n /**\n * Best-effort context assembly BEFORE the first run, so the breakdown is\n * available the moment the chat screen opens (no \"send a message first\").\n *\n * Resolves skills + MCP via `warmup()`, then assembles the same static pieces\n * `run()` captures — system prompt (with skills catalog + MCP instructions),\n * the wire tool specs, and MCP groupings. The disclosure subtleties don't\n * matter pre-run (nothing has been unlocked yet); we treat every native +\n * MCP tool as disclosed, matching the eager seed of a fresh run.\n */\n async function buildPreRunAssembly(modelOverride?: string): Promise<ContextAssembly | null> {\n if (destroyed)\n return null\n // Resolve SKILLS only (local, cheap) — deliberately do NOT call `warmup()`,\n // which would connect MCP servers (spawn stdio processes, possibly trigger\n // OAuth) just to render a read-only popover. MCP tools surface in the\n // breakdown once a real run (or an explicit `warmup()`) has connected them;\n // until then we reflect whatever `mcpConnection` already holds (often null).\n try {\n await ensureSkillsResolved()\n }\n catch {\n // Skills resolution failed — fall through with whatever resolved.\n }\n\n const model = modelOverride ?? provider.meta.defaultModel\n const base = agentSystem || 'You are a helpful assistant.'\n const baseSystem = renderSystemForWire(base)\n let system = base\n if (skillsCatalog)\n system = appendStaticSection(system, skillsCatalog)\n\n const baseTools = mcpConnection ? { ...sourceTools, ...mcpConnection.tools } : sourceTools\n const mcpNames = new Set(mcpConnection ? Object.keys(mcpConnection.tools) : [])\n\n // Mirror run()'s wire-name pipeline so the pre-run estimate matches what a\n // real turn sends: separator aliases + host toolAliases + the invalid-name\n // sanitizer (which keeps e.g. `mcp_SSH Ipseity_exec` as a valid wire name\n // instead of letting the provider-level drop guard discard it). Canonical\n // names still drive server attribution; only the emitted `name` is wire.\n const preRunSeparatorAliases = buildMcpToolNameSeparatorAliases(mcpNames, mcpServers, agentBehavior?.mcpToolNameSeparator ?? '_')\n const preRunBaseAliases = preRunSeparatorAliases\n ? { ...preRunSeparatorAliases, ...(toolAliases ?? {}) }\n : toolAliases\n const preRunSanitizedAliases = buildSanitizedWireAliases(mcpNames, preRunBaseAliases)\n const preRunAliases = preRunSanitizedAliases\n ? { ...(preRunBaseAliases ?? {}), ...preRunSanitizedAliases }\n : preRunBaseAliases\n const wireNameFor = (canonical: string): string => preRunAliases?.[canonical] ?? canonical\n\n // Pre-run treats every native + MCP tool as disclosed (nothing unlocked\n // yet). Native tools first, then MCP — matching the wire order below.\n const disclosedTools: DisclosedToolInput[] = []\n const nativeSpecs: ToolSpec[] = []\n const mcpSpecs: ToolSpec[] = []\n const mcpServerByIndex: string[] = []\n for (const t of Object.values(baseTools)) {\n const canonical = t.spec.name\n const spec: ToolSpec = { name: wireNameFor(canonical), description: t.spec.description || '', inputSchema: t.spec.inputSchema }\n if (mcpNames.has(canonical)) {\n mcpSpecs.push(spec)\n // Attribution parses the CANONICAL name (`mcp_<server>_<tool>`); the\n // sanitized wire name would no longer match a configured server prefix.\n mcpServerByIndex.push(resolveMcpToolParts(canonical, mcpServers)?.server ?? 'mcp')\n }\n else {\n nativeSpecs.push(spec)\n }\n }\n for (const spec of nativeSpecs)\n disclosedTools.push({ name: spec.name, description: spec.description || '', inputSchema: spec.inputSchema })\n for (let i = 0; i < mcpSpecs.length; i++) {\n const spec = mcpSpecs[i]!\n disclosedTools.push({ name: spec.name, description: spec.description || '', inputSchema: spec.inputSchema, mcpServer: mcpServerByIndex[i]! })\n }\n\n const wireTools = (nativeSpecs.length + mcpSpecs.length) > 0\n ? provider.formatTools([...nativeSpecs, ...mcpSpecs])\n : []\n\n const mcpInstructionsText = (mcpConnection?.instructions && mcpConnection.instructions.size > 0)\n ? renderMcpInstructionsSection(mcpConnection.instructions)\n : undefined\n if (mcpInstructionsText)\n system = appendStaticSection(system, mcpInstructionsText)\n\n return assembleContextSnapshot({\n modelId: model,\n system: renderSystemForWire(system),\n baseSystem,\n disclosedTools,\n deferredEntries: [],\n mcpInstructions: mcpInstructionsText,\n skillsCatalog,\n wireTools,\n })\n }\n\n async function getContextBreakdown(opts?: ContextBreakdownOptions): Promise<ContextBreakdown | null> {\n // Post-destroy: nothing to report, and `countTokens` would run against a\n // torn-down provider. Matches `buildPreRunAssembly`'s `destroyed` guard.\n if (destroyed)\n return null\n // Use the live run snapshot when present; otherwise assemble a pre-run view\n // so `ctrl+g` works the moment the chat screen opens.\n const assembly = lastContextAssembly ?? await buildPreRunAssembly(opts?.model)\n if (!assembly)\n return null\n\n const usage = lastTurnUsage()\n const used = effectiveInputFromTurn(usage)\n const effectiveWindow = opts?.effectiveWindow ?? used\n // When an auto-compact threshold is set, the buffer is the whole tail from\n // the threshold to the end of the window (so the bar/row show where\n // compaction fires); otherwise it's the reserved-output tokens.\n const autocompactBuffer\n = opts?.compactThreshold !== undefined && opts.compactThreshold > 0 && opts.compactThreshold < 1\n ? Math.max(0, Math.round((1 - opts.compactThreshold) * effectiveWindow))\n : opts?.autocompactBuffer ?? OUTPUT_RESERVE_TOKENS\n\n // Exact path — provider counts (system-only, system+tools). Best effort:\n // any failure leaves `exact` undefined and the heuristic fills the buckets.\n let exact: ContextExactCounts | undefined\n if (typeof provider.countTokens === 'function') {\n // Pass the provider-wire tool specs (already through `formatTools`) — the\n // count endpoint validates the real wire shape, so the internal `ToolSpec`\n // form would be rejected.\n //\n // count_tokens requires at least one message, so every probe carries a\n // constant 1-token dummy user message. Differencing cancels the dummy's\n // fixed overhead from every derived bucket:\n // system = count(sys + dummy) − count(BASELINE + dummy)\n // tools = count(sys + tools + dummy) − count(sys + dummy)\n //\n // The baseline uses a 1-char placeholder system (`BASELINE`) rather than\n // an empty string so that provider-specific system scaffolding stays\n // symmetric across the two probes — notably the Anthropic OAuth path,\n // which injects a synthetic user/assistant pair ONLY when the system is\n // non-empty. An empty baseline would skip that scaffolding and inflate the\n // system bucket; a non-empty baseline keeps it on both sides so it cancels.\n const BASELINE = '.'\n const dummy: SessionMessage[] = [{ role: 'user', content: [{ type: 'text', text: '.' }] }]\n const count = (system: string, tools: unknown[]) =>\n provider.countTokens!({ model: assembly.modelId, system, tools, messages: dummy }, opts?.signal)\n try {\n const [base, sysProbe, sysToolsProbe] = await Promise.all([\n count(BASELINE, []),\n count(assembly.system, []),\n count(assembly.system, assembly.wireTools),\n ])\n // Partial success is fine: a resolved `null` from one probe (the\n // documented \"unavailable\" return) just leaves that field off `exact`,\n // and the builder falls back to the heuristic for it. A thrown error\n // (rejection) is all-or-nothing via `Promise.all` → `catch` below.\n if (base !== null && sysProbe !== null) {\n exact = {\n system: Math.max(0, sysProbe - base),\n ...(sysToolsProbe !== null\n ? { systemAndTools: Math.max(0, sysToolsProbe - base) }\n : {}),\n }\n }\n }\n catch {\n exact = undefined\n }\n }\n\n const materializedTools = materializeAssemblyTools(assembly)\n return buildContextBreakdown({\n modelId: assembly.modelId,\n system: assembly.system,\n baseSystem: assembly.baseSystem,\n ...(assembly.rulesBlock ? { rulesBlock: assembly.rulesBlock } : {}),\n ...(assembly.rulesFiles?.length ? { rulesFiles: assembly.rulesFiles } : {}),\n toolsJson: materializedTools.toolsJson,\n deferredToolsJson: materializedTools.deferredToolsJson,\n mcpGroups: materializedTools.mcpGroups,\n deferredMcpGroups: materializedTools.deferredMcpGroups,\n ...(assembly.mcpInstructions ? { mcpInstructions: assembly.mcpInstructions } : {}),\n ...(assembly.skillsCatalog ? { skillsCatalog: assembly.skillsCatalog } : {}),\n ...(assembly.subagentDefs ? { subagentDefs: assembly.subagentDefs } : {}),\n used,\n effectiveWindow,\n autocompactBuffer,\n ...(exact ? { exact } : {}),\n ...(usage\n ? {\n usage: {\n input: usage.input ?? 0,\n cacheRead: usage.cacheRead ?? 0,\n cacheCreation: usage.cacheCreation ?? 0,\n output: usage.output ?? 0,\n },\n }\n : {}),\n ...(skillActivationState.active().length > 0\n ? { activeSkills: skillActivationState.active().map(s => s.skill.name) }\n : {}),\n })\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 get hasPendingTaskNotifications() { return pendingTaskNotifications.size > 0 },\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 getContextBreakdown,\n // TC39 explicit-resource-management hook. `destroy` is already\n // idempotent + async, so aliasing it here is sufficient — no extra\n // wrapping needed.\n [Symbol.asyncDispose]: destroy,\n }\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, ListFilesEntry } from '../contexts'\n\nfunction entryPath(entry: ListFilesEntry): string {\n return typeof entry === 'string' ? entry : entry.path\n}\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: ListFilesEntry[]\n try {\n entries = await execution.listFiles(handle, dir)\n }\n catch {\n return null\n }\n\n for (const item of entries) {\n const entry = entryPath(item)\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 { describeVia, resolveOldString, stripLineNumberPrefixes, styleReplacementForVia } from './edit-utils'\nimport { suggestionFor } from './path-suggest'\nimport { hashContent, readStateKey, resolveReadStateMap } from './read-state'\nimport { pathArgError } from './validation'\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: 'File path (relative to the execution-context cwd, or absolute).' },\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 const pathErr = pathArgError(target)\n if (pathErr) {\n ctx.reportOutcome?.('failed')\n return `Edit error: ${pathErr}`\n }\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 ctx.reportOutcome?.('failed')\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 ctx.reportOutcome?.('failed')\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 ctx.reportOutcome?.('noop')\n return `Edit error: replacement produced no change in ${target}.`\n }\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 ctx.reportOutcome?.('edited')\n // Flag non-exact recoveries so a mistargeted fallback is visible in the\n // transcript instead of silently passing as a verbatim match.\n const viaNote = via === 'exact' ? '' : ` (old_string was not found verbatim; matched via ${describeVia(via)})`\n return `Edited ${target}: replaced ${occurrences} occurrence${occurrences === 1 ? '' : 's'}.${viaNote}`\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 { ListFileMetadata, ListFilesEntry } from '../contexts'\nimport type { ToolContext, ToolDef } from './types'\nimport { errorMessage } from '../errors'\n\n/**\n * Glob-pattern file matching.\n *\n * Delegates matching to {@link ExecutionContext.listFiles} with `{ glob:\n * true }`. This keeps filesystem semantics inside the execution context\n * (process / docker / sandbox) instead of reaching around it from the tool.\n *\n * Results are capped at 1000 entries to keep model input bounded. The\n * cap applies after the directory filter, so dirs don't consume the\n * file budget.\n *\n * Patterns are deliberately NOT confined to the workspace: like\n * `read_file`, `../`-prefixed patterns may match outside the cwd — the\n * execution context is the sandbox boundary, not this tool.\n *\n * By default, contexts that can cheaply provide metadata return\n * `<path>\\t<size>\\t<mtime>` rows. Contexts without cheap metadata support return\n * paths only. Pass `metadata: false` to request plain newline-separated paths.\n */\nconst DEFAULT_LIMIT = 1000\n\nfunction entryPath(entry: ListFilesEntry): string {\n return typeof entry === 'string' ? entry : entry.path\n}\n\nfunction entryMetadata(entry: ListFilesEntry): ListFileMetadata | undefined {\n return typeof entry === 'string' ? undefined : entry\n}\n\nfunction renderEntry(entry: ListFilesEntry, wantMetadata: boolean): string {\n const path = entryPath(entry)\n const meta = entryMetadata(entry)\n if (!wantMetadata || typeof meta?.size !== 'number' || typeof meta.mtimeMs !== 'number')\n return path\n return `${path}\\t${meta.size}\\t${new Date(meta.mtimeMs).toISOString()}`\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; patterns may reach outside the workspace (e.g. \"../shared/**/*.ts\"). 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 if (ctx.execution.capabilities.glob !== true)\n throw new Error(`The active execution context (${ctx.execution.type}) does not support glob matching.`)\n const entries = await ctx.execution.listFiles(ctx.handle, pat, {\n glob: true,\n limit: max,\n metadata: wantMetadata,\n })\n if (entries.length === 0)\n return '(no matches)'\n return entries.map(entry => renderEntry(entry, wantMetadata)).join('\\n')\n }\n catch (err) {\n return `Glob error: ${errorMessage(err)}`\n }\n },\n}\n","import type { ToolContext, ToolDef } from './types'\nimport { Buffer } from 'node:buffer'\nimport { errorMessage, isAbortLikeError } from '../errors'\nimport { shellQuote } from './shell-quote'\n\n/**\n * Search file contents by regex.\n *\n * Wraps ripgrep (`rg`) when available; when it's not, falls back to a\n * regex search that enumerates files via the execution context's shell\n * (`find` over `exec`) and reads them via `ctx.execution.readFile`. Both\n * legs go through the {@link ToolContext.execution} seam, so the search\n * runs wherever the context's files actually live — in-process, a\n * container, or a remote sandbox — without the tool ever touching the host\n * filesystem directly.\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. Pagination operates on\n * OUTPUT LINES, not match groups — in content mode, context lines and `--`\n * separators consume the budget too (documented in the schema).\n *\n * A line cap is not enough on its own: ripgrep is line-oriented, so a minified\n * / single-line file collapses to ONE match line that is the entire file, and\n * `head_limit` would happily return megabytes — enough to blow the model's\n * context window. Output is therefore ALSO byte-bounded: each line is clipped\n * to `MAX_LINE_BYTES` and the total to `OUTPUT_BYTE_CAP`, mirroring the byte\n * discipline `read_file` already enforces.\n *\n * `path` is deliberately NOT confined to the workspace: like `read_file`,\n * absolute paths and `../` traversal are allowed — the execution context is\n * the sandbox boundary, not this tool.\n */\n\nconst DEFAULT_HEAD_LIMIT = 250\n\n/**\n * Per-line byte cap. ripgrep reports a match as the whole matching LINE, so a\n * minified / single-line file (one line = the entire file) would otherwise be\n * returned in full. Clip each line so a single line can't dump unbounded bytes\n * into the model context.\n */\nconst MAX_LINE_BYTES = 2000\n\n/** Total output byte cap, mirroring read_file's `DEFAULT_BYTE_CAP`. */\nconst OUTPUT_BYTE_CAP = 262_144\n\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 portable shell-listing + regex fallback when `rg` is absent.',\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: \".\". May be absolute or outside the workspace (no confinement — consistent with read_file).' },\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 lines. Default: 250. Set 0 for unlimited lines. In content mode, context lines (-A/-B/-C) and \"--\" separators count toward the cap, so the number of matches shown may be lower. Output is independently byte-bounded: each line is clipped to ${MAX_LINE_BYTES} bytes and the total to ${OUTPUT_BYTE_CAP} bytes, so long (e.g. minified) lines never overflow the context window.` },\n 'offset': { type: 'integer', description: 'Skip first N output lines. Default: 0.' },\n },\n required: ['pattern'],\n },\n },\n async execute(rawInput, ctx: ToolContext) {\n const input = rawInput as unknown as GrepInput\n\n try {\n if (await isRipgrepAvailable(ctx))\n return await runViaRipgrep(input, ctx)\n return await runFallback(input, ctx)\n }\n catch (err) {\n // grep must never leak a raw exception to the model. An abort is the\n // sole exception we re-throw — the loop renders its canonical\n // interrupt/cascade marker; everything else (a broken `exec`, an\n // unreadable tree, a remote transport error) becomes a `grep error:`.\n if (isAbortLikeError(err))\n throw err\n return `grep error: ${errorMessage(err)}`\n }\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', { signal: ctx.signal })\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, { signal: ctx.signal })\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// Fallback path (context-routed `find` enumeration + readFile + JS regex)\n// ---------------------------------------------------------------------------\n\nasync function runFallback(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 (err) {\n // Skip unreadable / binary / vanished files — but let an abort\n // propagate (a remote readFile may reject with AbortError on cancel).\n if (isAbortLikeError(err))\n throw err\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 root = input.path ?? '.'\n\n // Single-file fast path: if `path` names a file, search just it. Routed\n // through `exec` so the check runs inside the context, not on the host.\n if (input.path && !input.path.includes('*') && !input.path.includes('?')) {\n const probe = await ctx.execution.exec(ctx.handle, `test -f ${shellQuote(input.path)} && echo file || echo dir`, { signal: ctx.signal })\n if (probe.stdout.trim() === 'file')\n return [input.path]\n }\n\n // Enumerate through the execution context's shell so the listing comes from\n // wherever the context's files live — never the host filesystem. `find` is\n // the same primitive the `glob` tool's portable path uses.\n const result = await ctx.execution.exec(ctx.handle, buildFindCommand(root, input.glob), { signal: ctx.signal })\n\n // A non-zero exit with NO output is a real failure (find unavailable, bad\n // root) — surface it like ripgrep would for a bad path. A non-zero exit WITH\n // output is a partial walk (e.g. an unreadable subdir): keep what we got,\n // matching the previous skip-on-error behavior. `find` writes diagnostics to\n // stderr, so stdout stays a clean newline-separated path list either way.\n if (result.exitCode !== 0 && !result.stdout.trim())\n throw new Error(result.stderr.trim() || `find exited with code ${result.exitCode}`)\n\n return result.stdout\n .split('\\n')\n .map(line => line.replace(/^\\.\\//, ''))\n .filter(line => line.length > 0)\n .sort()\n}\n\n/**\n * Build a `find` command listing files under `root`, optionally filtered by\n * `glob`. Mirrors `glob`'s shell fallback:\n * - a bare basename, or one behind a leading recursive-wildcard segment,\n * with no inner slash → `-name` (matches at any depth, including the top\n * level — preserving the recursive-glob semantics).\n * - anything with a slash → `-path` against the full relative path. `find`'s\n * `*` spans `/`, so the single- and double-star forms collapse to the\n * same matcher; close enough for the no-`rg` fallback.\n */\nfunction buildFindCommand(root: string, glob?: string): string {\n const findRoot = normalizeFindRoot(root)\n const base = shellQuote(findRoot)\n if (!glob)\n return `find ${base} -type f`\n const anyDepth = glob.match(/^(?:\\*\\*\\/)?([^/]+)$/)\n if (anyDepth)\n return `find ${base} -type f -name ${shellQuote(anyDepth[1])}`\n const normRoot = findRoot.replace(/\\/$/, '')\n const pathPattern = root === '.' ? `./${glob}` : `${normRoot}/${glob}`\n return `find ${base} -type f -path ${shellQuote(pathPattern)}`\n}\n\nfunction normalizeFindRoot(root: string): string {\n // POSIX `find` parses a leading-dash first operand as an expression/option.\n // Prefix relative paths like `-fixtures` so they remain path operands.\n return root.startsWith('-') ? `./${root}` : root\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 // Byte budget. `head_limit` caps the LINE count, but a single line can be\n // arbitrarily large (a minified / one-line file is one match line = the whole\n // file), so a line cap alone leaves byte output unbounded and can overflow the\n // model context. Clip each line, then stop once the total crosses the cap.\n const capped: string[] = []\n let bytesUsed = 0\n let byteCut = false\n for (const line of sliced) {\n const clipped = clipLine(line)\n const lineBytes = Buffer.byteLength(clipped) + 1 // +1 for the rejoining '\\n'\n if (bytesUsed + lineBytes > OUTPUT_BYTE_CAP && capped.length > 0) {\n byteCut = true\n break\n }\n capped.push(clipped)\n bytesUsed += lineBytes\n }\n\n const shown = capped.length\n const truncatedHead = offset > 0\n // Lines withheld by the line-count window, independent of the byte cut.\n const lineCutTail = headLimit > 0 && offset + headLimit < total\n\n // Markers say \"lines\", not \"matches\" — in content mode the pagination\n // unit includes context lines and `--` separators.\n let out = capped.join('\\n')\n if (truncatedHead)\n out = `…(${offset} earlier lines skipped)…\\n${out}`\n if (byteCut)\n out = `${out}\\n…(output truncated at ${OUTPUT_BYTE_CAP} bytes after ${shown} of ${total - offset} matching lines; narrow the pattern or page with offset=${offset + shown})`\n else if (lineCutTail)\n out = `${out}\\n…(${total - offset - headLimit} more lines; re-run with offset=${offset + headLimit} or larger head_limit)`\n return out\n}\n\n/**\n * Clip one output line to `MAX_LINE_BYTES` at a UTF-8-safe boundary, appending\n * a marker that reports how many bytes were withheld. Lines within budget pass\n * through untouched.\n */\nfunction clipLine(line: string): string {\n if (Buffer.byteLength(line) <= MAX_LINE_BYTES)\n return line\n let cut = Math.min(line.length, MAX_LINE_BYTES)\n while (cut > 0 && Buffer.byteLength(line.slice(0, cut)) > MAX_LINE_BYTES)\n cut--\n const omitted = Buffer.byteLength(line) - Buffer.byteLength(line.slice(0, cut))\n return `${line.slice(0, cut)}… [line clipped, ${omitted} more bytes]`\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\nfunction abortReason(signal: AbortSignal): string {\n return typeof signal.reason === 'string' && signal.reason.length > 0\n ? signal.reason\n : 'Interaction aborted'\n}\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 // Race the host resolver against the run's abort signal. `onRequest`\n // settles only on external (human/system) action, so without this an\n // `agent.abort()` / `agent.destroy()` — every teardown/cancel path —\n // would leave the tool body (and the run) hanging on a promise that can\n // never settle. On abort we reject so the loop unwinds cleanly.\n const { signal } = ctx\n if (signal?.aborted)\n throw new Error(abortReason(signal))\n\n let onAbort: (() => void) | undefined\n const abortPromise = signal\n ? new Promise<never>((_, reject) => {\n onAbort = () => reject(new Error(abortReason(signal)))\n signal.addEventListener('abort', onAbort, { once: true })\n })\n : undefined\n\n try {\n const result = abortPromise\n ? await Promise.race([options.onRequest(input, ctx), abortPromise])\n : await options.onRequest(input, ctx)\n return typeof result === 'string' ? result : JSON.stringify(result)\n }\n finally {\n if (signal && onAbort)\n signal.removeEventListener('abort', onAbort)\n }\n },\n }\n}\n","import type { ListFilesEntry } from '../contexts'\nimport type { ToolContext, ToolDef } from './types'\n\nfunction entryPath(entry: ListFilesEntry): string {\n return typeof entry === 'string' ? entry : entry.path\n}\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 the immediate entries (files and subdirectories) at literal directory `path`. Returns a newline-separated list, or `(empty directory)` when the directory exists but is empty. Returns `Directory not found: <path>` for a missing path. Non-recursive: use `glob` for pattern matching across nested directories.',\n inputSchema: {\n type: 'object',\n properties: {\n path: { type: 'string', description: 'Directory path (relative to the execution-context cwd, or absolute). Defaults to `.`.' },\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.map(entryPath).join('\\n') || '(empty directory)'\n }\n catch {\n return `Directory not found: ${path}`\n }\n },\n}\n","import type { ToolContext, ToolDef } from './types'\nimport { describeVia, resolveOldString, styleReplacementForVia } from './edit-utils'\nimport { suggestionFor } from './path-suggest'\nimport { hashContent, readStateKey, resolveReadStateMap } from './read-state'\nimport { pathArgError } from './validation'\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: 'File path (relative to the execution-context cwd, or absolute).' },\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 const pathErr = pathArgError(target)\n if (pathErr) {\n ctx.reportOutcome?.('failed')\n return `multi_edit error: ${pathErr}`\n }\n\n if (!Array.isArray(steps) || steps.length === 0) {\n ctx.reportOutcome?.('failed')\n return `multi_edit error: edits must be a non-empty array.`\n }\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 ctx.reportOutcome?.('failed')\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 ctx.reportOutcome?.('failed')\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 }\n if (prior.contentHash !== hashContent(current)) {\n ctx.reportOutcome?.('failed')\n return `multi_edit error: ${target} has changed on disk since the last read. Re-read the file before editing.`\n }\n }\n }\n\n const outcomes: StepOutcome[] = []\n // Non-exact recovery notes (one per step that matched via a fallback) —\n // kept OUT of the `<edit-outcomes>` annotation block, whose line format\n // is parsed strictly by `parseEditOutcomesFromResult`.\n const viaNotes: string[] = []\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 if (via !== 'exact')\n viaNotes.push(`edit #${i + 1}: old_string was not found verbatim; matched via ${describeVia(via)}`)\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 // Reported only AFTER the write lands — a throwing `writeFile` falls\n // through to the loop's error path, which must not see a stale `edited`.\n ctx.reportOutcome?.('edited')\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 else {\n // No edit applied — nothing reached disk.\n ctx.reportOutcome?.('failed')\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 (viaNotes.length > 0)\n parts.push(viaNotes.join('\\n'))\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 { parseAttachmentHandle, resolveAttachmentFromTurns } from '../attachment-handle'\nimport { errorMessage } from '../errors'\nimport { looksBinary } from './binary-detect'\nimport { imageMediaTypeFor, readFileAsBase64 } from './binary-read'\nimport { reconcileImageMediaType } from './media-sniff'\nimport { suggestionFor } from './path-suggest'\nimport { hashContent, readStateKey, resolveReadStateMap } from './read-state'\nimport { pathArgError } from './validation'\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 attachment bytes we'll inline as a base64 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_ATTACHMENT_BYTE_CAP = 5 * 1024 * 1024\n\nfunction attachmentByteLength(data: string, encoding?: 'base64' | 'text'): number {\n if (encoding === 'text')\n return Buffer.byteLength(data)\n if (data.length === 0)\n return 0\n const pad = data.endsWith('==') ? 2 : data.endsWith('=') ? 1 : 0\n return Math.max(0, Math.floor((data.length * 3) / 4) - pad)\n}\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. Text files return 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. Images (png/jpg/gif/webp) and PDFs return structured attachments for capable models; other binary files return a short marker rather than mojibake.',\n inputSchema: {\n type: 'object',\n properties: {\n path: { type: 'string', description: 'File path (relative to the execution-context cwd, or absolute).' },\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 for text, 5242880 for image/PDF attachments. 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 // Attachment re-resolve: `read_file path=\"attachment://<hash>\"` pulls an\n // aged-out image/document back from session history (see media age-out in\n // the loop). Branch before the filesystem-path checks — the handle is not a\n // real path. Re-inlines the bytes for THIS turn only; they age out again on\n // the natural schedule.\n const handleHash = typeof path === 'string' ? parseAttachmentHandle(path) : null\n if (handleHash) {\n const turns = ctx.session?.turns\n if (!turns)\n return `Attachment ${path} cannot be resolved: no session is bound to this run.`\n const found = resolveAttachmentFromTurns(turns, handleHash)\n if (!found)\n return `Attachment ${path} not found in this session's history (it may predate this session, or only ever lived as a host-offloaded reference).`\n const sizeCap = maxBytes !== undefined ? normalizeInteger(maxBytes, DEFAULT_ATTACHMENT_BYTE_CAP) : DEFAULT_ATTACHMENT_BYTE_CAP\n const byteLength = attachmentByteLength(found.data, found.encoding)\n if (sizeCap > 0 && byteLength > sizeCap)\n return `[attachment too large to re-inline: ${path}, ${byteLength} bytes (cap ${sizeCap}). Raise maxBytes if you need the original bytes.]`\n const label = found.kind === 'image' ? 'Image' : 'Document'\n const content: ToolResultContent[] = [\n { type: 'text', text: `${label} re-resolved from session history (${found.mediaType}${found.name ? `, \"${found.name}\"` : ''}).` },\n found.kind === 'image'\n ? { type: 'image', mediaType: found.mediaType, data: found.data, ...(found.name ? { name: found.name } : {}) }\n : { type: 'document', mediaType: found.mediaType, data: found.data, encoding: found.encoding ?? 'base64', ...(found.name ? { name: found.name } : {}) },\n ]\n return content\n }\n\n // Reasoning-leak backstop — see `pathArgError`.\n const pathErr = pathArgError(path as string)\n if (pathErr)\n return `Read error: ${pathErr}`\n\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 first (cheap), then reconciled\n // against magic bytes — Anthropic rejects the whole request when the\n // declared `media_type` disagrees with the actual bytes (e.g. a JPEG\n // saved with a `.webp` extension).\n const extMedia = imageMediaTypeFor(path as string)\n if (extMedia) {\n const sizeCap = maxBytes !== undefined ? normalizeInteger(maxBytes, DEFAULT_ATTACHMENT_BYTE_CAP) : DEFAULT_ATTACHMENT_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 imgMedia = reconcileImageMediaType(extMedia, base64)\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 const docMedia = documentMediaTypeFor(path as string)\n if (docMedia) {\n const sizeCap = maxBytes !== undefined ? normalizeInteger(maxBytes, DEFAULT_ATTACHMENT_BYTE_CAP) : DEFAULT_ATTACHMENT_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 `[document too large to attach: ${path}, ${byteLength} bytes (cap ${sizeCap}). Raise maxBytes, or use shell/read text extraction to inspect.]`\n }\n if (docMedia === 'application/pdf' && !isPdfBase64(base64)) {\n return `[binary file: ${path}, ${byteLength} bytes; extension suggests PDF but file header does not start with %PDF-]`\n }\n const content: ToolResultContent[] = [\n { type: 'text', text: `Document: ${path} (${byteLength} bytes, ${docMedia})` },\n { type: 'document', mediaType: docMedia, data: base64, encoding: 'base64', name: fileNameForPath(path as string) },\n ]\n return content\n }\n catch (err) {\n const hint = await suggestionFor(ctx.execution, ctx.handle, path as string)\n return `Document 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 const rememberRead = () => {\n if (!readState)\n return\n readState.set(absKey, {\n contentHash: currentHash,\n offset: offsetForKey,\n limit: limitForKey,\n maxBytes: maxBytesForKey,\n lineNumbers: showLineNumbers,\n mtimeMs: Date.now(),\n })\n }\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 // Stale-read elision removed the prior output from the on-wire\n // history — the stub would point at content the model can no\n // longer see. Serve the full body; `rememberRead` clears the flag.\n && prior.elided !== true\n ) {\n // A dedup hit still represents an intentional re-read. Refresh the\n // tracking entry so `requireReadBeforeEdit` sees the re-read even\n // though we keep the token-saving stub output.\n //\n // This stub only fires when the prior output is still on the wire:\n // every elision path (stale-read elision, tail compaction) flips the\n // entry's `elided` flag, which the guard above checks so a re-read of\n // compacted-away content MISSES dedup and re-serves the full body.\n // So we deliberately do NOT tell the model to \"re-read with a\n // different offset/limit\" — that advice, when the content was already\n // gone, drove an infinite offset-cycling re-read loop.\n rememberRead()\n return `File ${path} unchanged since the previous read in this session — the prior result above is still current, so this duplicate read was skipped to save tokens.`\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 rememberRead()\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\nfunction documentMediaTypeFor(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 return ext === 'pdf' ? 'application/pdf' : undefined\n}\n\nfunction fileNameForPath(path: string): string {\n const normalized = path.replace(/\\\\/g, '/')\n const slash = normalized.lastIndexOf('/')\n return slash === -1 ? normalized : normalized.slice(slash + 1)\n}\n\nfunction isPdfBase64(base64: string): boolean {\n return Buffer.from(base64.slice(0, 16), 'base64').toString('ascii').startsWith('%PDF-')\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 'stream:server_tool_use',\n 'stream:server_tool_result',\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. Content-ref resolution (`data`), gate hooks (`block` /\n// `result`), and `tool:transform` (`result` rewrite) need this so parent\n// policy lands on the child's wire-level request/result.\nconst BUBBLED_MUTABLE_EVENTS = [\n 'content-ref:resolve',\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 'stream:server_tool_use': 'child:stream:server_tool_use',\n 'stream:server_tool_result': 'child:stream:server_tool_result',\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 'content-ref:resolve': 'content-ref:resolve',\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 * Read-only tool whitelist applied when a subagent preset has\n * `readonly: true` and no explicit `tools` list. Intentionally narrow —\n * only obviously non-mutating built-ins. Hosts wanting a different\n * read-only profile (e.g. include `glob` for a code-search agent) should\n * pass an explicit `tools` array on the subagent def.\n */\nconst READONLY_TOOL_DEFAULTS = ['read_file', 'grep', 'glob', 'list_files'] as const\n\n/**\n * Apply a subagent preset's tool filter to the parent's tool registry.\n * Always a strict subset of the input — the child agent never gains\n * tools the parent doesn't have. Names that don't match a parent tool\n * are silently dropped (matches MCP's lenient `enabledTools` behavior).\n *\n * Precedence: `def.tools` (explicit list) > `def.readonly: true`\n * (built-in read-only set) > unfiltered.\n */\nfunction filterToolsForSubagent(\n parentTools: Record<string, ToolDef> | undefined,\n def: SubagentDef,\n): Record<string, ToolDef> | undefined {\n if (!parentTools)\n return parentTools\n const explicit = def.tools\n // Match on `tool.spec.name` rather than the parent registry's object\n // key — registry keys are an implementation detail of the host\n // (`basicTools = { readFile, listFiles, ... }` uses camelCase JS\n // keys), but `spec.name` (`'read_file'`, `'list_files'`) is the\n // stable, documented, model-visible identifier hosts reason about.\n if (explicit && explicit.length > 0) {\n const wanted = new Set(explicit)\n const filtered: Record<string, ToolDef> = {}\n for (const [registryKey, t] of Object.entries(parentTools)) {\n if (wanted.has(t.spec.name))\n filtered[registryKey] = t\n }\n return filtered\n }\n if (def.readonly) {\n const wanted = new Set<string>(READONLY_TOOL_DEFAULTS)\n const filtered: Record<string, ToolDef> = {}\n for (const [registryKey, t] of Object.entries(parentTools)) {\n if (wanted.has(t.spec.name))\n filtered[registryKey] = t\n }\n return filtered\n }\n return parentTools\n}\n\n/**\n * Render the per-type descriptions into a single schema-field\n * description string the model reads when choosing a `subagent_type`.\n * Falls back to a generic line when the host didn't supply any per-type\n * descriptions.\n */\nfunction buildSubagentTypeDescription(registry: SubagentRegistry): string {\n const lines: string[] = []\n let hasAny = false\n for (const [key, def] of Object.entries(registry)) {\n if (def.description) {\n lines.push(`- \"${key}\": ${def.description}`)\n hasAny = true\n }\n else {\n lines.push(`- \"${key}\"`)\n }\n }\n lines.push('- \"general-purpose\": no specialization; uses the spawn tool\\'s default config.')\n if (!hasAny) {\n return `Optional subagent preset. One of: ${lines.map(l => l.replace(/^- /, '').replace(/:.*$/, '')).join(', ')}.`\n }\n return `Optional subagent preset that overlays the spawn tool's defaults.\\n${lines.join('\\n')}`\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 // Self-mapped events (parentEvt === evt, e.g. `content-ref:resolve`) already\n // have an originating-bubble listener on this same event (registered above).\n // A second listener here would fire the parent twice for a direct child.\n // Grandchild propagation still works without it: the child→grandchild\n // bubble fires the child's own `content-ref:resolve`, which this child's\n // originating listener forwards upward.\n if (parentEvt === evt)\n continue\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 /**\n * Child model override. When unset, the child inherits the parent run's\n * resolved model (`ToolContext.model`), falling back to\n * `provider.meta.defaultModel` only when the parent didn't expose one.\n */\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' | 'xhigh' | 'max'\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 * Named subagent presets the model can select via the `subagent_type`\n * input field. Mirrors the Claude Code SDK's surface — models trained\n * on it routinely emit `subagent_type: 'Explore' | 'Plan' |\n * 'Verification' | 'general-purpose'`, and without a registry the\n * field is silently dropped, so hosts wanting type-specialized\n * subagents have to invent their own dispatch layer.\n *\n * Each entry overlays the base spawn config for that particular\n * dispatch. Per-call `input.system` still wins over `subagents[type].system`\n * so the model can always specialize further.\n *\n * When the registry is non-empty:\n * - `subagent_type` appears in the spawn input schema as a `string`\n * enum of the registered keys (plus the always-available\n * `'general-purpose'` fallback).\n * - Models that pass an unregistered type are routed to\n * `'general-purpose'` (no error — degrade gracefully so trained\n * models keep working even on hosts that haven't wired every\n * type Claude Code uses).\n *\n * When the registry is empty / unset, the field is omitted entirely\n * (preserves the historical schema for hosts that never use this).\n *\n * Default: `undefined` (no subagent types; `subagent_type` schema\n * field hidden).\n */\n subagents?: SubagentRegistry\n}\n\n/**\n * Per-type subagent override applied when the model calls\n * `spawn({ subagent_type: '…' })`. All fields are optional; absent\n * fields fall back to the parent's resolved configuration (see\n * {@link SpawnToolOptions} comments for the merge order).\n */\nexport interface SubagentDef {\n /**\n * System prompt override for this subagent type. Per-call\n * `input.system` still wins — model-supplied specialization beats\n * preset defaults.\n */\n system?: string\n /**\n * Restrict the child agent's tool registry to this list of canonical\n * tool names. Operates as a filter over the parent's tools (the\n * parent's selection is the upper bound — a subagent can never gain\n * tools the parent doesn't have). When unset, the child inherits the\n * parent's full tool list.\n *\n * Tool names are canonical (registry-key) not wire/alias names — the\n * filter runs before aliases are applied. Names that don't match a\n * parent tool are silently dropped (matches the lenient behaviour of\n * `enabledTools` on MCP configs).\n */\n tools?: readonly string[]\n /**\n * Mark this subagent as read-only — equivalent to listing only\n * obviously-non-mutating tools (`read_file`, `grep`, `glob`,\n * `list_files`) in {@link SubagentDef.tools}. Convenience for the\n * common \"Plan\" / \"Explore\" subagent shape Claude Code ships.\n *\n * When both `readonly: true` and `tools` are set, `tools` wins\n * (explicit beats implicit).\n */\n readonly?: boolean\n /**\n * Short description rendered into the spawn tool's schema (the\n * `subagent_type` field's description) so the model can pick a type\n * that matches the task without round-tripping through docs.\n */\n description?: string\n}\n\n/**\n * Map of subagent-type key → preset. Keys are case-sensitive and\n * appear verbatim in the spawn input schema's enum so the model emits\n * them with the same casing. Common conventions: `'Explore'`,\n * `'Plan'`, `'Verification'`, `'general-purpose'` (Claude Code SDK's\n * built-in set).\n */\nexport type SubagentRegistry = Record<string, SubagentDef>\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 // Pre-compute the subagent_type schema piece once. Including the\n // enum keys (plus the canonical `general-purpose` fallback) saves\n // the host from re-typing them in their preset description. Empty\n // registry — or one passed as `{}` with no entries — drops the field\n // entirely, preserving the historical schema. We don't surface\n // `subagent_type` with only `general-purpose` in the enum (a\n // single-value enum is noise to the model with no decision to make).\n const subagentRegistry = options.subagents\n const hasSubagentEntries = !!subagentRegistry && Object.keys(subagentRegistry).length > 0\n const subagentTypeKeys = hasSubagentEntries\n ? [...new Set([...Object.keys(subagentRegistry!), 'general-purpose'])]\n : []\n const subagentTypeProperty = subagentTypeKeys.length > 0\n ? {\n subagent_type: {\n type: 'string' as const,\n enum: subagentTypeKeys,\n description: buildSubagentTypeDescription(subagentRegistry!),\n },\n }\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 ...subagentTypeProperty,\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 requestedSubagentType = typeof input.subagent_type === 'string'\n ? input.subagent_type\n : undefined\n // Resolve the subagent preset. Unknown types degrade to\n // `general-purpose` (i.e. no overlay) so Claude-Code-trained\n // models that emit `subagent_type: 'Verification'` against hosts\n // that never wired that type don't get silently broken — the\n // call still runs, just with the parent's default config.\n const subagentDef = requestedSubagentType && subagentRegistry\n ? (subagentRegistry[requestedSubagentType] ?? undefined)\n : 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 // Apply the subagent-type tool filter on top of the parent's\n // tool registry. `tools` wins over `readonly` when both are\n // set (explicit beats implicit). The filter is a strict subset\n // — a subagent can never gain tools the parent doesn't have.\n const filteredTools = subagentDef\n ? filterToolsForSubagent(ctx.tools, subagentDef)\n : ctx.tools\n\n const parentPreset: Preset = {\n ...(ctx.name !== undefined ? { name: ctx.name } : {}),\n ...(ctx.system !== undefined ? { system: ctx.system } : {}),\n tools: filteredTools,\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 // System prompt precedence (highest first):\n // 1. per-call `input.system` — model-supplied specialization.\n // 2. `subagents[type].system` — host preset for this type.\n // 3. `options.system` — spawn-tool default.\n // The previous logic skipped (2). Subagent-type system\n // overrides matter for Plan/Explore-style presets where the\n // type IS the contract (e.g. \"you are a research subagent…\").\n const effectiveSystem\n = systemOverride\n ?? subagentDef?.system\n ?? options.system\n // Model precedence: explicit spawn-tool override > parent run's\n // resolved model > provider default (inside `agent.run`). Without\n // the `ctx.model` fallback, a parent running on a per-run model\n // would silently spawn children on `provider.meta.defaultModel`\n // (e.g. a sonnet run spawning opus subagents).\n const runPromise = agent.run({\n prompt: task,\n model: options.model ?? ctx.model,\n system: effectiveSystem,\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 Promise.resolve(ctx.hooks.callHook('background:exit', info)).catch(() => {})\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'\nimport { hashContent, readStateKey, resolveReadStateMap } from './read-state'\nimport { pathArgError } from './validation'\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 `path`, creating any missing parent directories. Overwrites existing files in full; prefer `edit` / `multi_edit` for surgical changes when you only want to alter part of a file. Returns one of: `Created <path> (N bytes)` (file did not exist), `Updated <path> (N bytes)` (content differed), or `No change needed: <path> already at target state (N bytes)` — so the model can detect no-ops without a separate `read_file`.',\n inputSchema: {\n type: 'object',\n properties: {\n path: { type: 'string', description: 'File path (relative to the execution-context cwd, or absolute).' },\n content: { type: 'string', description: 'Complete file content. Overwrites any existing file at `path`.' },\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 // Reasoning-leak backstop — reject pathologically-shaped paths with a\n // steering message instead of letting the fs surface ENAMETOOLONG.\n const pathErr = pathArgError(targetPath)\n if (pathErr) {\n ctx.reportOutcome?.('failed')\n return `Write error: ${pathErr}`\n }\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 ctx.reportOutcome?.('noop')\n return `No change needed: ${targetPath} already at target state (${bytes} bytes).`\n }\n\n await ctx.execution.writeFile(ctx.handle, targetPath, targetContent)\n\n // Seed read-state with the just-written bytes so a follow-up `edit` /\n // `multi_edit` on this path passes the `requireReadBeforeEdit` gate and\n // compares against current content — the model demonstrably knows the\n // file's bytes (it just authored them), so forcing a redundant `read_file`\n // would be pure overhead. Slice params mirror a full read (offset 0, no\n // limit) so a later partial re-read still invalidates cleanly. No-op\n // without a read-state map (sessionless, no shared map).\n const readState = resolveReadStateMap(ctx)\n if (readState) {\n readState.set(readStateKey(ctx.handle.cwd, targetPath), {\n contentHash: hashContent(targetContent),\n offset: 0,\n limit: Number.POSITIVE_INFINITY,\n maxBytes: Number.POSITIVE_INFINITY,\n mtimeMs: Date.now(),\n })\n }\n\n ctx.reportOutcome?.(existing === undefined ? 'created' : 'updated')\n return existing === undefined\n ? `Created ${targetPath} (${bytes} bytes).`\n : `Updated ${targetPath} (${bytes} bytes).`\n },\n}\n"],"mappings":"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AAS,iBAAiB,MAAM,MAAM,GACnF,WAAW,OAAO,GACzB,QAAQ,IAAI,YAAY;GAAA;EAE9B;CACF;CAEF,OAAO;EAAE;EAAS;CAAY;AAChC;;;;;;AAOA,SAAS,mBAAmB,MAA6B;CACvD,OAAO;EACL;EACA,cAAc,UAAU,KAAK,MAAM,EAAE;EACrC,cAAc,UAAU,KAAK,OAAO,EAAE;EACtC,kBAAkB,UAAU,KAAK,UAAU,EAAE;EAC7C,qBAAqB,KAAK,aAAa;EACvC,oBAAoB,KAAK,aAAa;EACtC;EACA;CACF,EAAE,KAAK,IAAI;AACb;AAmGA,MAAM,iBAAsC,IAAI,IAAI;CAhGlD;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;AAGiE,CAAC;AAEpE,SAAS,iBAAiB,OAA0C;CAClE,OAAO,eAAe,IAAI,KAAK;AACjC;;;;;;;;;;;;;;;;;;;;;;;;;;AA0CA,eAAe,6BACb,OACA,iBACA,OACA,UACA,OACA,OACe;CACf,IAAI,MAAM,WAAW,GACnB;CACF,MAAM,OAAO,MAAM,MAAM,SAAS;CAClC,IAAI,KAAK,SAAS,aAChB;CACF,MAAM,aAAuB,CAAC;CAC9B,KAAK,MAAM,SAAS,KAAK,SACvB,IAAI,MAAM,SAAS,aACjB,WAAW,KAAK,MAAM,EAAE;CAE5B,IAAI,WAAW,WAAW,GACxB;CAIF,MAAM,2BAAW,IAAI,IAAY;CACjC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"}
|