zidane 6.2.0 → 6.2.6
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-0JO-UBEv.js → acp-CYJvnqPa.js} +2 -2
- package/dist/{acp-0JO-UBEv.js.map → acp-CYJvnqPa.js.map} +1 -1
- package/dist/acp-cli.js +4 -4
- package/dist/acp.d.ts +1 -1
- package/dist/acp.js +1 -1
- package/dist/{agent-25moybsj.js → agent-D11SxCvA.js} +84 -66
- package/dist/agent-D11SxCvA.js.map +1 -0
- package/dist/agent.d.ts +1 -1
- package/dist/agent.js +2 -2
- package/dist/{anthropic-CV_OSYVD.js → anthropic-DdlSgT6K.js} +47 -25
- package/dist/anthropic-DdlSgT6K.js.map +1 -0
- package/dist/{auth-D5FfJtLf.js → auth-Ys_7TmRj.js} +2 -2
- package/dist/{auth-D5FfJtLf.js.map → auth-Ys_7TmRj.js.map} +1 -1
- package/dist/{cache-telemetry-BKAwsNxm.js → cache-telemetry-KJ7B-_Gh.js} +8 -3
- package/dist/cache-telemetry-KJ7B-_Gh.js.map +1 -0
- package/dist/chat/pure.d.ts +3 -3
- package/dist/chat/pure.js +1 -1
- package/dist/chat.d.ts +11 -4
- package/dist/chat.d.ts.map +1 -1
- package/dist/chat.js +5 -5
- package/dist/contexts/daytona.d.ts +1 -1
- package/dist/contexts/e2b.d.ts +1 -1
- package/dist/eval.d.ts +1 -1
- package/dist/extensions.d.ts +1 -1
- package/dist/extensions.js +1 -1
- package/dist/headless.d.ts +1 -1
- package/dist/headless.js +1 -1
- package/dist/{index-mUT8TI9j.d.ts → index-CNyYEOy0.d.ts} +110 -36
- package/dist/index-CNyYEOy0.d.ts.map +1 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +4 -4
- package/dist/{inline-autocomplete-w_O3SVtN.js → inline-autocomplete-BAwBcZtD.js} +12 -7
- package/dist/{inline-autocomplete-w_O3SVtN.js.map → inline-autocomplete-BAwBcZtD.js.map} +1 -1
- package/dist/mcp.d.ts +1 -1
- package/dist/output/stream-json.d.ts +1 -1
- package/dist/output/terminal.d.ts +1 -1
- package/dist/presets.d.ts +1 -1
- package/dist/presets.js +1 -1
- package/dist/providers/anthropic.d.ts +1 -1
- package/dist/providers/anthropic.js +1 -1
- package/dist/providers/arcee.d.ts +1 -1
- package/dist/providers/baseten.d.ts +1 -1
- package/dist/providers/cerebras.d.ts +1 -1
- package/dist/providers/local.d.ts +1 -1
- package/dist/providers/openai-compat.d.ts +1 -1
- package/dist/providers/openai.d.ts +1 -1
- package/dist/providers/openrouter.d.ts +1 -1
- package/dist/providers/xai.d.ts +1 -1
- package/dist/providers.d.ts +1 -1
- package/dist/providers.js +1 -1
- package/dist/restate.d.ts +1 -1
- package/dist/session/sqlite.d.ts +1 -1
- package/dist/session.d.ts +1 -1
- package/dist/skills.d.ts +1 -1
- package/dist/{tool-formatters-DOcJz_J9.d.ts → tool-formatters-B7oWiy5m.d.ts} +2 -2
- package/dist/tool-formatters-B7oWiy5m.d.ts.map +1 -0
- package/dist/tools/fetch-url.d.ts +1 -1
- package/dist/tools/web-search.d.ts +1 -1
- package/dist/tools.d.ts +1 -1
- package/dist/tools.js +1 -1
- package/dist/{transcript-anchors-CzMMNrhJ.d.ts → transcript-anchors-BUZ915pM.d.ts} +2 -2
- package/dist/{transcript-anchors-CzMMNrhJ.d.ts.map → transcript-anchors-BUZ915pM.d.ts.map} +1 -1
- package/dist/{transcript-anchors-CO-WnuYQ.js → transcript-anchors-cc4gsU-j.js} +12 -4
- package/dist/{transcript-anchors-CO-WnuYQ.js.map → transcript-anchors-cc4gsU-j.js.map} +1 -1
- package/dist/tui.d.ts +17 -14
- package/dist/tui.d.ts.map +1 -1
- package/dist/tui.js +206 -32
- package/dist/tui.js.map +1 -1
- package/dist/{turn-operations-CrCrJnvo.d.ts → turn-operations-2Ou_DkbC.d.ts} +3 -3
- package/dist/{turn-operations-CrCrJnvo.d.ts.map → turn-operations-2Ou_DkbC.d.ts.map} +1 -1
- package/dist/{turn-operations-D9p-y9EC.js → turn-operations-CXjRaltC.js} +72 -2
- package/dist/{turn-operations-D9p-y9EC.js.map → turn-operations-CXjRaltC.js.map} +1 -1
- package/dist/types-BIarq1qE.js.map +1 -1
- package/dist/types.d.ts +1 -1
- package/docs/ARCHITECTURE.md +1 -1
- package/docs/EXTENSIONS.md +6 -5
- package/package.json +2 -1
- package/scripts/eval.ts +21 -3
- package/dist/agent-25moybsj.js.map +0 -1
- package/dist/anthropic-CV_OSYVD.js.map +0 -1
- package/dist/cache-telemetry-BKAwsNxm.js.map +0 -1
- package/dist/index-mUT8TI9j.d.ts.map +0 -1
- package/dist/tool-formatters-DOcJz_J9.d.ts.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agent-D11SxCvA.js","names":["pathResolve","DEFAULT_MAX_NUDGES","DANGEROUS_RECORD_KEYS","readJson","entryPath","DEFAULT_LIMIT","entryPath","readFile","writeFile","turnsToMessages","withTimeout","pathResolve","withTimeout","builtinShell"],"sources":["../src/behaviors/dedup-tools.ts","../src/behaviors/shared.ts","../src/behaviors/max-turns-warning.ts","../src/behaviors/progress-nudges.ts","../src/behaviors/read-file-defaults.ts","../src/behaviors/repeat-guard.ts","../src/behaviors/text-tool-call-rescue.ts","../src/behaviors/thinking-decay.ts","../src/behaviors/thinking-overrun-policy.ts","../src/behaviors/tool-budgets.ts","../src/behaviors/unknown-tool-recovery.ts","../src/behaviors/write-file-existing-policy.ts","../src/compact/policy.ts","../src/extensions/config.ts","../src/extensions/define.ts","../src/extensions/with-timeout.ts","../src/extensions/discovery.ts","../src/chat/keybindings.ts","../src/chat/xdg.ts","../src/tools/path-suggest.ts","../src/tools/validation.ts","../src/tools/whole-read-guard.ts","../src/tools/edit.ts","../src/tools/glob.ts","../src/tools/grep.ts","../src/tools/interaction.ts","../src/tools/list-files.ts","../src/tools/model-search.ts","../src/tools/multi-edit.ts","../src/attachment-handle.ts","../src/tools/binary-detect.ts","../src/tools/binary-read.ts","../src/tools/read-file.ts","../src/tools/shell-semantics.ts","../src/tools/truncate.ts","../src/tools/shell.ts","../src/tools/shell-kill.ts","../src/tools/skills-read.ts","../src/tools/skills-run-script.ts","../src/tools/skills-use.ts","../src/tools/spawn.ts","../src/tools/tool-search.ts","../src/tools/wait-task.ts","../src/tools/write-file.ts","../src/presets/basic.ts","../src/presets/index.ts","../src/extensions/host-runtime.ts","../src/extensions/register.ts","../src/extensions/discover-and-build.ts","../src/extensions/host-modules.ts","../src/extensions/hud.ts","../src/extensions/install.ts","../src/extensions/prompt-bridge.ts","../src/extensions/types.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/loop.ts","../src/model-targets.ts","../src/tool-cancel.ts","../src/agent.ts"],"sourcesContent":["/**\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","import { resolve as pathResolve } from 'node:path'\n\nexport type Unregister = () => void\n\nexport function noopUnregister(): void {}\n\n/**\n * Clamp an optional knob to a positive integer, falling back when the value is\n * missing or non-finite. Shared by the behavior installers that accept caps\n * like `maxNudges` / `maxSuggestions`.\n */\nexport function clampPositiveInt(value: number | undefined, fallback: number): number {\n return typeof value === 'number' && Number.isFinite(value)\n ? Math.max(1, Math.floor(value))\n : fallback\n}\n\n/**\n * Resolve a `string | ((args) => string)` message knob with a built-in\n * fallback. A function that throws or returns a blank string falls through to\n * `fallback`, so a buggy host callback can never erase the steering text.\n */\nexport function resolveCustomMessage<Args>(\n custom: string | ((args: Args) => string) | undefined,\n args: Args,\n fallback: () => string,\n): string {\n if (typeof custom === 'function') {\n try {\n const out = custom(args)\n if (typeof out === 'string' && out.trim())\n return out\n }\n catch {\n // Fall through to the built-in message.\n }\n }\n if (typeof custom === 'string' && custom.trim())\n return custom\n return fallback()\n}\n\nfunction hasWildcard(pattern: string): boolean {\n return pattern.startsWith('*') || pattern.endsWith('*')\n}\n\nfunction canonicalizePath(cwd: string, path: string): string {\n return pathResolve(cwd, path)\n}\n\nfunction matchOnePath(path: string, pattern: string): boolean {\n if (pattern === path)\n return true\n if (pattern.endsWith('*'))\n return path.startsWith(pattern.slice(0, -1))\n if (pattern.startsWith('*'))\n return path.endsWith(pattern.slice(1))\n return false\n}\n\nexport function matchPathPattern(path: string, cwd: string, patterns: readonly string[] | undefined): boolean {\n if (!patterns || patterns.length === 0)\n return false\n const canonical = canonicalizePath(cwd, path)\n return patterns.some((pattern) => {\n if (matchOnePath(path, pattern) || matchOnePath(canonical, pattern))\n return true\n if (!hasWildcard(pattern))\n return canonical === canonicalizePath(cwd, pattern)\n if (pattern.endsWith('*') && !pattern.startsWith('*')) {\n const prefix = pattern.slice(0, -1)\n const separator = prefix.endsWith('/') || prefix.endsWith('\\\\') ? '/' : ''\n const canonicalPattern = `${canonicalizePath(cwd, prefix)}${separator}*`\n return matchOnePath(canonical, canonicalPattern)\n }\n return false\n })\n}\n","import type { AgentBehavior } from '../types'\nimport { resolveCustomMessage } from './shared'\n\nexport interface MaxTurnsWarningContext {\n remaining: number\n maxTurns: number\n}\n\nexport function formatMaxTurnsWarning(\n message: AgentBehavior['maxTurnsWarningMessage'],\n ctx: MaxTurnsWarningContext,\n): string {\n return resolveCustomMessage(\n message,\n ctx,\n () => `[Turn limit approaching: ${ctx.remaining} of ${ctx.maxTurns} turns remaining. Stop exploring, finish the most important concrete path, and summarize what you did and what is left.]`,\n )\n}\n","import type { Hookable } from 'hookable'\nimport type { AgentHooks } from '../agent'\nimport type { AgentBehavior, ProgressNudgeKind } from '../types'\nimport type { Unregister } from './shared'\nimport { clampPositiveInt, noopUnregister, resolveCustomMessage } from './shared'\n\nconst DEFAULT_READ_ONLY_TURNS = 4\nconst DEFAULT_FAILED_MUTATIONS = 3\nconst DEFAULT_NOOP_MUTATIONS = 3\nconst DEFAULT_VALIDATION_REJECTS = 3\nconst DEFAULT_TOOL_ERRORS = 3\nconst DEFAULT_MAX_NUDGES = 3\n\nconst DEFAULT_READ_ONLY_TOOLS = new Set([\n 'read_file',\n 'grep',\n 'glob',\n 'list_files',\n 'web_search',\n 'fetch_url',\n 'read_chat_turns',\n 'tool_search',\n 'skills_read',\n 'todoread',\n])\n\nfunction threshold(value: number | false | undefined, fallback: number): number | false {\n if (value === false)\n return false\n return clampPositiveInt(value, fallback)\n}\n\nfunction defaultMessage(kind: ProgressNudgeKind): string {\n switch (kind) {\n case 'readOnlyTurns':\n return 'You have spent several turns gathering context without changing approach. Use the information already collected to take the smallest concrete next step, or summarize the blocker clearly.'\n case 'failedMutations':\n return 'Several edit/write attempts failed without changing files. Stop retrying the same shape: re-read the target context, choose a smaller change, then apply one focused edit.'\n case 'noopMutations':\n return 'Several mutation calls produced no file changes. Check whether the requested state is already reached; if so, report that instead of issuing more edits.'\n case 'validationRejects':\n return 'Several tool calls had invalid arguments. Re-check the advertised tool schema and emit one well-formed native tool call.'\n case 'toolErrors':\n return 'Several tool calls errored. Pivot from repeating the failing call: inspect the error, adjust the input or approach, then continue with one concrete action.'\n }\n}\n\nfunction messageFor(\n config: Exclude<AgentBehavior['progressNudges'], false | undefined>,\n kind: ProgressNudgeKind,\n count: number,\n): string {\n return resolveCustomMessage(config.messages?.[kind], { kind, count }, () => defaultMessage(kind))\n}\n\nexport function installProgressNudges(\n hooks: Hookable<AgentHooks>,\n config: AgentBehavior['progressNudges'],\n deps: { enqueueNudge: (message: string) => void },\n): Unregister {\n if (!config || (config.mode ?? 'steer') !== 'steer')\n return noopUnregister\n const resolvedConfig = config\n\n const maxNudges = clampPositiveInt(resolvedConfig.maxNudges, DEFAULT_MAX_NUDGES)\n const readOnlyTurns = threshold(resolvedConfig.readOnlyTurns, DEFAULT_READ_ONLY_TURNS)\n const failedMutations = threshold(resolvedConfig.failedMutations, DEFAULT_FAILED_MUTATIONS)\n const noopMutations = threshold(resolvedConfig.noopMutations, DEFAULT_NOOP_MUTATIONS)\n const validationRejects = threshold(resolvedConfig.validationRejects, DEFAULT_VALIDATION_REJECTS)\n const toolErrors = threshold(resolvedConfig.toolErrors, DEFAULT_TOOL_ERRORS)\n const readOnlyTools = new Set([...(resolvedConfig.readOnlyTools ?? DEFAULT_READ_ONLY_TOOLS)])\n\n let nudgeCount = 0\n const fired = new Set<ProgressNudgeKind>()\n let consecutiveReadOnlyTurns = 0\n let consecutiveFailedMutations = 0\n let consecutiveNoopMutations = 0\n let validationRejectCount = 0\n let toolErrorCount = 0\n // `tool:error` fires immediately before this call's `tool:after`; tracking\n // the errored call ids lets `tool:after` reset the consecutive error counter\n // only for calls that actually executed cleanly (concurrency-safe).\n const erroredCallIds = new Set<string>()\n\n function maybeNudge(kind: ProgressNudgeKind, count: number, at: number | false): void {\n if (at === false || count < at || nudgeCount >= maxNudges || fired.has(kind))\n return\n fired.add(kind)\n nudgeCount++\n deps.enqueueNudge(messageFor(resolvedConfig, kind, count))\n }\n\n const unhooks = [\n hooks.hook('tool:after', (ctx) => {\n // Any `tool:after` means a call cleared validation, so a run of invalid\n // arguments is broken. A call that did not error also breaks a run of\n // tool errors; the errored-id set distinguishes the error's own\n // `tool:after` (which must not reset the counter).\n validationRejectCount = 0\n if (erroredCallIds.delete(ctx.callId))\n return\n toolErrorCount = 0\n\n // `ToolOutcome` only contains mutation outcomes, so a present value is\n // always a mutation result; read-only tools report none.\n if (ctx.outcome === undefined)\n return\n if (ctx.outcome === 'failed') {\n consecutiveFailedMutations++\n maybeNudge('failedMutations', consecutiveFailedMutations, failedMutations)\n return\n }\n consecutiveFailedMutations = 0\n\n if (ctx.outcome === 'noop') {\n consecutiveNoopMutations++\n maybeNudge('noopMutations', consecutiveNoopMutations, noopMutations)\n return\n }\n consecutiveNoopMutations = 0\n }),\n hooks.hook('validation:reject', () => {\n validationRejectCount++\n maybeNudge('validationRejects', validationRejectCount, validationRejects)\n }),\n hooks.hook('tool:error', (ctx) => {\n erroredCallIds.add(ctx.callId)\n toolErrorCount++\n maybeNudge('toolErrors', toolErrorCount, toolErrors)\n }),\n hooks.hook('turn:after', (ctx) => {\n const entries = Object.entries(ctx.toolCounts.turn).filter(([, count]) => count > 0)\n if (entries.length === 0)\n return\n const readOnly = entries.every(([tool]) => readOnlyTools.has(tool))\n if (!readOnly) {\n consecutiveReadOnlyTurns = 0\n return\n }\n consecutiveReadOnlyTurns++\n maybeNudge('readOnlyTurns', consecutiveReadOnlyTurns, readOnlyTurns)\n }),\n ]\n\n return () => {\n for (const unhook of unhooks)\n unhook()\n }\n}\n","import type { AgentBehavior } from '../types'\n\nexport interface ReadFileDefaultFallbacks {\n lineLimit: number\n textMaxBytes: number\n attachmentMaxBytes: number\n}\n\n// Resolved defaults are structurally the fallbacks with every field filled in.\nexport type ResolvedReadFileDefaults = ReadFileDefaultFallbacks\n\nexport function normalizeReadFileInteger(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\nexport function resolveReadFileDefaults(\n behavior: AgentBehavior | undefined,\n fallbacks: ReadFileDefaultFallbacks,\n): ResolvedReadFileDefaults {\n return {\n lineLimit: normalizeReadFileInteger(behavior?.readFileDefaults?.limit, fallbacks.lineLimit),\n textMaxBytes: normalizeReadFileInteger(behavior?.readFileDefaults?.maxBytes, fallbacks.textMaxBytes),\n attachmentMaxBytes: normalizeReadFileInteger(behavior?.readFileDefaults?.maxBytes, fallbacks.attachmentMaxBytes),\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","import type { Hookable } from 'hookable'\nimport type { AgentHooks } from '../agent'\nimport type { AgentBehavior } from '../types'\nimport type { Unregister } from './shared'\nimport { noopUnregister } from './shared'\n\n// The literal markers (XML/Liquid tags, fenced-block hints, JSON keys) are all\n// lowercase, so no `/i` flag — keeping the generic call clause anchored to an\n// uppercase initial avoids matching ordinary prose like `read_file(path='a')`.\nconst TOOL_CALL_TEXT_RE = /<tool_call\\b|<\\/tool_call>|<\\|tool_call_(?:start|end)\\|>|```(?:json|tool|function_call)|\\{\\s*\"(?:name|tool|tool_name|function|arguments)\"\\s*:|\\b[A-Z]\\w*\\s*\\([^)]*=/\n\nexport function installTextToolCallRescue(\n hooks: Hookable<AgentHooks>,\n config: AgentBehavior['textToolCallRescue'],\n deps: { enqueueSteer: (message: string) => void },\n): Unregister {\n if (!config || config.mode !== 'steer')\n return noopUnregister\n let sent = false\n return hooks.hook('turn:after', (ctx) => {\n if (sent)\n return\n const hasToolCall = ctx.message.content.some(block => block.type === 'tool_call')\n if (hasToolCall)\n return\n const text = ctx.message.content.map((block) => {\n if (block.type === 'text' || block.type === 'thinking')\n return block.text\n return ''\n }).join('\\n')\n if (!TOOL_CALL_TEXT_RE.test(text))\n return\n sent = true\n deps.enqueueSteer('I saw a tool call written as text/markup. Re-emit it using the native tool-call interface with one of the advertised tool names; do not wrap it in markdown, XML, JSON, or Liquid-style markers.')\n })\n}\n","import type { AgentBehavior } from '../types'\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 downstream\n * tooling that wants to 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 // Decay reduces; never raises above the caller's explicit budget. NaN and\n // negative values collapse to 0 so bad curves do not leak into providers.\n if (Number.isNaN(raw) || raw <= 0)\n return 0\n return Math.round(Math.min(baseBudget, raw))\n}\n","import type { Hookable } from 'hookable'\nimport type { AgentHooks } from '../agent'\nimport type { AgentBehavior } from '../types'\nimport type { Unregister } from './shared'\nimport { estimateTokens } from '../compact/utils'\nimport { noopUnregister } from './shared'\n\nexport function installThinkingOverrunPolicy(\n hooks: Hookable<AgentHooks>,\n policy: AgentBehavior['thinkingOverrunPolicy'],\n deps: { baseBudget?: number, enqueueNudge: (message: string) => void },\n): Unregister {\n const mode = typeof policy === 'string' ? policy : policy?.mode\n if (!mode || mode === 'off')\n return noopUnregister\n const threshold = typeof policy === 'object' ? policy.thresholdTokens ?? deps.baseBudget : deps.baseBudget\n if (!threshold || threshold <= 0)\n return noopUnregister\n const message = typeof policy === 'object' && policy.message\n ? policy.message\n : 'Thinking budget is exhausted. Stop deliberating and take the smallest concrete next action now.'\n let fired = false\n return hooks.hook('stream:thinking', (ctx) => {\n if (fired)\n return\n if (estimateTokens(ctx.thinking) >= threshold) {\n fired = true\n deps.enqueueNudge(message)\n }\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 the loop's non-interrupting nudge queue, NOT the user\n * `steeringQueue` — the batch schedulers skip remaining sibling tool calls\n * when the steering queue is non-empty, and a budget nudge must never kill\n * 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 non-interrupting nudge queue so the loop drains it at the next\n * turn 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","import type { Hookable } from 'hookable'\nimport type { AgentHooks } from '../agent'\nimport type { AgentBehavior } from '../types'\nimport type { Unregister } from './shared'\nimport { clampPositiveInt, noopUnregister, resolveCustomMessage } from './shared'\n\nconst DEFAULT_MAX_SUGGESTIONS = 3\nconst DEFAULT_MAX_NUDGES = 2\n\nfunction editDistance(a: string, b: string): number {\n const aa = a.toLowerCase()\n const bb = b.toLowerCase()\n const prev = Array.from({ length: bb.length + 1 }, (_, i) => i)\n const curr = Array.from({ length: bb.length + 1 }).fill(0) as number[]\n for (let i = 1; i <= aa.length; i++) {\n curr[0] = i\n for (let j = 1; j <= bb.length; j++) {\n const cost = aa.charCodeAt(i - 1) === bb.charCodeAt(j - 1) ? 0 : 1\n curr[j] = Math.min(\n prev[j] + 1,\n curr[j - 1] + 1,\n prev[j - 1] + cost,\n )\n }\n for (let j = 0; j <= bb.length; j++)\n prev[j] = curr[j]\n }\n return prev[bb.length]\n}\n\nfunction scoreSuggestion(input: string, candidate: string): number {\n const a = input.toLowerCase()\n const b = candidate.toLowerCase()\n const dist = editDistance(a, b)\n const maxLen = Math.max(a.length, b.length, 1)\n let score = dist / maxLen\n if (a && b.includes(a))\n score -= 0.35\n if (a[0] && a[0] === b[0])\n score -= 0.1\n return score\n}\n\nfunction suggestionsFor(input: string, tools: readonly string[], maxSuggestions: number): string[] {\n const unique = [...new Set(tools)].filter(Boolean)\n return unique\n .map(name => ({ name, score: scoreSuggestion(input, name) }))\n .sort((a, b) => a.score - b.score || a.name.localeCompare(b.name))\n .slice(0, maxSuggestions)\n .map(item => item.name)\n}\n\nfunction buildMessage(\n called: string,\n suggestions: readonly string[],\n custom: Exclude<AgentBehavior['unknownToolRecovery'], false | undefined>['message'] | undefined,\n): string {\n return resolveCustomMessage(custom, { called, suggestions }, () => {\n const suffix = suggestions.length > 0\n ? ` Closest advertised tools: ${suggestions.map(s => `\\`${s}\\``).join(', ')}.`\n : ' No close advertised tool names were found.'\n return `Unknown tool \"${called}\" was called.${suffix} Re-emit the call with one advertised tool name; do not invent tool names.`\n })\n}\n\nexport function installUnknownToolRecovery(\n hooks: Hookable<AgentHooks>,\n config: AgentBehavior['unknownToolRecovery'],\n deps: {\n advertisedToolNames: () => readonly string[]\n enqueueNudge: (message: string) => void\n },\n): Unregister {\n if (!config || (config.mode ?? 'steer') !== 'steer')\n return noopUnregister\n\n const sent = new Set<string>()\n let nudgeCount = 0\n const maxSuggestions = clampPositiveInt(config.maxSuggestions, DEFAULT_MAX_SUGGESTIONS)\n const maxNudges = clampPositiveInt(config.maxNudges, DEFAULT_MAX_NUDGES)\n\n return hooks.hook('tool:unknown', (ctx) => {\n if (nudgeCount >= maxNudges)\n return\n const called = ctx.displayName || ctx.name\n const key = called.toLowerCase()\n if (sent.has(key))\n return\n sent.add(key)\n nudgeCount++\n\n const suggestions = suggestionsFor(called, deps.advertisedToolNames(), maxSuggestions)\n deps.enqueueNudge(buildMessage(called, suggestions, config.message))\n })\n}\n","import type { Hookable } from 'hookable'\nimport type { AgentHooks } from '../agent'\nimport type { AliasMaps } from '../aliasing'\nimport type { ExecutionContext, ExecutionHandle } from '../contexts'\nimport type { AgentBehavior } from '../types'\nimport type { Unregister } from './shared'\nimport { toWireName } from '../aliasing'\nimport { matchPathPattern, noopUnregister } from './shared'\n\nexport function installWriteFileExistingPolicy(\n hooks: Hookable<AgentHooks>,\n policy: AgentBehavior['writeFileExistingPolicy'],\n deps: { execution: ExecutionContext, getHandle: () => ExecutionHandle | undefined, aliasMaps: AliasMaps },\n): Unregister {\n const mode = typeof policy === 'string' ? policy : policy?.mode\n if (mode !== 'block-with-edit-recipe')\n return noopUnregister\n return hooks.hook('tool:gate', async (ctx) => {\n if (ctx.block || ctx.name !== 'write_file')\n return\n const path = typeof ctx.input.path === 'string' ? ctx.input.path : ''\n if (!path)\n return\n const handle = deps.getHandle()\n if (!handle)\n return\n const cwd = handle.cwd\n if (matchPathPattern(path, cwd, typeof policy === 'object' ? policy.allowPaths : undefined))\n return\n if (typeof policy === 'object' && policy.denyPaths && !matchPathPattern(path, cwd, policy.denyPaths))\n return\n try {\n await deps.execution.readFile(handle, path)\n }\n catch {\n return\n }\n ctx.block = true\n const readFileName = toWireName('read_file', deps.aliasMaps)\n const editName = toWireName('edit', deps.aliasMaps)\n const multiEditName = toWireName('multi_edit', deps.aliasMaps)\n ctx.reason = [\n `Existing file writes are disabled for ${path}.`,\n `Read the file first, then use ${editName} or ${multiEditName} for the smallest safe change.`,\n `Next action: ${readFileName}({ \"path\": ${JSON.stringify(path)} })`,\n ].join(' ')\n })\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 * Typed, extension-contributed configuration.\n *\n * Extensions declare config CATEGORIES and typed config ITEMS during\n * `setup(ctx)` via `ctx.registerConfigCategory` / `ctx.registerConfigItem`.\n * Values are persisted in the user's `Settings.extensionConfig` bag\n * (`extName → itemKey → value`) and read back — default-first, with\n * graceful degradation on missing / malformed values — through a single\n * resolver ({@link resolveExtensionConfigValue}). The same resolver\n * powers `ctx.config.get(key)` inside the extension (hooks / tools) and\n * both Settings UIs (TUI + GUI).\n *\n * Design intent:\n * - **One chokepoint for graceful degradation.** Every read — host,\n * extension, TUI render, GUI render — funnels through\n * {@link resolveExtensionConfigValue}; a value that's missing, the\n * wrong type, out of range, off the enum, or failing the author's\n * `validate` collapses to the item's `default`. The UI therefore\n * never has to special-case a broken `state.json` / DB row.\n * - **Namespacing by extension.** Items are keyed `extName → itemKey`\n * so two extensions can both ship a `verbose` knob without colliding.\n * Categories are a flat global registry (an extension may add items\n * to another extension's category, or to its own implicit category\n * whose id is the extension name).\n * - **Wire-safe schema.** The optional main-side `validate` callback is\n * the only non-serializable field; {@link toWireConfigSchema} drops it\n * so the schema crosses an IPC boundary (the GUI) cleanly.\n */\n\nimport type { Settings } from '../chat/types'\nimport type { ExtensionRegistry } from './types'\n\n// ---------------------------------------------------------------------------\n// Schema types\n// ---------------------------------------------------------------------------\n\n/** Concrete value a config item can hold. */\nexport type ExtensionConfigValue = boolean | number | string\n\nexport type ExtensionConfigScope = 'user' | 'project' | 'session'\nexport type ExtensionConfigChangeBehavior = 'live' | 'agent-rebuild' | 'restart'\n\n/** Fields shared by every config-item variant. */\nexport interface ExtensionConfigItemBase {\n /**\n * Stable key, unique within the declaring extension. Used as the\n * persistence key (`extensionConfig[extName][key]`) and the\n * `ctx.config.get(key)` lookup — pick a stable identifier.\n */\n key: string\n /** Short display label rendered in the Settings UIs. */\n label: string\n /** Longer one-line gloss shown under the label. */\n description?: string\n /**\n * Optional main-side validator, run at READ time AFTER the built-in\n * type / range / enum / pattern checks (both must pass). Throwing is\n * treated as \"invalid\" — the read degrades to the item's `default`. It\n * is **never invoked at registration** (declaring an item has no side\n * effects) and is **never serialized over IPC** ({@link toWireConfigSchema}\n * strips it), so it only runs in-process (TUI host + extension reads);\n * the GUI renderer is optimistic and relies on the authoritative core read.\n */\n validate?: (v: unknown) => boolean\n /**\n * Desired persistence scope. Current stores still persist through the\n * existing per-extension settings bag unless a host implements scoped writes;\n * this metadata lets UIs and future stores present the right ownership.\n */\n scope?: ExtensionConfigScope\n /** Whether changing this value requires rebuilding runtime objects. */\n requiresRestart?: boolean\n /** How hosts should apply a changed value. Defaults to live reads through `ctx.config.get`. */\n onChange?: ExtensionConfigChangeBehavior\n}\n\n/** A boolean config item — rendered as a toggle. */\nexport interface ExtensionConfigBooleanItem extends ExtensionConfigItemBase {\n type: 'boolean'\n default: boolean\n}\n\n/**\n * A numeric config item — rendered as a free input row, clamped to `[min, max]`\n * and snapped to `step`. The `default` must itself lie within `[min, max]` or\n * the item is rejected at registration.\n */\nexport interface ExtensionConfigNumberItem extends ExtensionConfigItemBase {\n type: 'number'\n default: number\n min?: number\n max?: number\n step?: number\n}\n\n/** A string config item — rendered as a free input row (optionally `multiline`), validated against `pattern`. */\nexport interface ExtensionConfigStringItem extends ExtensionConfigItemBase {\n type: 'string'\n default: string\n /**\n * Anchored or unanchored JS-regex source gating user-entered values at read\n * time. An invalid pattern is ignored (treated as \"no constraint\"). The\n * pattern is intentionally NOT applied to the `default`, so an empty / unset\n * default (`default: ''`) for an optional patterned field is valid.\n */\n pattern?: string\n multiline?: boolean\n}\n\n/**\n * An enum config item — rendered as a cycling choice row. The `default` must be\n * one of the `options` values or the item is rejected at registration.\n */\nexport interface ExtensionConfigEnumItem extends ExtensionConfigItemBase {\n type: 'enum'\n default: string\n options: readonly { value: string, label: string }[]\n}\n\n/**\n * A model-id config item — rendered as an inline searchable picker over the\n * host's credentialed model catalog (the same interaction grammar as the\n * ctrl+m cross-provider picker: type to filter, `↵` picks — or toggles, in\n * `multi` mode, where `esc` applies the selection).\n *\n * The persisted value is a plain model id, or a comma-separated id list\n * when `multi` — a string either way, so {@link ExtensionConfigValue} and\n * every settings store stay unchanged, and hosts without a catalog\n * (headless, no authed providers) degrade to the free string input.\n * Deliberately catalog-UNvalidated at read time: a stored id whose\n * provider is temporarily unauthed must survive, not silently reset to\n * the default — catalog validation is the consuming extension's call.\n */\nexport interface ExtensionConfigModelItem extends ExtensionConfigItemBase {\n type: 'model'\n default: string\n /** Allow picking MORE than one model (comma-separated persisted value). */\n multi?: boolean\n}\n\n/**\n * What a config ACTION reports back to the host after running. Actions\n * cannot write settings directly — there is deliberately no `config.set`\n * surface for extensions — so a successful run RETURNS the values it wants\n * persisted and the host writes them through its own validated settings\n * path ({@link sanitizeExtensionConfigActionPatch} gates the patch).\n */\nexport interface ExtensionConfigActionResult {\n /** One-line feedback shown in the Settings UI (e.g. \"proxy running at …\"). */\n message?: string\n /**\n * Config values of the DECLARING extension to persist on success, keyed\n * by item key. Entries naming unknown keys, action items, or values that\n * fail the item's schema are dropped (not errors) — a buggy action can\n * degrade but never corrupt the settings bag.\n */\n config?: Record<string, ExtensionConfigValue>\n}\n\n/**\n * An imperative config item — rendered as a \"press ↵ to run\" row instead\n * of a value editor. `run` executes in the extension's own process (it is\n * never serialized — {@link toWireConfigSchema} omits action items\n * entirely), so it can close over the extension's `setup(ctx)` scope.\n *\n * Contract for `run`:\n * - Resolve with an {@link ExtensionConfigActionResult} (or `void`) on\n * success — `message` becomes the row's feedback line, `config`\n * values are persisted by the host.\n * - Throw / reject on failure — the error message is shown on the row.\n * - Bound your own runtime: the host serializes actions (one in flight\n * per modal) but imposes no timeout, so a hanging `run` holds the\n * \"running…\" state until it settles.\n *\n * Actions hold no persisted value: `ctx.config.get(key)` returns\n * `undefined` for an action key and the resolver never yields a value.\n */\nexport interface ExtensionConfigActionItem extends Pick<ExtensionConfigItemBase, 'key' | 'label' | 'description'> {\n type: 'action'\n run: () => ExtensionConfigActionResult | void | Promise<ExtensionConfigActionResult | void>\n /**\n * Optional passive probe — called by the host when the settings panel\n * surfaces the row (and re-called after any action of the panel\n * settles), so the row can show live state (\"running at …\") WITHOUT\n * the user pressing ↵. Contract:\n *\n * - Resolve an {@link ExtensionConfigActionStatus} to show it, or\n * `undefined` for \"no status\" (e.g. the probed tool isn't\n * installed — stay quiet rather than nag).\n * - Throwing is treated as `undefined` — probes must be safe to call\n * at any time and MUST NOT mutate anything.\n * - Be fast: the host caps each probe (a few seconds) and discards\n * late results.\n */\n status?: () => ExtensionConfigActionStatus | undefined | Promise<ExtensionConfigActionStatus | undefined>\n}\n\n/**\n * Result of an action's passive `status` probe. `state` drives the row\n * color (`ok` accent / `off` mute / `error` error); `message` is the\n * one-line text rendered in the row's value slot.\n */\nexport interface ExtensionConfigActionStatus {\n state: 'ok' | 'off' | 'error'\n message: string\n}\n\n/**\n * The value-holding config items — everything except `action`. Functions\n * that produce or validate persisted VALUES take this narrower union;\n * {@link ExtensionConfigItem} is the full declarable set.\n */\nexport type ExtensionConfigValueItem\n = | ExtensionConfigBooleanItem\n | ExtensionConfigNumberItem\n | ExtensionConfigStringItem\n | ExtensionConfigEnumItem\n | ExtensionConfigModelItem\n\n/**\n * Discriminated union of the typed config items an extension can declare.\n * The `type` tag drives both validation ({@link resolveExtensionConfigValue})\n * and rendering (toggle / choice / input / action row).\n */\nexport type ExtensionConfigItem = ExtensionConfigValueItem | ExtensionConfigActionItem\n\n/**\n * A config category — a labelled grouping the Settings UIs render as a\n * sub-header. Declared via `ctx.registerConfigCategory`. An item that\n * names no category lands in its extension's implicit category (id =\n * extension name); register a category with that id to give the implicit\n * grouping a friendlier label.\n */\nexport interface ExtensionConfigCategory {\n id: string\n label: string\n description?: string\n}\n\n/**\n * A config item as STORED on the registry / contributions — the authored\n * item plus the resolved `category` id it belongs to. The owning\n * extension is implied by the namespacing key on\n * {@link ExtensionRegistry.configItems}.\n */\nexport type StoredExtensionConfigItem = ExtensionConfigItem & { category: string }\n\n/** The `ctx.config` surface handed to extensions for reading their own config. */\nexport interface ExtensionConfigSurface {\n /**\n * Read the current value of one of THIS extension's config items,\n * default-first. Returns the persisted value when present + valid, the\n * item's `default` when missing / malformed, or `undefined` when `key`\n * was never registered via `ctx.registerConfigItem`.\n *\n * Reads through a host-supplied live settings getter: during `setup`\n * (before the host wires live settings) it returns the default (or the\n * boot snapshot); inside hooks / tools (post-boot) it returns the live\n * value.\n */\n get: (key: string) => ExtensionConfigValue | undefined\n}\n\n// ---------------------------------------------------------------------------\n// Authored-schema validation (registration-time guard)\n// ---------------------------------------------------------------------------\n\n/**\n * Exhaustiveness guard for the `ExtensionConfigItem` union: a future item\n * `type` left unhandled is a COMPILE error (the `never` parameter), while at\n * runtime an unrecognized / missing type is treated as invalid (`false`) —\n * malformed items reach here via `as never` casts from untyped `.js` extensions.\n */\nfunction unreachableInvalidItem(_item: never): false {\n return false\n}\n\n/**\n * Whether an authored item is well-formed for its declared `type` — used\n * by `ctx.registerConfigItem` to drop malformed declarations (wrong /\n * missing `default`, an `enum` without `options`) before they reach the\n * registry. Cheaper than letting a broken item ride to render time.\n */\nexport function isValidExtensionConfigItem(item: ExtensionConfigItem): boolean {\n switch (item.type) {\n case 'boolean':\n return typeof item.default === 'boolean'\n case 'number':\n // The default must be finite AND within the declared range, so the\n // resolver never surfaces an out-of-range value (the module's \"every read\n // yields a valid value\" invariant). `step` is a UI affordance, not a hard\n // constraint, so it is NOT enforced on the default.\n return typeof item.default === 'number'\n && Number.isFinite(item.default)\n && (item.min === undefined || item.default >= item.min)\n && (item.max === undefined || item.default <= item.max)\n case 'string':\n // A string `pattern` is intentionally NOT enforced against the default:\n // an empty / unset default (`default: ''`) for an optional patterned\n // field is a legitimate idiom. The pattern still gates user-entered\n // values at read time via `resolveExtensionConfigValue`. The author\n // `validate` is likewise deferred to read time, so registration stays\n // side-effect-free (no `validate(default)` call at register time).\n return typeof item.default === 'string'\n case 'enum':\n // The default MUST be one of the options — otherwise the picker can't\n // represent it and the resolver would surface an off-list value.\n return typeof item.default === 'string'\n && Array.isArray(item.options)\n && item.options.length > 0\n && item.options.every(o => o && typeof o.value === 'string' && typeof o.label === 'string')\n && item.options.some(o => o.value === item.default)\n case 'model':\n // Ids are free-form strings (catalogs are host- and auth-dependent),\n // so only the shape is checked — see the type's docstring for why\n // values are never catalog-validated.\n return typeof item.default === 'string'\n case 'action':\n // No `default` — requires a callable `run`; `status` is optional\n // but must be callable when present.\n return typeof item.run === 'function'\n && (item.status === undefined || typeof item.status === 'function')\n default:\n return unreachableInvalidItem(item)\n }\n}\n\n// ---------------------------------------------------------------------------\n// Validation + resolution — the single graceful-degradation chokepoint\n// ---------------------------------------------------------------------------\n\n/** Build a RegExp from `pattern`, returning `null` when the source is invalid. */\nfunction safeRegExp(pattern: string): RegExp | null {\n try {\n return new RegExp(pattern)\n }\n catch {\n return null\n }\n}\n\n/**\n * Whether `value` is a valid concrete value for `item` — type match plus\n * the item's constraints (number range, enum membership, string pattern)\n * and the optional author `validate`. `step` is intentionally NOT enforced\n * here (it's a UI affordance, not a hard constraint — a persisted value or\n * default that doesn't align to the step is still honored).\n */\nexport function isValidExtensionConfigValue(item: ExtensionConfigItem, value: unknown): value is ExtensionConfigValue {\n switch (item.type) {\n case 'boolean':\n if (typeof value !== 'boolean')\n return false\n break\n case 'number':\n if (typeof value !== 'number' || !Number.isFinite(value))\n return false\n if (item.min !== undefined && value < item.min)\n return false\n if (item.max !== undefined && value > item.max)\n return false\n break\n case 'string': {\n if (typeof value !== 'string')\n return false\n if (item.pattern) {\n const re = safeRegExp(item.pattern)\n // A broken author pattern is treated as \"no constraint\" rather\n // than rejecting every value the user types.\n if (re && !re.test(value))\n return false\n }\n break\n }\n case 'enum':\n if (typeof value !== 'string' || !item.options.some(o => o.value === value))\n return false\n break\n case 'model':\n if (typeof value !== 'string')\n return false\n break\n case 'action':\n // Actions hold no persisted value — nothing is ever valid for them.\n return false\n default:\n return unreachableInvalidItem(item)\n }\n if (item.validate) {\n try {\n return item.validate(value)\n }\n catch {\n return false\n }\n }\n return true\n}\n\n/**\n * Resolve the effective value of a config item from its persisted value.\n * THE single place graceful degradation lives: a `persisted` that's\n * `undefined` (never set) OR fails {@link isValidExtensionConfigValue}\n * collapses to `item.default`. Action items hold no value and resolve to\n * `undefined` — the overloads keep value-item call sites exact.\n */\nexport function resolveExtensionConfigValue(\n item: ExtensionConfigValueItem,\n persisted: unknown,\n): ExtensionConfigValue\nexport function resolveExtensionConfigValue(\n item: ExtensionConfigItem,\n persisted: unknown,\n): ExtensionConfigValue | undefined\nexport function resolveExtensionConfigValue(\n item: ExtensionConfigItem,\n persisted: unknown,\n): ExtensionConfigValue | undefined {\n if (item.type === 'action')\n return undefined\n if (persisted === undefined)\n return item.default\n if (!isValidExtensionConfigValue(item, persisted))\n return item.default\n return persisted\n}\n\n/**\n * Clamp a number to the item's `[min, max]` and snap to `step` (when set).\n * Used by the input-row commit paths so a user-typed value is coerced into\n * range before it's persisted.\n */\nexport function clampExtensionConfigNumber(item: ExtensionConfigNumberItem, n: number): number {\n let v = n\n if (item.min !== undefined)\n v = Math.max(item.min, v)\n if (item.max !== undefined)\n v = Math.min(item.max, v)\n if (item.step !== undefined && item.step > 0) {\n const base = item.min ?? 0\n v = base + Math.round((v - base) / item.step) * item.step\n // Snapping can nudge a boundary value out of range — re-clamp.\n if (item.max !== undefined)\n v = Math.min(item.max, v)\n if (item.min !== undefined)\n v = Math.max(item.min, v)\n }\n return v\n}\n\n/**\n * Coerce a raw input string (from a free number / string input row) into a\n * valid persisted value, or `undefined` when it can't be made valid.\n * Numbers are parsed + clamped; strings are taken verbatim and checked\n * against the pattern; enum / boolean items aren't edited via free input\n * (they use choice / toggle rows) so they return `undefined` here.\n */\nexport function coerceExtensionConfigInput(\n item: ExtensionConfigItem,\n raw: string,\n): ExtensionConfigValue | undefined {\n switch (item.type) {\n case 'number': {\n const n = Number(raw.trim())\n if (!Number.isFinite(n))\n return undefined\n const clamped = clampExtensionConfigNumber(item, n)\n return isValidExtensionConfigValue(item, clamped) ? clamped : undefined\n }\n case 'string':\n return isValidExtensionConfigValue(item, raw) ? raw : undefined\n case 'model':\n // Fallback path for hosts without a catalog — the value is typed as\n // free text (a comma-separated id list when `multi`).\n return isValidExtensionConfigValue(item, raw) ? raw : undefined\n default:\n // Enum / boolean use choice / toggle rows; actions have no value.\n return undefined\n }\n}\n\n/**\n * Convenience read against a merged {@link ExtensionRegistry} + the user's\n * `Settings`. Looks up the item schema (`extName → key`), reads the\n * persisted value out of `settings.extensionConfig`, and runs it through\n * {@link resolveExtensionConfigValue}. Returns `undefined` only when the\n * `(extName, key)` pair names no registered item.\n */\nexport function resolveExtensionConfig(\n registry: Pick<ExtensionRegistry, 'configItems'>,\n settings: Pick<Settings, 'extensionConfig'> | undefined,\n extName: string,\n key: string,\n): ExtensionConfigValue | undefined {\n const item = registry.configItems[extName]?.[key]\n if (!item)\n return undefined\n return resolveExtensionConfigValue(item, settings?.extensionConfig?.[extName]?.[key])\n}\n\n/**\n * Gate the `config` patch an action returned before the host persists it.\n * Per-entry, an item is accepted only when it names a registered VALUE\n * item of the declaring extension and passes that item's schema\n * ({@link isValidExtensionConfigValue}); everything else lands in\n * `rejected` for logging. Pure — the host applies `accepted` through its\n * own settings write path.\n */\nexport function sanitizeExtensionConfigActionPatch(\n items: Readonly<Record<string, StoredExtensionConfigItem>> | undefined,\n patch: Readonly<Record<string, ExtensionConfigValue>> | undefined,\n): { accepted: Record<string, ExtensionConfigValue>, rejected: string[] } {\n const accepted: Record<string, ExtensionConfigValue> = {}\n const rejected: string[] = []\n if (!patch || typeof patch !== 'object')\n return { accepted, rejected }\n for (const [key, value] of Object.entries(patch)) {\n const item = items?.[key]\n if (!item || item.type === 'action' || !isValidExtensionConfigValue(item, value)) {\n rejected.push(key)\n continue\n }\n accepted[key] = value\n }\n return { accepted, rejected }\n}\n\n// ---------------------------------------------------------------------------\n// Wire-safe serialization (GUI IPC)\n// ---------------------------------------------------------------------------\n\n/** Fields the wire shape adds on top of the authored item (minus `validate`). */\ninterface WireConfigExtras {\n category: string\n extension: string\n}\n\n/** A config item flattened for the wire — carries `category` + `extension`, drops `validate`. */\nexport type WireExtensionConfigItem\n = | (Omit<ExtensionConfigBooleanItem, 'validate'> & WireConfigExtras)\n | (Omit<ExtensionConfigNumberItem, 'validate'> & WireConfigExtras)\n | (Omit<ExtensionConfigStringItem, 'validate'> & WireConfigExtras)\n | (Omit<ExtensionConfigEnumItem, 'validate'> & WireConfigExtras)\n | (Omit<ExtensionConfigModelItem, 'validate'> & WireConfigExtras)\n\n/** The full extension-config schema in IPC-safe form. */\nexport interface WireExtensionConfigSchema {\n categories: readonly ExtensionConfigCategory[]\n items: readonly WireExtensionConfigItem[]\n}\n\n/**\n * Project the merged registry's config schema into a wire-safe shape:\n * categories as a flat array, items flattened to a single list carrying\n * their `extension` + `category`, with the non-serializable `validate`\n * callback stripped. Action items are omitted entirely — their `run`\n * callback only exists in the main process, so a remote renderer (the\n * GUI) has nothing it could do with the row. Pure — factored out so its\n * serialization is unit testable without an Electron / IPC harness.\n */\nexport function toWireConfigSchema(\n registry: Pick<ExtensionRegistry, 'configCategories' | 'configItems'>,\n): WireExtensionConfigSchema {\n const items: WireExtensionConfigItem[] = []\n for (const [extension, byKey] of Object.entries(registry.configItems)) {\n for (const stored of Object.values(byKey)) {\n if (stored.type === 'action')\n continue\n // Drop the main-only `validate` callback; keep everything else.\n const { validate: _validate, ...rest } = stored\n items.push({ ...rest, extension } as WireExtensionConfigItem)\n }\n }\n return {\n categories: Object.values(registry.configCategories),\n items,\n }\n}\n","/**\n * `defineExtension` — identity helper for authoring extensions.\n *\n * Mirrors {@link definePreset} / {@link defineSkill}: returns its\n * argument verbatim, but its presence in the source gives TypeScript\n * an inference anchor and the import surface a stable name.\n *\n * Authoring example (drop in `.zidane/extensions/hello/index.ts`):\n *\n * ```ts\n * import { defineExtension } from 'zidane'\n *\n * export default defineExtension({\n * name: 'hello',\n * version: '0.1.0',\n * setup(ctx) {\n * ctx.registerHook('tool:after', ({ name }) =>\n * ctx.logger.info('tool ran', { name }))\n * },\n * })\n * ```\n *\n * No validation runs here — that lives in the loader so a malformed\n * definition surfaces in the Settings panel rather than throwing\n * during a module's top-level evaluation.\n *\n * **Zero-import alternative.** The loader accepts a plain object too,\n * so extensions running against the compiled `zidane-tui` binary\n * (which doesn't expose `node_modules/zidane` to dynamic imports\n * outside the bundle) can skip the import entirely:\n *\n * ```ts\n * // .zidane/extensions/hello/index.ts — no imports needed\n * export default {\n * name: 'hello',\n * setup(ctx) {\n * ctx.registerHook('tool:after', e => ctx.logger.info('ran', e))\n * },\n * }\n * ```\n *\n * You lose TypeScript inference on `ctx` this way; pair with\n * `/// <reference types=\"zidane\" />` or an `import type { ExtensionContext } from 'zidane'`\n * (type-only imports don't need a runtime resolution) to recover it.\n */\n\nimport type { ExtensionDefinition } from './types'\n\nexport function defineExtension(definition: ExtensionDefinition): ExtensionDefinition {\n return definition\n}\n","/**\n * Internal: race a promise against a timeout. Shared by the discovery\n * loader (module imports) and the registry builder (extension `setup()`).\n * The timer is always cleared so a settled promise doesn't pin the\n * event loop for the full timeout.\n */\nexport function withTimeout<T>(\n promise: Promise<T>,\n timeoutMs: number,\n message: string,\n onTimeout?: () => void,\n): Promise<T> {\n let timer: ReturnType<typeof setTimeout> | undefined\n const timeout = new Promise<never>((_, reject) => {\n timer = setTimeout(() => {\n onTimeout?.()\n reject(new Error(message))\n }, timeoutMs)\n })\n return Promise.race([promise, timeout]).finally(() => {\n if (timer)\n clearTimeout(timer)\n })\n}\n","/**\n * Extension discovery.\n *\n * Walks the standard project + user roots\n * (`{project,user}/.{agents,zidane}/extensions/<name>/`) using the\n * shared {@link projectUserPaths} convention, then dynamic-imports only\n * trusted `<name>/index.{ts,tsx,mjs,js}` files (probed in\n * that preference order — see {@link ENTRY_CANDIDATES}). The first valid\n * module per name wins — project beats user, `.agents/` beats\n * `.<prefix>/` within a single scope (see `projectUserPaths` for the\n * full order).\n *\n * Loading uses native `await import(pathToFileURL(...))` which Bun\n * handles for both `.ts`/`.tsx` and JS variants in `bun run` and in\n * `bun build --compile` binaries. No transpiler dependency is needed.\n *\n * Before each import the extension directory's module graph is EVICTED\n * from Bun's module cache (`require.cache` deletion covers ESM too), so\n * re-running discovery — the `/reload` command, a cwd-change rebuild, an\n * overwrite-install — loads the current on-disk code, deep imports\n * included. Module-level state resets per reload; the teardown contract\n * covers cleanup of the outgoing instance.\n *\n * Failures are isolation-first: a broken module produces a\n * {@link LoadedExtension} with `error` set instead of throwing — one\n * vibe-coded syntax error mustn't keep the TUI from booting.\n */\n\nimport type { ExtensionDefinition, ExtensionManifest, ExtensionSource, LoadedExtension } from './types'\nimport { lstatSync, readdirSync, readFileSync, realpathSync } from 'node:fs'\nimport { createRequire } from 'node:module'\nimport { delimiter, dirname, resolve, sep } from 'node:path'\nimport { pathToFileURL } from 'node:url'\nimport { projectUserPaths } from '../chat/project-user-paths'\nimport { errorMessage } from '../errors'\nimport { withTimeout } from './with-timeout'\n\n// ---------------------------------------------------------------------------\n// Constants\n// ---------------------------------------------------------------------------\n\n/**\n * Entry-point filenames probed inside each `<name>/` directory, in\n * preference order. Bun loads `.ts` / `.tsx` natively; `.mjs` / `.js`\n * are accepted so pre-compiled extensions (or vibe-coded JS-only ones)\n * work too. `index.js` is last because most authors will reach for\n * `.ts` first.\n */\nconst ENTRY_CANDIDATES = ['index.ts', 'index.tsx', 'index.mjs', 'index.js'] as const\nconst DANGEROUS_RECORD_KEYS = new Set(['__proto__', 'prototype', 'constructor'])\n\n/** A discovered (but not yet loaded) extension entry. */\ninterface ExtensionScanEntry {\n /** Folder name — the canonical persisted extension identity. */\n name: string\n /** Absolute path to the extension directory. */\n dir: string\n /** Absolute path to the entry file picked from `ENTRY_CANDIDATES`. */\n entry: string\n manifest?: ExtensionManifest\n /** Where the discovery root lives. */\n source: ExtensionSource\n}\n\ninterface ExtensionScanRoot {\n path: string\n source: 'project' | 'user'\n}\n\n// ---------------------------------------------------------------------------\n// Path resolution\n// ---------------------------------------------------------------------------\n\n/**\n * Default scan roots for extension discovery. Mirrors\n * {@link defaultSkillScanPaths} / {@link defaultMcpsConfigPaths}; the\n * subPath is `'extensions'` so each root expands to\n * `<scope>/.<convention>/extensions/`.\n */\nexport function defaultExtensionScanPaths(opts: {\n cwd?: string\n home?: string\n prefix?: string\n} = {}): ExtensionScanRoot[] {\n return projectUserPaths({ subPath: 'extensions', ...opts })\n}\n\n/**\n * Parse a platform-delimited list of additional extension scan roots.\n * Useful for local dogfooding / host-provided packages:\n *\n * ZIDANE_EXTENSION_DIRS=/repo/extensions bun run tui\n *\n * Each path is a scan ROOT whose children are extension dirs/packages\n * (`<root>/<name>/index.ts`), not a single extension directory.\n */\nexport function parseExtensionScanPathEnv(value: string | undefined = process.env.ZIDANE_EXTENSION_DIRS): string[] {\n if (!value)\n return []\n return value.split(delimiter).map(p => p.trim()).filter(Boolean)\n}\n\n/**\n * Walk every scan root, collecting one {@link ExtensionScanEntry} per\n * `<root>/<name>/index.*` hit. First-found per `name` wins (matches the\n * project-user precedence used elsewhere — see `projectUserPaths` JSDoc).\n *\n * Non-existent roots are skipped silently. Stat errors inside a present\n * root are surfaced under `ZIDANE_DEBUG` only — they shouldn't bring\n * boot to a halt.\n */\nexport function scanExtensionDirectories(opts: {\n cwd?: string\n home?: string\n prefix?: string\n extensionScanPaths?: readonly string[]\n} = {}): ExtensionScanEntry[] {\n const seen = new Map<string, ExtensionScanEntry>()\n for (const entry of scanAllExtensionDirectories(opts)) {\n if (!seen.has(entry.name))\n seen.set(entry.name, entry)\n }\n return [...seen.values()]\n}\n\nfunction scanAllExtensionDirectories(opts: {\n cwd?: string\n home?: string\n prefix?: string\n extensionScanPaths?: readonly string[]\n} = {}): ExtensionScanEntry[] {\n const roots: ExtensionScanRoot[] = [\n ...(opts.extensionScanPaths ?? []).map(path => ({ path: resolve(path), source: 'user' as const })),\n ...defaultExtensionScanPaths(opts),\n ]\n const entriesOut: ExtensionScanEntry[] = []\n\n for (const root of roots) {\n try {\n // The scan root and its parent (`.zidane` / `.agents`) are part of\n // the trust boundary too — a symlinked parent would let every entry\n // resolve outside the configured roots while the per-entry lstat\n // checks below still pass.\n if (lstatSync(dirname(root.path)).isSymbolicLink())\n continue\n if (!lstatSync(root.path).isDirectory())\n continue\n }\n catch {\n continue\n }\n let realRoot: string\n try {\n realRoot = realpathSync(root.path)\n }\n catch (err) {\n debugLog(`realpath ${root.path}`, err)\n continue\n }\n let entries: string[]\n try {\n entries = readdirSync(root.path)\n }\n catch (err) {\n debugLog(`readdir ${root.path}`, err)\n continue\n }\n for (const name of entries) {\n if (name.startsWith('.'))\n continue\n const dir = resolve(root.path, name)\n try {\n // Extension roots and entry files are trust boundaries. Do not follow\n // symlinks out of the configured scan roots.\n if (!lstatSync(dir).isDirectory())\n continue\n }\n catch (err) {\n debugLog(`stat ${dir}`, err)\n continue\n }\n const entry = findEntryFile(dir)\n if (!entry)\n continue\n // Containment: the canonical entry file must stay under the\n // canonical scan root. Catches symlinks at any level the lstat\n // checks above didn't cover.\n try {\n const realEntry = realpathSync(entry)\n if (!realEntry.startsWith(realRoot + sep)) {\n debugLog(`skip ${entry}`, new Error(`resolves outside scan root ${realRoot}`))\n continue\n }\n }\n catch (err) {\n debugLog(`realpath ${entry}`, err)\n continue\n }\n entriesOut.push({ name, dir, entry, source: root.source, ...readExtensionManifest(dir) })\n }\n }\n return entriesOut\n}\n\n/**\n * Pick the first existing `index.*` file inside `dir`, or `null` if none.\n * Exported for the installer, which probes fetched trees with the same rule.\n */\nexport function findEntryFile(dir: string): string | null {\n for (const candidate of ENTRY_CANDIDATES) {\n const full = resolve(dir, candidate)\n try {\n if (lstatSync(full).isFile())\n return full\n }\n catch {\n continue\n }\n }\n return null\n}\n\n// ---------------------------------------------------------------------------\n// Loading\n// ---------------------------------------------------------------------------\n\n/**\n * Discover + load every trusted extension reachable from the default\n * scan paths — including ones the user's enable allowlist has toggled\n * OFF. Disabled extensions still import (and later run `setup()`) so\n * their config schema is browsable in Settings before enabling; the\n * allowlist gates their CONTRIBUTIONS at registry level, not their code.\n * The trust boundary stays with `trustProjectExtensions`. Returns one {@link LoadedExtension} per scan entry:\n * skipped entries have no definition, failed entries carry `error`.\n * Loaders never throw — the host can decide what to do with load errors\n * (typically: ship them to the Settings panel).\n *\n * Imports run in parallel: a slow extension can't block the rest. The\n * returned array preserves the original scan order (project-first,\n * then user) so first-found-wins precedence stays intact.\n *\n * `signal` is honored opportunistically — if the abort fires BEFORE\n * we dispatch imports, the whole set is skipped. Once `Promise.all` is\n * in flight, individual imports can't be cancelled (the module system\n * has no abort affordance).\n *\n * Project-scoped extensions execute arbitrary local code, so they're\n * skipped unless `trustProjectExtensions` resolves to `true` — either\n * via the option or the `ZIDANE_TRUST_PROJECT_EXTENSIONS=1` env var\n * fallback. User-scoped extensions always load.\n *\n * Each extension's module graph is evicted from the runtime cache\n * before import, so calling this again (e.g. `/reload`) re-evaluates\n * the current on-disk code — edits, overwrites, and updates apply\n * without a restart.\n */\nexport async function discoverExtensions(opts: {\n cwd?: string\n home?: string\n prefix?: string\n extensionScanPaths?: readonly string[]\n signal?: AbortSignal\n trustProjectExtensions?: boolean\n importTimeoutMs?: number\n} = {}): Promise<LoadedExtension[]> {\n const entries = selectExtensionEntries(scanAllExtensionDirectories(opts), opts)\n if (opts.signal?.aborted || entries.length === 0)\n return []\n return Promise.all(entries.map((entry) => {\n if (!('entry' in entry))\n return Promise.resolve(entry)\n return loadExtensionFromDisk(entry, opts.importTimeoutMs ?? 10_000)\n }))\n}\n\nfunction selectExtensionEntries(\n entries: readonly ExtensionScanEntry[],\n opts: { trustProjectExtensions?: boolean },\n): Array<ExtensionScanEntry | LoadedExtension> {\n // Safe by default: project extensions are untrusted unless the host\n // opts in explicitly or the user sets the env var the error message\n // below points at.\n const trustProjectExtensions = opts.trustProjectExtensions\n ?? (process.env.ZIDANE_TRUST_PROJECT_EXTENSIONS === '1')\n const selected = new Map<string, ExtensionScanEntry | LoadedExtension>()\n const locked = new Set<string>()\n\n for (const entry of entries) {\n if (locked.has(entry.name))\n continue\n if (entry.source === 'project' && !trustProjectExtensions) {\n if (!selected.has(entry.name)) {\n selected.set(entry.name, {\n name: entry.name,\n dir: entry.dir,\n source: entry.source,\n ...(entry.manifest ? { manifest: entry.manifest } : {}),\n error: 'Project extensions are disabled until this project is trusted. Set ZIDANE_TRUST_PROJECT_EXTENSIONS=1 or pass trustProjectExtensions to enable them.',\n })\n }\n continue\n }\n selected.set(entry.name, entry)\n locked.add(entry.name)\n }\n\n return [...selected.values()]\n}\n\nconst requireForCache = createRequire(import.meta.url)\n\n/**\n * Monotonic import generation — appended as a query so each discovery\n * pass gets a fresh ENTRY module. The query alone is not enough: relative\n * imports resolve to plain file paths and hit the cache, which is why\n * {@link evictModuleGraph} runs first. Both together are what makes\n * `/reload` a true hot-reload (verified empirically: query-only keeps\n * deep imports stale; eviction-only keeps the entry stale).\n */\nlet importGeneration = 0\n\n/**\n * Evict everything under `dir` from Bun's module cache (`require.cache`\n * deletion covers the ES registry for these keys). This refreshes the\n * extension's RELATIVE import graph; the entry itself is refreshed by the\n * generation query above. The extension's own `node_modules` are evicted\n * too so a dependency bump applies on update without a restart.\n */\nfunction evictModuleGraph(dir: string): void {\n const cache = requireForCache.cache\n if (!cache)\n return\n // Cache keys are PHYSICAL paths — realpath the dir or the prefix match\n // silently misses through symlinks (macOS `/var` → `/private/var`).\n let physical: string\n try {\n physical = realpathSync(dir)\n }\n catch {\n physical = dir\n }\n for (const root of physical === dir ? [dir] : [dir, physical]) {\n const prefix = root.endsWith(sep) ? root : root + sep\n for (const key of Object.keys(cache)) {\n if (key.startsWith(prefix))\n delete cache[key]\n }\n }\n}\n\n/**\n * Import + validate a single extension entry. Always returns a\n * {@link LoadedExtension} — the load error (if any) lives on the\n * returned object, never as a thrown exception.\n */\nasync function loadExtensionFromDisk(entry: ExtensionScanEntry, timeoutMs: number): Promise<LoadedExtension> {\n try {\n evictModuleGraph(entry.dir)\n importGeneration += 1\n const mod = await withTimeout(\n import(`${pathToFileURL(entry.entry).href}?zidane-gen=${importGeneration}`) as Promise<unknown>,\n timeoutMs,\n `Timed out importing ${entry.entry} after ${timeoutMs}ms. The module's top-level code may still be running — imports cannot be cancelled.`,\n )\n const definition = pickExtensionDefinition(mod)\n if (!definition) {\n return {\n name: entry.name,\n dir: entry.dir,\n source: entry.source,\n ...(entry.manifest ? { manifest: entry.manifest } : {}),\n error: `Module at ${entry.entry} does not export an ExtensionDefinition (expected \\`default\\` or named \\`extension\\`).`,\n }\n }\n if (entry.manifest?.apiVersion !== undefined && entry.manifest.apiVersion !== 1) {\n return {\n name: entry.name,\n dir: entry.dir,\n source: entry.source,\n manifest: entry.manifest,\n error: `Unsupported extension API version \"${entry.manifest.apiVersion}\". This runtime supports apiVersion 1.`,\n }\n }\n if (definition.apiVersion !== undefined && definition.apiVersion !== 1) {\n return {\n name: entry.name,\n dir: entry.dir,\n source: entry.source,\n ...(entry.manifest ? { manifest: entry.manifest } : {}),\n error: `Unsupported extension API version \"${definition.apiVersion}\". This runtime supports apiVersion 1.`,\n }\n }\n if (definition.name !== entry.name) {\n return {\n name: entry.name,\n dir: entry.dir,\n source: entry.source,\n ...(entry.manifest ? { manifest: entry.manifest } : {}),\n error: `Extension name mismatch: folder is \"${entry.name}\" but module exports name \"${definition.name}\". Rename one so the persisted identity is stable.`,\n }\n }\n if (entry.manifest?.name && entry.manifest.name !== entry.name) {\n return {\n name: entry.name,\n dir: entry.dir,\n source: entry.source,\n manifest: entry.manifest,\n error: `Extension manifest name mismatch: folder is \"${entry.name}\" but manifest declares \"${entry.manifest.name}\".`,\n }\n }\n return {\n definition,\n name: entry.name,\n dir: entry.dir,\n source: entry.source,\n ...(entry.manifest ? { manifest: entry.manifest } : {}),\n }\n }\n catch (err) {\n return {\n name: entry.name,\n dir: entry.dir,\n source: entry.source,\n ...(entry.manifest ? { manifest: entry.manifest } : {}),\n error: `Failed to import ${entry.entry}: ${errorMessage(err)}`,\n }\n }\n}\n\n/**\n * Extract the `ExtensionDefinition` from an imported module. Accepts\n * either the `default` export (preferred) or a named `extension`\n * export. Anything else — including a module that happens to have\n * top-level `name` + `setup` exports — is rejected so the loader's\n * surface stays unambiguous.\n */\nfunction pickExtensionDefinition(mod: unknown): ExtensionDefinition | null {\n if (!mod || typeof mod !== 'object')\n return null\n const m = mod as Record<string, unknown>\n if (isExtensionDefinition(m.default))\n return m.default\n if (isExtensionDefinition(m.extension))\n return m.extension\n return null\n}\n\n/**\n * Structural type-guard for an {@link ExtensionDefinition}. Kept lax:\n * `name` + `setup` are required; everything else is optional. A non-\n * function `setup` is rejected outright so the loader doesn't have\n * to second-guess it later.\n */\nexport function isExtensionDefinition(value: unknown): value is ExtensionDefinition {\n if (!value || typeof value !== 'object')\n return false\n const v = value as Record<string, unknown>\n if (typeof v.name !== 'string' || v.name.length === 0)\n return false\n if (DANGEROUS_RECORD_KEYS.has(v.name))\n return false\n if (typeof v.setup !== 'function')\n return false\n return true\n}\n\n/**\n * Tag an inline (host-passed) extension into the same shape disk-\n * loaded ones produce, so host-bundled extensions (e.g. those passed\n * via `RunTuiOptions.extensions`) flow through `buildExtensionRegistry`\n * identically to disk-discovered ones.\n */\nexport function inlineLoadedExtension(definition: ExtensionDefinition): LoadedExtension {\n return {\n definition,\n name: definition.name,\n source: 'inline',\n }\n}\n\n/**\n * Read `extension.json` (or `package.json#zidane`) as a shape-validated\n * manifest. Exported for the installer so \"what counts as a manifest\" has\n * exactly one definition — a malformed manifest is ignored identically at\n * install-validation time and at discovery time.\n */\nexport function readExtensionManifest(dir: string): { manifest?: ExtensionManifest } {\n const fromExtensionJson = readJson(resolve(dir, 'extension.json'))\n if (isExtensionManifest(fromExtensionJson))\n return { manifest: fromExtensionJson }\n const pkg = readJson(resolve(dir, 'package.json'))\n if (pkg && typeof pkg === 'object' && !Array.isArray(pkg)) {\n const manifest = (pkg as { zidane?: unknown }).zidane\n if (isExtensionManifest(manifest))\n return { manifest }\n }\n return {}\n}\n\nfunction readJson(path: string): unknown {\n try {\n return JSON.parse(readFileSync(path, 'utf8')) as unknown\n }\n catch {\n return null\n }\n}\n\nfunction isExtensionManifest(value: unknown): value is ExtensionManifest {\n if (!value || typeof value !== 'object' || Array.isArray(value))\n return false\n const v = value as Record<string, unknown>\n // `apiVersion`, when present, must be a number. We still recognize an\n // unsupported version (e.g. 2) as a manifest so the loader can surface a\n // clear \"unsupported API version\" error rather than silently ignoring it —\n // but the rest of the shape is still validated.\n if (v.apiVersion !== undefined && typeof v.apiVersion !== 'number')\n return false\n const isStringArray = (x: unknown): boolean => Array.isArray(x) && x.every(el => typeof el === 'string')\n return (v.name === undefined || typeof v.name === 'string')\n && (v.displayName === undefined || typeof v.displayName === 'string')\n && (v.description === undefined || typeof v.description === 'string')\n // Element-checked, not just Array.isArray — the predicate claims\n // `ExtensionManifest` whose lists are `string[]`, so honor that claim.\n && (v.permissions === undefined || isStringArray(v.permissions))\n && (v.capabilities === undefined || isStringArray(v.capabilities))\n}\n\n// ---------------------------------------------------------------------------\n// Internals\n// ---------------------------------------------------------------------------\n\nfunction debugLog(label: string, err: unknown): void {\n if (process.env.ZIDANE_DEBUG)\n process.stderr.write(`[zidane/extensions] ${label}: ${errorMessage(err)}\\n`)\n}\n\n/** Re-export for tests / advanced hosts that want to log the scan results before loading. */\nexport type { ExtensionScanEntry }\n/** Re-export so callers can read the canonical entry file list. */\nexport { ENTRY_CANDIDATES }\n","/**\n * Keybindings — a single source of truth for the app's global shortcuts.\n *\n * Every customizable action is declared once in {@link KEYBINDING_DEFS}\n * with its default trigger, label, and description. The TUI imports\n * `DEFAULT_KEYBINDINGS` for the baked-in defaults; on launch we load\n * `<userDir>/keybindings.json` (when present) and merge user overrides\n * on top so the user can rebind any action without recompiling.\n *\n * Scope (deliberate):\n *\n * - We expose only the **global, action-opening** shortcuts here —\n * \"open the Settings modal\", \"cycle agent profile\", \"enter\n * select-turn mode\", etc. Modal-internal letter shortcuts (`f`\n * fork, `d` delete, `c` copy on the turn-details modal) and\n * universal navigation (`↑/↓/↵/⎋`) stay hardcoded — they live\n * too close to a renderable's input contract to remap safely.\n *\n * - Storage lives at the **user level** (`<userDir>/keybindings.json`),\n * never inside a project's `.{prefix}/`. Shortcut muscle memory is\n * a per-user concern, not a per-repo one.\n *\n * Format on disk: JSON with `//` and `/* … *\\/` comments allowed\n * (parsed via a small JSONC stripper). Each entry maps an action id\n * to a binding spec string. Specs are parsed by {@link parseBindingSpec};\n * unknown / malformed entries fall back to the default for that action\n * (with a debug log under `ZIDANE_DEBUG`) so a syntax slip never bricks\n * the TUI.\n */\n\nimport { existsSync, readFileSync } from 'node:fs'\nimport { resolve } from 'node:path'\nimport { writeFileAtomic } from '../atomic-write'\n\n// ---------------------------------------------------------------------------\n// Action catalog\n// ---------------------------------------------------------------------------\n\n/**\n * Every action the keybindings system can drive. Adding a new action\n * here forces every consumer (TUI keyboard handler, hint rows, settings\n * UI) to handle it, and ships a default that the user can override.\n */\nexport type KeyAction\n // Global action shortcuts — fire from the top-level keyboard\n // handler in `app.tsx`, gated on screen / busy state.\n = | 'openSettings'\n | 'openSessionDetails'\n | 'newSession'\n | 'openModelPicker'\n | 'openEffortPicker'\n | 'openTodos'\n | 'openContextPanel'\n | 'openKeybindings'\n | 'cycleAgent'\n | 'enterSelectTurnMode'\n | 'cancelToolCall'\n | 'changeCwd'\n | 'attachClipboardImage'\n | 'openExternalEditor'\n // Message-queue navigation — only meaningful when the user has\n // type-ahead prompts waiting in the queue box above the prompt\n // input. `enterQueueSelection` moves the focus from the textarea\n // into the queue; while there, `pushQueuedMessage` (steers the\n // selected entry into the live run) and `dropQueuedMessage`\n // (removes the entry) act on the highlighted row.\n | 'enterQueueSelection'\n | 'pushQueuedMessage'\n | 'dropQueuedMessage'\n // Turn-details modal actions (`ctrl+s` select-turn mode → enter).\n // Each binding only matches while that modal is mounted, so the\n // single-letter defaults can clash with letters used elsewhere\n // without conflict.\n | 'turnFork'\n | 'turnDelete'\n | 'turnCopy'\n | 'turnEdit'\n // Session-details modal actions (opened via `ctrl+shift+x`).\n | 'sessionDelete'\n | 'sessionCopyId'\n | 'sessionGenerateTitle'\n | 'sessionExportMarkdown'\n | 'sessionExportJson'\n | 'sessionCompact'\n\n/** Resolved keybindings — every action has a current spec string. */\nexport type KeyBindings = Record<KeyAction, string>\n\n/**\n * Resolved spec string for one extension-contributed keybinding. Same\n * shape as a built-in {@link KeyBindings} entry but the `id` is\n * extension-owned (no `KeyAction` union member). The TUI dispatcher\n * iterates these alongside the built-ins; user overrides flow through\n * the same `keybindings.json` file by matching on `id`.\n */\nexport interface ResolvedExtensionKeybinding {\n /** Extension-owned id (matches `ExtensionKeybinding.id`). */\n id: string\n /** Active spec string — user override when present, default otherwise. May be empty (= unbound). */\n spec: string\n}\n\n/**\n * Logical grouping for one action — used by the keybindings panel to\n * render section headers. Stable strings (not an enum) so future\n * groups can be added without churning every existing entry.\n */\nexport type KeyBindingGroup\n = | 'Global'\n | 'Message queue'\n | 'Turn details'\n | 'Session details'\n\n/** Static metadata for one action — used by the loader, hint rows, settings UI. */\nexport interface KeyBindingDef {\n /** Stable identifier — JSON key + TS union member. */\n action: KeyAction\n /** Default trigger spec (e.g. `'ctrl+o'`). */\n default: string\n /** Short label used in shortcut hint rows. */\n label: string\n /** Longer description for the settings UI and the annotated JSON file. */\n description: string\n /**\n * Group label rendered as a section header in the keybindings panel.\n * Items are listed in {@link KEYBINDING_DEFS} order; the panel just\n * inserts a header whenever this field changes between adjacent\n * entries — so groups stay contiguous and the order here is the\n * order on screen.\n */\n group: KeyBindingGroup\n}\n\n/**\n * The canonical action catalog. Order is the source of truth for the\n * generated keybindings file and the settings UI.\n */\nexport const KEYBINDING_DEFS: readonly KeyBindingDef[] = [\n {\n action: 'openSettings',\n default: 'ctrl+o',\n label: 'settings',\n description: 'open the Settings modal (toggles, theme, keybindings)',\n group: 'Global',\n },\n {\n // ctrl+shift+x keeps ctrl+x free (the conventional \"cut\" key in most\n // editors). Like the context panel, it relies on the kitty keyboard\n // protocol to stay distinct from ctrl+x; on terminals without it\n // (e.g. macOS Terminal.app) shift is dropped and it collapses to\n // ctrl+x — rebind there.\n action: 'openSessionDetails',\n default: 'ctrl+shift+x',\n label: 'session',\n description: 'open the session details modal (stats, export, delete, rename)',\n group: 'Global',\n },\n {\n action: 'newSession',\n default: 'ctrl+n',\n label: 'new session',\n description: 'start a fresh chat session with the active provider/model (same as the \"+ new session\" row)',\n group: 'Global',\n },\n {\n action: 'openModelPicker',\n default: 'ctrl+m',\n label: 'model',\n description: 'open the cross-provider model picker',\n group: 'Global',\n },\n {\n action: 'openEffortPicker',\n default: 'ctrl+l',\n label: 'options',\n description: 'open the model-options picker — reasoning effort + feature toggles (e.g. fast mode)',\n group: 'Global',\n },\n {\n action: 'openTodos',\n default: 'ctrl+t',\n label: 'todos',\n description: 'open the active run\\'s todo list (the agent\\'s `todowrite` checkpoints)',\n group: 'Global',\n },\n {\n // ctrl+shift+g leans on the kitty keyboard protocol (the renderer\n // enables it) to stay distinct from ctrl+g (`openExternalEditor`). On\n // terminals without it — e.g. macOS Terminal.app — shift is dropped and\n // the keypress collapses to ctrl+g (the editor); rebind there.\n action: 'openContextPanel',\n default: 'ctrl+shift+g',\n label: 'context',\n description: 'open the context-usage breakdown (what is taking up the context window)',\n group: 'Global',\n },\n {\n action: 'openKeybindings',\n default: 'ctrl+y',\n label: 'keybindings',\n description: 'open the keybindings panel — list every shortcut and jump to the keybindings.json file',\n group: 'Global',\n },\n {\n action: 'cycleAgent',\n default: 'shift+tab',\n label: 'cycle agent',\n description: 'cycle to the next agent profile (when multiple profiles are registered)',\n group: 'Global',\n },\n {\n action: 'enterSelectTurnMode',\n default: 'ctrl+s',\n label: 'messages',\n description: 'enter select-turn mode to navigate previous messages',\n group: 'Global',\n },\n {\n action: 'cancelToolCall',\n default: 'ctrl+k',\n label: 'cancel tool',\n description: 'open the in-flight tool picker to cancel a single tool call without aborting the run (esc still aborts the whole run)',\n group: 'Global',\n },\n {\n action: 'changeCwd',\n default: 'ctrl+h',\n label: 'cwd',\n description: 'open the directory picker to change the working directory',\n group: 'Global',\n },\n {\n action: 'attachClipboardImage',\n default: 'ctrl+v',\n label: 'paste image',\n description: 'attach an image from the OS clipboard to the prompt (needs a platform helper: pngpaste on macOS, wl-clipboard/xclip on Linux)',\n group: 'Global',\n },\n {\n // Handled inside `PromptBlock`'s onKeyDown (it owns the textarea ref),\n // NOT the App-level keyboard handler — so it only ever fires while the\n // prompt composer is focused. Gated by the `externalEditor` setting:\n // disabled there, the key falls through to its normal textarea behavior.\n // ctrl+g (Claude Code's muscle-memory key) is universally reliable; the\n // context panel moved to ctrl+shift+g to free it up.\n action: 'openExternalEditor',\n default: 'ctrl+g',\n label: 'edit in $EDITOR',\n description: 'open the current prompt in $EDITOR / $VISUAL, reloading it when you save & quit (git-style); requires the \"Edit prompt in $EDITOR\" setting',\n group: 'Global',\n },\n // Message-queue navigation — only matches when the queue box above\n // the prompt has at least one entry. Defaults pick keys the prompt\n // textarea doesn't already claim (no default for enter — see below).\n //\n // `enterQueueSelection` is intentionally unbound by default: the\n // natural way to enter the queue is to press plain `↑` on an empty\n // prompt buffer (handled in `PromptBlock`'s onKeyDown), which doesn't\n // need a global shortcut. The action stays in the catalog so users\n // who want a dedicated key — e.g. for entering the queue WITHOUT\n // emptying their draft first — can rebind it in `keybindings.json`.\n // We don't ship a default because no arrow / modifier combo is both\n // free in OpenTUI's textarea AND forwarded by every common terminal\n // (ctrl+arrow → macOS Mission Control; cmd+arrow → terminal scroll /\n // tab nav; alt+arrow → terminal word-nav).\n {\n action: 'enterQueueSelection',\n default: '',\n label: 'select queue',\n description: 'move focus from the prompt textarea into the type-ahead queue box (also: ↑ on an empty prompt)',\n group: 'Message queue',\n },\n {\n action: 'pushQueuedMessage',\n default: 'ctrl+return',\n label: 'push',\n description: 'steer the selected queued message into the live run (delivered between tool calls)',\n group: 'Message queue',\n },\n {\n action: 'dropQueuedMessage',\n default: 'backspace',\n label: 'drop',\n description: 'remove the selected queued message — exits back to the prompt when the queue empties (the big \"remove\" key works everywhere: backspace on Win/Linux, the laptop key labeled \"delete\" on Mac, which sends backspace)',\n group: 'Message queue',\n },\n // Turn-details modal — only fires while that modal is mounted.\n {\n action: 'turnFork',\n default: 'f',\n label: 'fork',\n description: 'fork the session at the selected turn (two-press confirm)',\n group: 'Turn details',\n },\n {\n action: 'turnDelete',\n default: 'd',\n label: 'delete',\n description: 'delete the selected turn (two-press confirm)',\n group: 'Turn details',\n },\n {\n action: 'turnCopy',\n default: 'c',\n label: 'copy',\n description: 'copy the selected turn content to the clipboard (OSC 52)',\n group: 'Turn details',\n },\n {\n action: 'turnEdit',\n default: 'e',\n label: 'edit',\n description: 'edit the text content of the selected turn',\n group: 'Turn details',\n },\n // Session-details modal — only fires while that modal is mounted.\n {\n action: 'sessionDelete',\n default: 'd',\n label: 'delete',\n description: 'delete the entire session (two-press confirm)',\n group: 'Session details',\n },\n {\n action: 'sessionCopyId',\n default: 'c',\n label: 'copy id',\n description: 'copy the full session id to the clipboard (OSC 52)',\n group: 'Session details',\n },\n {\n action: 'sessionGenerateTitle',\n default: 'g',\n label: 'generate title',\n description: 'generate a title for the session via the active provider/model',\n group: 'Session details',\n },\n {\n action: 'sessionExportMarkdown',\n default: 'e',\n label: 'export md',\n description: 'export the session as Markdown under .{prefix}/sessions/',\n group: 'Session details',\n },\n {\n action: 'sessionExportJson',\n default: 'j',\n label: 'export json',\n description: 'export the session as JSON under .{prefix}/sessions/',\n group: 'Session details',\n },\n {\n action: 'sessionCompact',\n default: 'k',\n label: 'compact',\n description: 'compact older turns via an LLM summary (model still sees everything from the boundary down)',\n group: 'Session details',\n },\n]\n\n/** Index by action for O(1) lookup of label / description / default. */\nexport const KEYBINDING_DEF_BY_ACTION: Readonly<Record<KeyAction, KeyBindingDef>>\n = KEYBINDING_DEFS.reduce(\n (acc, def) => Object.assign(acc, { [def.action]: def }),\n {} as Record<KeyAction, KeyBindingDef>,\n )\n\n/** Defaults, derived from {@link KEYBINDING_DEFS} so the two never drift. */\nexport const DEFAULT_KEYBINDINGS: KeyBindings\n = KEYBINDING_DEFS.reduce(\n (acc, def) => Object.assign(acc, { [def.action]: def.default }),\n {} as KeyBindings,\n )\n\n// ---------------------------------------------------------------------------\n// Spec parsing + matching\n// ---------------------------------------------------------------------------\n\n/** Parsed shape of a `\"ctrl+shift+o\"` spec. */\nexport interface ParsedBinding {\n ctrl: boolean\n shift: boolean\n alt: boolean\n meta: boolean\n /** The key name (e.g. `'o'`, `'tab'`, `'escape'`). Always lowercase. */\n name: string\n}\n\n/**\n * Aliases the parser accepts on input so the on-disk format can be\n * forgiving (different terminals + docs label modifiers + keys\n * differently). Output names are canonicalized to one form per key so\n * `matchesBinding` only has to compare one canonical against a\n * KeyEvent's `name`. Unknown names pass through verbatim — that's\n * useful for terminal-specific keys we don't have to enumerate.\n */\nconst NAME_ALIASES: Readonly<Record<string, string>> = {\n esc: 'escape',\n enter: 'return',\n ret: 'return',\n space: 'space',\n spc: 'space',\n del: 'delete',\n ins: 'insert',\n pgup: 'pageup',\n pgdn: 'pagedown',\n pagup: 'pageup',\n pagedn: 'pagedown',\n}\n\nconst MODIFIER_ALIASES: Readonly<Record<string, 'ctrl' | 'shift' | 'alt' | 'meta'>> = {\n ctrl: 'ctrl',\n control: 'ctrl',\n shift: 'shift',\n alt: 'alt',\n option: 'alt',\n opt: 'alt',\n meta: 'meta',\n cmd: 'meta',\n command: 'meta',\n super: 'meta',\n win: 'meta',\n}\n\n/**\n * Parse a `'ctrl+shift+o'`-style spec into structured fields. Returns\n * `null` for empty / malformed inputs; the caller falls back to the\n * default for that action.\n *\n * Tolerant by design:\n * - Case-insensitive (`Ctrl+O` → same as `ctrl+o`).\n * - Modifier order doesn't matter (`shift+ctrl+o` works).\n * - Mixed `+` and `-` separators allowed (`ctrl-o`, `ctrl+o`).\n * - Trailing / leading whitespace is stripped.\n * - Duplicate modifiers collapse silently.\n */\nexport function parseBindingSpec(spec: string | undefined | null): ParsedBinding | null {\n if (typeof spec !== 'string')\n return null\n const trimmed = spec.trim().toLowerCase()\n if (!trimmed)\n return null\n // Split on `+` OR `-`. `-` is tolerated mostly for users who copy\n // bindings from VS Code-style strings (`ctrl-o`).\n const parts = trimmed.split(/[+\\-]/).map(p => p.trim()).filter(Boolean)\n if (parts.length === 0)\n return null\n const result: ParsedBinding = { ctrl: false, shift: false, alt: false, meta: false, name: '' }\n for (const part of parts) {\n const modifier = MODIFIER_ALIASES[part]\n if (modifier) {\n result[modifier] = true\n continue\n }\n if (result.name) {\n // A second non-modifier token would mean a binding like `o+p` —\n // not meaningful in OpenTUI's keypress model (one key at a time).\n return null\n }\n result.name = NAME_ALIASES[part] ?? part\n }\n if (!result.name)\n return null\n return result\n}\n\n/**\n * Test a KeyEvent against a parsed-or-string binding. Accepts either\n * form so callers can pre-parse (hot path) or pass the raw spec for\n * one-off checks.\n *\n * Modifier semantics: ALL declared modifiers must be pressed AND no\n * extra modifiers may be pressed. So `'o'` does NOT match `ctrl+o`,\n * and `'ctrl+o'` does NOT match `ctrl+shift+o`. This avoids surprises\n * where a binding that should be specific accidentally fires for a\n * superset of key combinations.\n */\nexport function matchesBinding(\n event: { name?: string, ctrl?: boolean, shift?: boolean, meta?: boolean, option?: boolean, alt?: boolean },\n spec: string | ParsedBinding,\n): boolean {\n const parsed = typeof spec === 'string' ? parseBindingSpec(spec) : spec\n if (!parsed)\n return false\n // OpenTUI's `ParsedKey` exposes `option` for Alt; some terminals\n // also report it as `alt`. Accept either.\n const altPressed = !!event.option || !!event.alt\n if (parsed.ctrl !== !!event.ctrl)\n return false\n if (parsed.shift !== !!event.shift)\n return false\n if (parsed.alt !== altPressed)\n return false\n if (parsed.meta !== !!event.meta)\n return false\n if ((event.name ?? '').toLowerCase() !== parsed.name)\n return false\n return true\n}\n\n// ---------------------------------------------------------------------------\n// Display helpers\n// ---------------------------------------------------------------------------\n\n/** Verbose key names → compact single-cell glyphs used by hint rows. */\nconst KEY_GLYPHS: Readonly<Record<string, string>> = {\n up: '↑',\n down: '↓',\n left: '←',\n right: '→',\n return: '↵',\n enter: '↵',\n delete: '⌫',\n backspace: '⌫',\n escape: 'esc',\n space: '␣',\n tab: '⇥',\n}\n\n/**\n * Render a binding spec (`\"ctrl+return\"`, `\"backspace\"`, `\"delete\"`) as a\n * compact display string the user recognizes at a glance. Substitutes\n * arrow / enter / backspace glyphs for their verbose names so the hint\n * row stays narrow; modifier names + plain keys pass through unchanged.\n *\n * Empty / missing specs return an empty string — callers decide whether\n * to render a placeholder (e.g. `'—'` for an unbound action in the\n * keybindings panel) or hide the surface entirely (e.g. hint rows that\n * fall back to a different chord when the binding is empty).\n */\nexport function formatBindingForDisplay(spec: string | null | undefined): string {\n if (!spec)\n return ''\n const segments = spec.toLowerCase().split('+')\n const key = segments.pop() ?? ''\n const modifiers = segments.join('+')\n const glyph = KEY_GLYPHS[key] ?? key\n return modifiers ? `${modifiers}+${glyph}` : glyph\n}\n\n// ---------------------------------------------------------------------------\n// Merge + file IO\n// ---------------------------------------------------------------------------\n\n/**\n * Merge a partial map of user overrides into the defaults. Unknown\n * action keys are dropped (a future TUI version may have retired the\n * action); invalid spec strings (parse fails) fall back to the default\n * for that action.\n */\nexport function mergeKeybindings(\n overrides: Readonly<Record<string, unknown>> | null | undefined,\n): KeyBindings {\n if (!overrides || typeof overrides !== 'object')\n return { ...DEFAULT_KEYBINDINGS }\n const merged: KeyBindings = { ...DEFAULT_KEYBINDINGS }\n for (const def of KEYBINDING_DEFS) {\n const raw = overrides[def.action]\n if (typeof raw !== 'string')\n continue\n if (!parseBindingSpec(raw))\n continue // invalid spec → keep the default\n merged[def.action] = raw\n }\n return merged\n}\n\n/** Path of the user-level keybindings file. */\nexport function keybindingsPath(userDir: string): string {\n return resolve(userDir, 'keybindings.json')\n}\n\n/**\n * One contiguous section of the catalog — every action in a single\n * `group` rendered together with the section header inserted once.\n * Adjacent sections with the same group are NOT merged: catalog order\n * wins so the on-screen story matches the on-disk file.\n *\n * `rows` may carry built-in `KeyBindingDef`s (the common case) or\n * extension-owned ones (when an extension contributes a keybinding).\n * Both share label / description / spec — see {@link KeyBindingRow}.\n */\nexport interface KeyBindingSection {\n group: string\n rows: ReadonlyArray<KeyBindingRow>\n}\n\n/**\n * One row in the keybindings catalog. Either a built-in action (with\n * its `def`) or an extension-contributed one (with `id` instead). The\n * renderer reads `label` / `description` / `spec` uniformly and only\n * needs the `kind` discriminator when offering action-specific UI\n * (e.g. \"edit in keybindings.json\" by action name).\n */\nexport type KeyBindingRow\n = | { kind: 'builtin', def: KeyBindingDef, spec: string }\n | { kind: 'extension', id: string, label: string, description: string, spec: string }\n\n/**\n * Walk {@link KEYBINDING_DEFS} in order and bucket rows into contiguous\n * sections by `group`. Shared between the standalone `KeybindingsModal`\n * and the `Keybindings` tab inside `SettingsModal` so both surfaces\n * render the same catalog with the same ordering rules.\n *\n * Pass `extensionRows` (resolved extension keybindings paired with\n * their definitions) to append an \"Extensions\" section at the end so\n * extension-contributed shortcuts are discoverable in the same UI.\n */\nexport function groupBindings(\n bindings: KeyBindings,\n extensionRows: readonly { id: string, label: string, description: string, spec: string }[] = [],\n): readonly KeyBindingSection[] {\n const sections: Array<{ group: string, rows: KeyBindingRow[] }> = []\n for (const def of KEYBINDING_DEFS) {\n const last = sections[sections.length - 1]\n const spec = bindings[def.action] ?? ''\n const row: KeyBindingRow = { kind: 'builtin', def, spec }\n if (last && last.group === def.group) {\n last.rows.push(row)\n continue\n }\n sections.push({ group: def.group, rows: [row] })\n }\n if (extensionRows.length > 0) {\n sections.push({\n group: 'Extensions',\n rows: extensionRows.map(r => ({\n kind: 'extension',\n id: r.id,\n label: r.label,\n description: r.description,\n spec: r.spec,\n })),\n })\n }\n return sections\n}\n\n/**\n * Width (in cells) of the fixed key column used by every binding-row\n * renderer. Derived once from {@link KEYBINDING_DEFS} so a new action\n * with a wider default spec (`ctrl+shift+x`, …) automatically grows\n * the column instead of truncating the label.\n */\nexport const KEYBINDING_KEY_COL_WIDTH = (() => {\n let max = 8\n for (const def of KEYBINDING_DEFS) {\n const width = formatBindingForDisplay(def.default).length\n if (width > max)\n max = width\n }\n return max + 2 // breathing room before the label column\n})()\n\n/**\n * Load + merge the user's keybindings file (when present). Always\n * returns a fully-resolved {@link KeyBindings} — defaults fill in for\n * any missing / invalid entries so consumers can treat the result as\n * total.\n *\n * Failure modes (all tolerated, all fall back to defaults):\n * - File missing\n * - File not valid JSON / JSONC (after comment stripping)\n * - Top-level is not an object\n * - Individual entries have invalid spec strings\n */\nexport function readKeybindings(userDir: string): KeyBindings {\n return mergeKeybindings(readKeybindingsRaw(userDir))\n}\n\n/**\n * Create the user-level `keybindings.json` if it doesn't exist,\n * prefilled with every default. Returns the absolute file path either\n * way so the caller can open it in `$EDITOR`. Idempotent: an existing\n * user file is NEVER overwritten — the user's customizations are\n * sacred.\n */\nexport function ensureKeybindingsFile(userDir: string): string {\n const path = keybindingsPath(userDir)\n if (existsSync(path))\n return path\n writeFileAtomic(path, renderDefaultFile(), { ensureDir: true })\n return path\n}\n\n/**\n * Read the raw on-disk JSON object from `keybindings.json` after the\n * JSONC stripper. Used by extension-keybinding resolution so the\n * user's overrides for extension-owned ids flow through the same file\n * the built-in actions use.\n *\n * Returns an empty object on missing / malformed / non-object files\n * — same fault-tolerance contract as {@link readKeybindings}.\n */\nexport function readKeybindingsRaw(userDir: string): Readonly<Record<string, unknown>> {\n const path = keybindingsPath(userDir)\n if (!existsSync(path))\n return {}\n try {\n const raw = readFileSync(path, 'utf-8')\n const parsed = JSON.parse(stripJsonComments(raw)) as unknown\n if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed))\n return {}\n return parsed as Readonly<Record<string, unknown>>\n }\n catch {\n return {}\n }\n}\n\n/**\n * Resolve the live spec string for each extension-contributed\n * keybinding. The user's `keybindings.json` is consulted for an\n * override on the binding's `id`; absent / invalid entries fall back\n * to the binding's `default`.\n *\n * Pass the raw object (from {@link readKeybindingsRaw}) — passing\n * `KeyBindings` here wouldn't help because the typed map only carries\n * built-in `KeyAction` entries.\n *\n * Returns one entry per input binding, in source order. Empty `spec`\n * (binding declared `default: ''` and the user didn't bind it) is a\n * legitimate \"unbound\" state — the dispatcher never matches an empty\n * spec, so the action stays accessible via re-binding through\n * `keybindings.json`.\n */\nexport function resolveExtensionKeybindings(\n bindings: readonly { id: string, default: string }[],\n raw: Readonly<Record<string, unknown>>,\n): ResolvedExtensionKeybinding[] {\n return bindings.map((binding) => {\n const candidate = raw[binding.id]\n if (\n typeof candidate === 'string'\n && parseBindingSpec(candidate)\n && !isReservedExtensionBinding(candidate)\n && !isBareCharExtensionBinding(candidate)\n ) {\n return { id: binding.id, spec: candidate }\n }\n return {\n id: binding.id,\n spec: isReservedExtensionBinding(binding.default) || isBareCharExtensionBinding(binding.default)\n ? ''\n : binding.default,\n }\n })\n}\n\n/**\n * `true` when `spec` is one of the chords the framework reserves for\n * itself — `escape` (abort / back / exit) and `ctrl+c` (quit). Extensions\n * can never claim these, neither via their declared `default` nor via a\n * user override in `keybindings.json`. Single source of truth shared by\n * `resolveExtensionKeybindings` above and the registration-time refusal\n * in `extensions/register.ts`.\n */\nexport function isReservedExtensionBinding(spec: string): boolean {\n const parsed = parseBindingSpec(spec)\n if (!parsed)\n return false\n const noExtraModifiers = !parsed.shift && !parsed.alt && !parsed.meta\n if (parsed.name === 'escape' && !parsed.ctrl && noExtraModifiers)\n return true\n return parsed.name === 'c' && parsed.ctrl && noExtraModifiers\n}\n\n/**\n * `true` when `spec` is a single printable character without a gating\n * modifier (ctrl / alt / meta — shift alone is just a capital letter).\n * Such a chord would fire on every matching keystroke while the user\n * types in the prompt, so extension bindings can't use one; they're\n * registered unbound instead (rebindable to a modified chord via\n * `keybindings.json`).\n */\nexport function isBareCharExtensionBinding(spec: string): boolean {\n const parsed = parseBindingSpec(spec)\n if (!parsed)\n return false\n return parsed.name.length === 1 && !parsed.ctrl && !parsed.alt && !parsed.meta\n}\n\n/**\n * Render the default `keybindings.json` body. JSONC with a header\n * explaining the format + per-action comments derived from\n * {@link KEYBINDING_DEFS}'s descriptions. The user sees the catalog +\n * defaults at a glance the moment they open the file.\n *\n * Extension-contributed bindings live alongside the built-ins in the\n * SAME file (keyed by their `id`, e.g. `\"my-ext.openPanel\"`) — they\n * don't appear in the scaffold because extensions are discovered at\n * runtime, but `resolveExtensionKeybindings` reads the same file so\n * a user-added entry overrides the extension's default. The hint at\n * the bottom of the scaffold tells the user how.\n */\nfunction renderDefaultFile(): string {\n const lines: string[] = []\n lines.push('// Zidane keybindings — edit values below to customize.')\n lines.push('//')\n lines.push('// Format:')\n lines.push('// \"<action>\": \"<modifier>+<modifier>+<key>\"')\n lines.push('//')\n lines.push('// Modifiers: ctrl, shift, alt (option), meta (cmd).')\n lines.push('// Keys: a-z, 0-9, tab, return, escape, space, delete, …')\n lines.push('//')\n lines.push('// Comments (`//` line, `/* block */`) are stripped before parsing.')\n lines.push('// Restart the TUI to apply changes.')\n lines.push('//')\n lines.push('// Extensions (.zidane/extensions/<name>/index.ts) can register')\n lines.push('// their own keybindings; rebind any of them by adding a line')\n lines.push('// using the extension binding\\'s id, e.g.')\n lines.push('// \"my-ext.openPanel\": \"ctrl+shift+j\"')\n lines.push('// The Settings → Keybindings tab lists every registered id.')\n lines.push('{')\n KEYBINDING_DEFS.forEach((def, i) => {\n const trailingComma = i < KEYBINDING_DEFS.length - 1 ? ',' : ''\n lines.push(` // ${def.description}`)\n lines.push(` ${JSON.stringify(def.action)}: ${JSON.stringify(def.default)}${trailingComma}`)\n })\n lines.push('}')\n lines.push('')\n return lines.join('\\n')\n}\n\n// ---------------------------------------------------------------------------\n// JSONC parsing — `//` line + `/* block */` comments, single pass\n// ---------------------------------------------------------------------------\n\n/**\n * Strip `//` line comments and `/* … *\\/` block comments from a JSONC\n * source string. Stays out of string literals so a `\"foo // bar\"` value\n * keeps its slashes. Exported for unit-tests; the only runtime caller\n * is {@link readKeybindings}.\n *\n * Trade-off: this is a tiny state-machine, not a full JSONC parser. It\n * doesn't understand `\\\\` outside strings (irrelevant for JSON) and\n * doesn't track line numbers in error messages. Good enough for a\n * config file — corruption in production gracefully falls back to the\n * defaults via the try/catch in `readKeybindings`.\n */\nexport function stripJsonComments(input: string): string {\n let out = ''\n let i = 0\n let inString = false\n let escape = false\n while (i < input.length) {\n const c = input[i]\n if (inString) {\n out += c\n if (escape)\n escape = false\n else if (c === '\\\\')\n escape = true\n else if (c === '\"')\n inString = false\n i++\n continue\n }\n if (c === '\"') {\n inString = true\n out += c\n i++\n continue\n }\n if (c === '/' && input[i + 1] === '/') {\n while (i < input.length && input[i] !== '\\n')\n i++\n continue\n }\n if (c === '/' && input[i + 1] === '*') {\n i += 2\n while (i < input.length && !(input[i] === '*' && input[i + 1] === '/'))\n i++\n i += 2\n continue\n }\n out += c\n i++\n }\n return out\n}\n","/**\n * XDG Base Directory resolution for zidane.\n *\n * The user's storage spreads across four logical slots:\n *\n * - **config** — credentials, mcp-credentials, keybindings, config.json,\n * mcps.json, skills (user-editable surface).\n * - **data** — sessions.db (long-lived chat history).\n * - **state** — state.json (per-machine UI bookkeeping: last provider,\n * last model, last agent, settings).\n * - **cache** — persisted tool results, background-task logs,\n * auto-update registry cache (regenerable, safe to wipe).\n *\n * Historically all four collapsed to `~/.zidane/`. This module adds\n * XDG-Base-Directory-aware defaults so a Linux user with no existing\n * `~/.zidane/` lands in the conventional `~/.config/zidane/`,\n * `~/.local/share/zidane/`, `~/.local/state/zidane/`, `~/.cache/zidane/`\n * trio. Existing setups (anyone with a `~/.zidane/` directory or\n * `ZIDANE_STORAGE_DIR` set) keep the single-dir layout unchanged.\n *\n * Resolution flow — applied in this order:\n *\n * 1. Caller passed an explicit `storageDir` (`ChatOptions.storageDir`)\n * OR `ZIDANE_STORAGE_DIR` is set → **legacy-explicit** single-dir\n * layout under `<storageDir>/<prefix>`. The four slots collapse to\n * the same directory.\n * 2. `~/<prefix>/` already exists on disk → **legacy-existing**\n * single-dir layout under that directory. Upgrades never silently\n * shift files around.\n * 3. Any `XDG_*_HOME` env var is set OR `process.platform === 'linux'`\n * → **XDG split**. Each slot lands in its own dir. Per-slot\n * `ZIDANE_{CONFIG,DATA,CACHE,STATE}_DIR` overrides beat the XDG\n * vars for surgical tweaks.\n * 4. Otherwise (macOS/Windows, no XDG signal, no existing dir) →\n * **default** single-dir layout at `~/<prefix>/`. Day-one behavior\n * on these platforms is unchanged.\n *\n * The `userDir` legacy alias always equals `configDir` so existing code\n * that still references `paths.userDir` keeps reading credentials, mcps,\n * etc. from the right place.\n */\n\nimport { existsSync } from 'node:fs'\nimport { homedir } from 'node:os'\nimport { resolve } from 'node:path'\n\n/** The four slot kinds we resolve. Names match the XDG spec. */\nexport type StorageSlot = 'config' | 'data' | 'state' | 'cache'\n\n/** Which resolution branch produced the layout — surfaced for debugging + tests. */\nexport type StorageMode = 'legacy-explicit' | 'legacy-existing' | 'xdg' | 'default'\n\n/**\n * Per-slot directories. In legacy / default modes the four fields all\n * point at the same path; in XDG mode they fan out into their\n * spec-conventional locations.\n *\n * `mode` carries the resolution branch so a host that wants to print\n * a startup banner (\"zidane is using XDG paths\") can detect the\n * layout without re-running the logic.\n */\nexport interface StorageDirs {\n configDir: string\n dataDir: string\n stateDir: string\n cacheDir: string\n mode: StorageMode\n}\n\nexport interface ResolveStorageDirsOptions {\n /** Storage prefix. Leading dot tolerated. Default: `'.zidane'`. */\n prefix?: string\n /**\n * Explicit storage root (`ChatOptions.storageDir`). When set, forces\n * legacy-explicit mode regardless of the platform / XDG signals.\n */\n storageDir?: string\n /** Override `os.homedir()` for tests. */\n home?: string\n /** Override `process.platform` for tests. */\n platform?: NodeJS.Platform\n /** Override `process.env` for tests. */\n env?: Readonly<Record<string, string | undefined>>\n}\n\n/**\n * Resolve the four storage slots according to the precedence above.\n *\n * Pure aside from one filesystem probe (`existsSync(home/<prefix>)`)\n * — the same probe `resolveStoragePaths` would do anyway when\n * computing `paths.userDir`. No directories are created here; the\n * relevant write paths (state store, credentials writer, persist\n * dir) handle their own lazy `mkdirSync` already.\n */\nexport function resolveStorageDirs(opts: ResolveStorageDirsOptions = {}): StorageDirs {\n const env = opts.env ?? process.env\n const home = opts.home ?? homedir()\n const platform = opts.platform ?? process.platform\n // Two prefix forms — legacy mode preserves whatever the caller\n // passed (no implicit dot insertion, so `prefix: 'myapp'` lands at\n // `<storageDir>/myapp` not `<storageDir>/.myapp` — matches the\n // pre-XDG behavior tests already rely on). XDG mode uses the de-\n // dotted form (`'.zidane'` → `'zidane'`) because the XDG dir itself\n // is already the dotfile container — `~/.config/.zidane/` would be\n // a double-dot offense.\n const rawPrefix = opts.prefix ?? '.zidane'\n const prefixBare = rawPrefix.replace(/^\\./, '')\n\n // Branch 1 — explicit storage root → legacy-explicit.\n // Host-supplied `storageDir` beats env so SDK callers can pin a\n // location for embedded usage; otherwise `ZIDANE_STORAGE_DIR` wins.\n const explicitStorageDir = opts.storageDir ?? env.ZIDANE_STORAGE_DIR\n if (explicitStorageDir) {\n const base = resolve(explicitStorageDir, rawPrefix)\n return single(base, 'legacy-explicit')\n }\n\n // Branch 2 — `~/<prefix>/` already exists → legacy-existing.\n // Detect ANY of the legacy locations the user might have:\n // - `~/.zidane/` (default prefix)\n // - `~/.<prefix>/` (custom prefix)\n // The probe is a single existsSync (cheap) and skipping it for a\n // user with a customized prefix would leak their data into XDG dirs\n // on next launch.\n const legacyHomeDir = resolve(home, rawPrefix)\n if (existsSync(legacyHomeDir))\n return single(legacyHomeDir, 'legacy-existing')\n\n // Branch 3 — XDG signals present → split layout.\n // The signal is generous: Linux always qualifies, any platform\n // where the user has set any `XDG_*_HOME` var opts in (a savvy\n // mac user can put zidane in their XDG hierarchy too), AND any\n // `ZIDANE_{CONFIG,DATA,STATE,CACHE}_DIR` per-slot override also\n // opts in — otherwise a user pinning JUST `ZIDANE_CACHE_DIR` on\n // macOS would have it silently ignored because none of the\n // `XDG_*_HOME` vars are set.\n const hasXdgSignal = platform === 'linux'\n || !!env.XDG_CONFIG_HOME\n || !!env.XDG_DATA_HOME\n || !!env.XDG_CACHE_HOME\n || !!env.XDG_STATE_HOME\n || !!env.ZIDANE_CONFIG_DIR\n || !!env.ZIDANE_DATA_DIR\n || !!env.ZIDANE_STATE_DIR\n || !!env.ZIDANE_CACHE_DIR\n\n if (hasXdgSignal) {\n return {\n configDir: env.ZIDANE_CONFIG_DIR\n ?? resolve(env.XDG_CONFIG_HOME || resolve(home, '.config'), prefixBare),\n dataDir: env.ZIDANE_DATA_DIR\n ?? resolve(env.XDG_DATA_HOME || resolve(home, '.local', 'share'), prefixBare),\n stateDir: env.ZIDANE_STATE_DIR\n ?? resolve(env.XDG_STATE_HOME || resolve(home, '.local', 'state'), prefixBare),\n cacheDir: env.ZIDANE_CACHE_DIR\n ?? resolve(env.XDG_CACHE_HOME || resolve(home, '.cache'), prefixBare),\n mode: 'xdg',\n }\n }\n\n // Branch 4 — default (mac/windows, no XDG signal, no existing dir).\n // Same as the day-one experience: everything under `~/<prefix>/`.\n return single(legacyHomeDir, 'default')\n}\n\nfunction single(base: string, mode: StorageMode): StorageDirs {\n return {\n configDir: base,\n dataDir: base,\n stateDir: base,\n cacheDir: base,\n mode,\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","/**\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 * OOM guard POLICY for tools that must materialize a WHOLE file in\n * memory (`read_file`'s text path, `edit` / `multi_edit`'s\n * read-replace-write, grep's no-ripgrep fallback): the\n * `ExecutionContext.readFile` primitive has no partial read, so a\n * multi-GB target would be pulled into the host process wholesale —\n * well past JS string limits and into OOM territory — before any\n * output cap gets a chance to apply.\n *\n * The size CAPABILITY lives on the execution context\n * (`ExecutionContext.fileSize`, containment-aware), keeping the\n * tools-never-touch-host-fs boundary intact: this module only decides\n * what to do with the number. Contexts without the probe (remote\n * sandboxes where it would cost a round-trip per read) return\n * `undefined`/`null` and the read proceeds unguarded — their own\n * resource limits are the backstop, as documented on the interface.\n */\n\nimport type { ExecutionContext, ExecutionHandle } from '../contexts'\n\n/**\n * Refuse whole-file reads above this. Chosen well under engine string\n * limits (≈512MB–1GB) with headroom for the copies string processing\n * makes (line split, slice, join ≈ 2–3× peak).\n */\nexport const WHOLE_READ_CEILING_BYTES = 256 * 1024 * 1024\n\n/**\n * `null` when the read may proceed; a ready-to-return refusal message\n * otherwise. Same bracketed-message convention as the attachment caps.\n */\nexport async function wholeReadRefusal(\n execution: ExecutionContext,\n handle: ExecutionHandle,\n path: string,\n what: 'read' | 'edit',\n): Promise<string | null> {\n const size = await execution.fileSize?.(handle, path) ?? null\n if (size === null || size <= WHOLE_READ_CEILING_BYTES)\n return null\n const mb = Math.round(size / 1024 / 1024)\n const capMb = WHOLE_READ_CEILING_BYTES / 1024 / 1024\n return what === 'read'\n ? `[file too large to ${what}: ${path} is ${mb}MB (whole-file ceiling ${capMb}MB). Use shell (head/tail/sed -n/grep) to inspect slices of it.]`\n : `[file too large to ${what}: ${path} is ${mb}MB (whole-file ceiling ${capMb}MB). Use shell (sed -i/awk) for targeted in-place changes.]`\n}\n","import type { ToolContext, ToolDef } from './types'\nimport { isOutputExceededMessage } from '../contexts/output-cap'\nimport { errorMessage } from '../errors'\nimport { describeVia, resolveOldString, stripLineNumberPrefixes, styleReplacementForVia } from './edit-utils'\nimport { suggestionFor } from './path-suggest'\nimport { hashContent, readStateKey, resolveReadStateMap } from './read-state'\nimport { pathArgError } from './validation'\nimport { wholeReadRefusal } from './whole-read-guard'\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 ctx.reportOutcome?.('failed')\n return `Edit error: old_string and new_string are identical — nothing to change in ${target}.`\n }\n\n if (find.length === 0) {\n ctx.reportOutcome?.('failed')\n return `Edit error: old_string is empty. Use write_file to create or fully overwrite a file.`\n }\n\n const refusal = await wholeReadRefusal(ctx.execution, ctx.handle, target, 'edit')\n if (refusal) {\n ctx.reportOutcome?.('failed')\n return refusal\n }\n\n let original: string\n try {\n original = await ctx.execution.readFile(ctx.handle, target)\n }\n catch (err) {\n // Transport output-cap errors (docker probe-failure safety net)\n // must not masquerade as missing files — see read-file.ts.\n const message = errorMessage(err)\n if (isOutputExceededMessage(message)) {\n ctx.reportOutcome?.('failed')\n return `[file too large to edit: ${target} — ${message}. Use shell (sed -i/awk) for targeted in-place changes.]`\n }\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 ctx.reportOutcome?.('failed')\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 }\n if (prior.contentHash !== hashContent(original)) {\n ctx.reportOutcome?.('failed')\n return `Edit error: ${target} has changed on disk since the last read. Re-read the file before editing.`\n }\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 ctx.reportOutcome?.('failed')\n return `Edit error: old_string appears ${occurrences} times in ${target}. Pass replace_all=true or expand old_string for uniqueness.`\n }\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\n/** Backstop so a pathological walk can never hang the agent's turn. */\nconst GLOB_TIMEOUT_MS = 20_000\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 gitignore: {\n type: 'boolean',\n description: 'Skip files git ignores (.gitignore, .git/info/exclude, global excludes). Default: true. Set false to include ignored files like node_modules. Honored where git is available; a no-op otherwise.',\n },\n },\n required: ['pattern'],\n },\n },\n async execute({ pattern, limit, metadata, gitignore }, 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\n // Backstop: a pathological walk must never hang the turn. Abort the\n // listing and return a steer after GLOB_TIMEOUT_MS — raced so the tool\n // settles on time even if a streaming matcher is slow to yield control.\n // The controller is also linked to the caller's signal.\n const controller = new AbortController()\n const onAbort = () => controller.abort()\n ctx.signal.addEventListener('abort', onAbort, { once: true })\n let timer: ReturnType<typeof setTimeout> | undefined\n const timeout = new Promise<'timeout'>((resolve) => {\n timer = setTimeout(() => {\n controller.abort()\n resolve('timeout')\n }, GLOB_TIMEOUT_MS)\n })\n try {\n const outcome = await Promise.race([\n ctx.execution.listFiles(ctx.handle, pat, {\n glob: true,\n limit: max,\n metadata: wantMetadata,\n gitignore: gitignore as boolean | undefined,\n signal: controller.signal,\n }),\n timeout,\n ])\n if (outcome === 'timeout')\n return `Glob timed out after ${GLOB_TIMEOUT_MS}ms — narrow the pattern (anchor a literal prefix like \\`src/**\\`) or scope it to a subdirectory.`\n if (outcome.length === 0)\n return '(no matches)'\n return outcome.map(entry => renderEntry(entry, wantMetadata)).join('\\n')\n }\n finally {\n clearTimeout(timer)\n ctx.signal.removeEventListener('abort', onAbort)\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 { GIT_LS_FILES_COMMAND, parseGitLsFiles } from '../contexts/git-ls-files'\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/**\n * OOM guard for the no-ripgrep fallback: it reads each candidate WHOLE\n * into memory (the execution interface has no streaming read), so one\n * multi-GB artifact would take the process down. Enforced twice — the\n * `find` walk excludes oversized files via the portable `-size`\n * predicate (POSIX `c` suffix, GNU + BSD), and the read loop probes\n * process-local sizes to cover the single-file fast path that bypasses\n * the walk. A documented divergence from the ripgrep path, which\n * streams and searches such files fine.\n */\nconst FALLBACK_MAX_FILE_BYTES = 16 * 1024 * 1024\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 'gitignore'?: boolean\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 'gitignore': { type: 'boolean', description: 'Skip files git ignores (.gitignore, .git/info/exclude, global excludes). Default: true. Set false to also search ignored files like node_modules.' },\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 // ripgrep respects .gitignore by default; only disable it on explicit opt-out.\n if (input.gitignore === false)\n args.push('--no-ignore')\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 // Second layer of the fallback's OOM guard, scoped to the single-file\n // fast path ONLY: walk-enumerated files are already size-filtered by\n // the find predicate, and probing each of them would cost one exec per\n // file on exec-probed contexts (docker/sandbox) — N extra round-trips\n // for zero added protection. The fast path bypasses the walk, so it is\n // the one entry that needs the probe. (Contexts without the capability\n // proceed unguarded: same accepted residual as whole-read-guard.ts.)\n const viaSingleFileFastPath = files.length === 1 && files[0] === input.path\n if (viaSingleFileFastPath) {\n const size = await ctx.execution.fileSize?.(ctx.handle, files[0] as string) ?? null\n if (size !== null && size > FALLBACK_MAX_FILE_BYTES)\n return '(no matches)'\n }\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 const files = result.stdout\n .split('\\n')\n .map(line => line.replace(/^\\.\\//, ''))\n .filter(line => line.length > 0)\n .sort()\n\n // `find` honors no ignore rules, so the no-`rg` fallback would otherwise\n // walk `node_modules` etc. — inconsistent with the ripgrep path. Restrict to\n // git's non-ignored set by default; `gitignore: false` keeps everything.\n if (input.gitignore === false)\n return files\n return filterByGitignore(files, root, ctx)\n}\n\n/**\n * Best-effort intersection of `files` with `git ls-files` (non-ignored set).\n * Returns `files` unchanged when git is unavailable, the dir isn't a repo, or\n * `root` escapes the repo (absolute / `..`) — the filter can only fail open.\n */\nasync function filterByGitignore(files: string[], root: string, ctx: ToolContext): Promise<string[]> {\n if (files.length === 0)\n return files\n if (root !== '.' && (root.startsWith('/') || root.split('/').includes('..')))\n return files\n try {\n const res = await ctx.execution.exec(ctx.handle, GIT_LS_FILES_COMMAND, { signal: ctx.signal })\n if (res.exitCode !== 0)\n return files\n const allowed = new Set(parseGitLsFiles(res.stdout))\n if (allowed.size === 0)\n return files\n return files.filter(file => allowed.has(file))\n }\n catch {\n return files\n }\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 const sizeGuard = `! -size +${FALLBACK_MAX_FILE_BYTES}c`\n if (!glob)\n return `find ${base} -type f ${sizeGuard}`\n const anyDepth = glob.match(/^(?:\\*\\*\\/)?([^/]+)$/)\n if (anyDepth)\n return `find ${base} -type f ${sizeGuard} -name ${shellQuote(anyDepth[1])}`\n const normRoot = findRoot.replace(/\\/$/, '')\n const pathPattern = root === '.' ? `./${glob}` : `${normRoot}/${glob}`\n return `find ${base} -type f ${sizeGuard} -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'\nimport { errorMessage } from '../errors'\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 — subdirectories carry a trailing `/` — or `(empty directory)` when the directory exists but is empty. 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 const target = (path as string) || '.'\n try {\n const entries = await ctx.execution.listFiles(ctx.handle, target)\n return entries.map(entryPath).join('\\n') || '(empty directory)'\n }\n catch (err) {\n // Differentiate the common failure shapes — reporting a permission\n // error or a file-not-a-directory as \"not found\" sends the model\n // chasing path typos that don't exist.\n ctx.reportOutcome?.('failed')\n // Matches both Node error codes (process context) and `ls` stderr\n // phrasing (docker context).\n const message = errorMessage(err)\n if (message.includes('ENOTDIR') || message.includes('Not a directory'))\n return `Not a directory: ${target} — it is a file; use read_file instead.`\n if (message.includes('EACCES') || message.includes('EPERM') || message.includes('Permission denied'))\n return `Permission denied: ${target}.`\n if (message.includes('ENOENT') || message.includes('No such file'))\n return `Directory not found: ${target}`\n return `list_files failed for ${target}: ${message}`\n }\n },\n}\n","/**\n * `model_search` — browse the models children may be spawned on.\n *\n * Companion to `createSpawnTool({ models })` for hosts whose catalog is\n * too large to embed in the spawn schema. Reuses the chat layer's\n * `ModelInfo` objects verbatim (id, display name, context window, real\n * per-Mtok pricing) paired with the runtime `Provider.name` that serves\n * each model — the same pairing the spawn tool validates against, so\n * what this tool lists is exactly what `spawn({ model })` accepts.\n *\n * Kept out of the system prompt by design: the model reaches for this\n * on demand when it wants to cost-tier a delegation, instead of every\n * run paying the token cost of a full catalog dump.\n */\n\nimport type { ModelInfo } from '../chat/providers'\nimport type { ToolDef } from './types'\n\n/**\n * A {@link ModelInfo} paired with the runtime `Provider.name` that serves\n * it. `provider` must match a `Provider.name` available to the run (the\n * primary or a fallback-chain provider — see `ToolContext.providers`);\n * the spawn tool routes the child agent to that instance.\n */\nexport interface SpawnModelEntry {\n provider: string\n model: ModelInfo\n}\n\n/**\n * One display line per model: id, provider, real pricing, context\n * window, reasoning flag. The single source of truth for how models\n * render in BOTH the spawn schema's embedded catalog and `model_search`\n * output, so the model never sees two spellings of the same entry.\n */\nexport function formatModelLine(entry: SpawnModelEntry): string {\n const { model } = entry\n const parts = [`${model.id} (${entry.provider})`]\n if (model.cost)\n parts.push(`$${model.cost.input}/$${model.cost.output} per Mtok`)\n if (typeof model.contextWindow === 'number' && model.contextWindow > 0)\n parts.push(`${Math.round(model.contextWindow / 1000)}k ctx`)\n if (model.reasoning)\n parts.push('reasoning')\n if (model.name && model.name !== model.id)\n parts.push(model.name)\n return parts.join(' · ')\n}\n\n/**\n * Case-insensitive multi-term filter across id, display name, and\n * provider. Every whitespace-separated term must match somewhere\n * (mirrors the TUI picker's `filterModelCatalog` semantics). Empty /\n * whitespace queries pass everything through.\n */\nexport function matchModelEntries(\n entries: readonly SpawnModelEntry[],\n query: string,\n): SpawnModelEntry[] {\n const trimmed = query.trim().toLowerCase()\n if (!trimmed)\n return [...entries]\n const terms = trimmed.split(/\\s+/)\n return entries.filter((entry) => {\n const corpus = `${entry.model.id} ${entry.model.name ?? ''} ${entry.provider}`.toLowerCase()\n return terms.every(t => corpus.includes(t))\n })\n}\n\n/**\n * Build the `model_search` tool over a spawn model catalog. Register it\n * alongside `createSpawnTool({ models })` — typically only when the\n * catalog is too large to embed in the spawn schema, though it is\n * harmless (and occasionally useful) alongside an embedded catalog too.\n */\nexport function createModelSearchTool(models: readonly SpawnModelEntry[]): ToolDef {\n return {\n spec: {\n name: 'model_search',\n description: [\n 'List the models available for spawned subagents, with per-Mtok pricing and context windows.',\n 'Use it to cost-tier delegation: find a cheap, fast model for mechanical or high-volume work before calling `spawn` with its id in the `model` field.',\n 'Pass `query` to filter by substring across model id, name, and provider; omit it to list everything.',\n ].join('\\n'),\n inputSchema: {\n type: 'object',\n properties: {\n query: {\n type: 'string',\n description: 'Optional filter — space-separated terms, ALL must match (e.g. \"haiku\" or \"openrouter qwen\").',\n },\n },\n additionalProperties: false,\n },\n },\n async execute(input): Promise<string> {\n const query = typeof input.query === 'string' ? input.query : ''\n const matches = matchModelEntries(models, query)\n if (matches.length === 0) {\n return query.trim() === ''\n ? 'No models are configured for subagent use.'\n : `No models match \"${query.trim()}\". Call model_search without a query to list all ${models.length}.`\n }\n const header = query.trim() === ''\n ? `${matches.length} model${matches.length === 1 ? '' : 's'} available for spawned subagents:`\n : `${matches.length} of ${models.length} model${models.length === 1 ? '' : 's'} match:`\n return [header, ...matches.map(m => `- ${formatModelLine(m)}`)].join('\\n')\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'\nimport { wholeReadRefusal } from './whole-read-guard'\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 const refusal = await wholeReadRefusal(ctx.execution, ctx.handle, target, 'edit')\n if (refusal)\n return refusal\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 * 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 { Buffer } from 'node:buffer'\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/**\n * Payload byte length of a media block's `data` — raw text length for\n * text-encoded documents, decoded (padding-accurate) length for base64.\n */\nexport function mediaPayloadBytes(data: string, encoding?: 'base64' | 'text'): number {\n if (encoding === 'text')\n return Buffer.byteLength(data)\n return decodedBase64Bytes(data)\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 encoding?: 'base64' | 'text'\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(mediaPayloadBytes(block.data, block.encoding))\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","/**\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 * 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 { ResolvedAttachment } from '../attachment-handle'\nimport type { ToolResultContent } from '../types'\nimport type { ToolContext, ToolDef } from './types'\nimport { Buffer } from 'node:buffer'\nimport { parseAttachmentHandle, resolveAttachmentFromTurns } from '../attachment-handle'\nimport { normalizeReadFileInteger, resolveReadFileDefaults } from '../behaviors/read-file-defaults'\nimport { isOutputExceededMessage } from '../contexts/output-cap'\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'\nimport { wholeReadRefusal } from './whole-read-guard'\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\n/**\n * Decode a resolved document attachment to text when its bytes are actually\n * textual. Unknown-extension source files (e.g. `.asm`) ingest as base64\n * `application/octet-stream` blobs; re-inlining megabytes of base64 whole is\n * exactly what OOMs a session, so textual payloads get routed through the\n * line-paged renderer instead. Returns `null` for images, PDFs, and genuinely\n * binary payloads — those keep the whole-blob inline path.\n */\nfunction attachmentText(found: ResolvedAttachment): string | null {\n if (found.kind !== 'document')\n return null\n if (found.encoding === 'text')\n return found.data\n if (found.mediaType === 'application/pdf')\n return null\n const decoded = Buffer.from(found.data, 'base64').toString('utf-8')\n return looksBinary(decoded) ? null : decoded\n}\n\ninterface TextSliceOptions {\n /** 1-indexed start line (already normalized). */\n offset: number\n /** Max lines; 0 = unlimited (already normalized). */\n limit: number\n /** Byte cap; 0 = unlimited (already normalized). */\n maxBytes: number\n lineNumbers: boolean\n}\n\n/**\n * Line-sliced, byte-capped rendering with the standard truncation footers.\n * Shared by the filesystem text path and the text-attachment re-resolve path\n * so both page identically (`offset`/`limit`/`maxBytes` semantics, footer\n * guidance included).\n */\nfunction renderTextSlice(raw: string, opts: TextSliceOptions): string {\n const { offset: offsetN, limit: limitN, maxBytes: maxBytesN, lineNumbers: showLineNumbers } = opts\n const totalBytes = Buffer.byteLength(raw)\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 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\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 const defaults = resolveReadFileDefaults(ctx.behavior, {\n lineLimit: DEFAULT_LINE_LIMIT,\n textMaxBytes: DEFAULT_BYTE_CAP,\n attachmentMaxBytes: DEFAULT_ATTACHMENT_BYTE_CAP,\n })\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 // Textual documents page through the standard line renderer — honoring\n // offset/limit/maxBytes with the TEXT defaults — instead of re-inlining\n // the whole payload (which OOMs the session for multi-MB attachments).\n const asText = attachmentText(found)\n if (asText !== null) {\n const header = `Document${found.name ? ` \"${found.name}\"` : ''} re-resolved from session history (${found.mediaType}, rendered as text).`\n const body = renderTextSlice(asText, {\n offset: normalizeReadFileInteger(offset, 1),\n limit: normalizeReadFileInteger(limit, defaults.lineLimit),\n maxBytes: normalizeReadFileInteger(maxBytes, defaults.textMaxBytes),\n lineNumbers: typeof lineNumbers === 'boolean' ? lineNumbers : (ctx.behavior?.readLineNumbers ?? true),\n })\n return `${header}\\n\\n${body}`\n }\n const sizeCap = normalizeReadFileInteger(maxBytes, defaults.attachmentMaxBytes)\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 = normalizeReadFileInteger(maxBytes, defaults.attachmentMaxBytes)\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 = normalizeReadFileInteger(maxBytes, defaults.attachmentMaxBytes)\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 // Whole-read OOM guard — see whole-read-guard.ts. Applies to the\n // text path only; attachments already have their own byte caps above.\n const refusal = await wholeReadRefusal(ctx.execution, ctx.handle, path as string, 'read')\n if (refusal)\n return refusal\n\n let raw: string\n try {\n raw = await ctx.execution.readFile(ctx.handle, path as string)\n }\n catch (err) {\n // Transport-level output cap (docker probe-failure safety net) —\n // reporting it as \"File not found\" would send the model chasing\n // path typos. Surface the real reason with the same guidance as\n // the whole-read refusal.\n const message = errorMessage(err)\n if (isOutputExceededMessage(message))\n return `[file too large to read: ${path} — ${message}. Use shell (head/tail/sed -n/grep) to inspect slices of it.]`\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 = normalizeReadFileInteger(offset, 1)\n const limitForKey = normalizeReadFileInteger(limit, defaults.lineLimit)\n const maxBytesForKey = normalizeReadFileInteger(maxBytes, defaults.textMaxBytes)\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 rememberRead()\n\n return renderTextSlice(raw, {\n offset: offsetForKey,\n limit: limitForKey,\n maxBytes: maxBytesForKey,\n lineNumbers: showLineNumbers,\n })\n },\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 * 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 from the workspace root (the `Cwd` in your environment) and return its combined stdout/stderr. Every call starts there — `cd` never persists between calls. Use relative paths (`cd src/app && bun test`, `ls docs`), not absolute ones.',\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}${absoluteCdNote(cmd, ctx.handle.cwd)}\\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 * Detect a `cd` whose absolute target is the workspace root itself or a\n * path under it. Every shell call already starts at the root, so the\n * absolute prefix is pure token noise — and once one absolute `cd`\n * appears in the transcript, models tend to keep imitating it. Returns\n * the relative form to suggest, `''` when the `cd` is a no-op (target\n * === root), or `null` when no redundant `cd` is present. Targets\n * OUTSIDE the workspace are legitimate and never flagged.\n */\nexport function redundantAbsoluteCd(command: string, cwd: string | undefined): string | null {\n if (!cwd || !cwd.startsWith('/') || cwd === '/')\n return null\n const root = cwd.replace(/\\/+$/, '')\n // `cd <target>` at the start of the command or after a separator\n // (`&&`, `||`, `;`). Quoted targets may contain spaces; bare targets\n // stop at whitespace/separators.\n const re = /(?:^|&&|\\|\\||;)\\s*cd\\s+(?:'(\\/[^']*)'|\"(\\/[^\"]*)\"|(\\/[^\\s'\";|&]*))/g\n for (const m of command.matchAll(re)) {\n const target = (m[1] ?? m[2] ?? m[3]).replace(/\\/+$/, '')\n if (target === root)\n return ''\n if (target.startsWith(`${root}/`))\n return target.slice(root.length + 1)\n }\n return null\n}\n\n/**\n * One-line steer appended to a successful call's metadata footer when\n * {@link redundantAbsoluteCd} fires. Deliberately gentle and\n * self-extinguishing: it only ever appears when the model actually\n * emits a redundant absolute `cd`, and disappears as soon as it stops.\n */\nfunction absoluteCdNote(command: string, cwd: string | undefined): string {\n const rel = redundantAbsoluteCd(command, cwd)\n if (rel === null)\n return ''\n return rel === ''\n ? '\\n(note: every call already runs at the workspace root — this `cd` is redundant)'\n : `\\n(note: every call already runs at the workspace root — \\`cd ${rel}\\` suffices)`\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 * `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 * `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 * 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 { Provider } from '../providers'\nimport type { Session } from '../session'\nimport type { AgentStats, ChildRunStats } from '../types'\nimport type { SpawnModelEntry } from './model-search'\nimport type { ToolContext, ToolDef } from './types'\nimport { createAgent } from '../agent'\nimport { flattenTurns, formatTokenUsage } from '../stats'\nimport { formatModelLine, matchModelEntries } from './model-search'\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 the instance's\n// `state.counter` — 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 * Catalogs up to this size embed their full model list in the spawn\n * schema's `model` field description; larger ones defer to the\n * `model_search` tool so the schema stays lean.\n */\nconst EMBEDDED_CATALOG_MAX = 8\n\n/**\n * Build the `model` field's description: delegation doctrine plus either\n * the embedded catalog (small) or a pointer at `model_search` (large).\n */\nfunction buildModelFieldDescription(catalog: readonly SpawnModelEntry[]): string {\n const doctrine = 'Optional model id to run the child on. Cost-tier your delegation: mechanical, parallelizable, or high-volume search work suits cheaper/faster models; keep synthesis and final judgment on your own model. Omit to inherit your model.'\n if (catalog.length <= EMBEDDED_CATALOG_MAX)\n return `${doctrine}\\nAvailable:\\n${catalog.map(e => `- ${formatModelLine(e)}`).join('\\n')}`\n return `${doctrine}\\n${catalog.length} models are available — call \\`model_search\\` to browse ids, pricing, and context windows.`\n}\n\n/**\n * Resolve a catalog entry's `Provider.name` against the run's provider\n * list: the active provider first, then the fallback-chain providers\n * (`ToolContext.providers`, populated by the loop for multi-provider\n * runs). `undefined` when no instance with that name serves this run.\n */\nfunction resolveProviderByName(name: string, ctx: ToolContext): Provider | undefined {\n if (ctx.provider.name === name)\n return ctx.provider\n return ctx.providers?.find(p => p.name === name)\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 /**\n * Catalog of models children may run on. When set, the spawn input\n * schema gains an optional `model` field so the CALLING MODEL can\n * cost-tier its delegation (mechanical / high-volume work on cheaper\n * models, synthesis on the primary). Reuses the chat layer's\n * `ModelInfo` objects verbatim — real `cost` pricing and context\n * windows, no separate catalog to maintain — paired with the runtime\n * `Provider.name` that serves each model (see {@link SpawnModelEntry}).\n *\n * - ≤ {@link EMBEDDED_CATALOG_MAX} entries → the list is embedded in\n * the field description (id · price · context) so no extra round-trip\n * and no system-prompt bloat.\n * - Larger → the description points at the `model_search` tool\n * (register {@link createModelSearchTool} with the same entries).\n * - Model-supplied ids are validated: unknown ids are rejected with\n * the closest matches so the model can self-correct; ids served by\n * a provider not present in the run are rejected with the reason.\n *\n * Default: unset — no `model` field, children always inherit\n * (byte-identical to the historical schema).\n */\n models?: readonly SpawnModelEntry[]\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 * Default model for this subagent type (e.g. an Explore preset pinned\n * to a fast, cheap model). Precedence: per-call `input.model` >\n * this > `SpawnToolOptions.model` > inherit the parent run's model.\n * When a catalog is configured and lists this id, the child also\n * routes to the provider that serves it.\n */\n model?: string\n /**\n * Default thinking level for this subagent type. Precedence: this >\n * `SpawnToolOptions.thinking` > provider default.\n */\n thinking?: 'off' | 'minimal' | 'low' | 'medium' | 'high' | 'xhigh' | 'max'\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 * A subagent def carried by an extension's `registerSubagent` — same as\n * {@link SubagentDef} but carrying its own dispatch `name` (the registry\n * key). Self-describing so the def survives the trip from extension\n * `setup` through the merged {@link ExtensionRegistry} to the host's\n * {@link SpawnTool.withSubagents} merge.\n */\nexport interface ExtensionSubagentDef extends SubagentDef {\n /** Registry key the model emits as `subagent_type`. */\n name: string\n}\n\n/**\n * Editing function for an existing subagent. Receives the current def\n * (host- or extension-provided; `undefined` when the name is unknown)\n * and returns the replacement — or `undefined` to delete the entry.\n */\nexport type SubagentOverride = (def: SubagentDef | undefined) => SubagentDef | undefined\n\n/**\n * The object {@link createSpawnTool} returns: a {@link ToolDef} that also\n * exposes live {@link SpawnToolState} plus {@link SpawnTool.withSubagents}\n * for rebuilding the tool against a merged subagent registry (the seam the\n * extension system uses to add / edit spawnable subagents).\n */\nexport interface SpawnTool extends ToolDef, SpawnToolState {\n /**\n * Return a FRESH spawn tool whose `subagent_type` enum is recomputed\n * from the base registry merged with `extra` (adds), then `overrides`\n * (edits, applied last — an override returning `undefined` deletes the\n * entry). Precedence: host base < adds < overrides. The rebuilt\n * instance shares this one's concurrency / stats / child-label state.\n */\n withSubagents: (extra: SubagentRegistry, overrides?: Record<string, SubagentOverride>) => SpawnTool\n /**\n * Return a FRESH spawn tool with the given model catalog (the `model`\n * schema field + validation — see {@link SpawnToolOptions.models}),\n * sharing this instance's counters, stats, and concurrency slots.\n * Hosts call it at agent-build time when the available models are only\n * known then (e.g. the TUI's provider chain).\n */\n withModels: (models: readonly SpawnModelEntry[]) => SpawnTool\n /**\n * Host-facing structured variant of `execute` — same child lifecycle\n * (model routing, hook bubbling, session persistence, concurrency\n * slots), but returns a {@link SpawnTaskOutcome} instead of the\n * model-facing string, so hosts (e.g. command `ctx.spawnAgents`) can\n * branch on `status` and read the bare `text` without string parsing.\n * `execute` is a thin formatter over this.\n */\n runTask: (input: SpawnTaskInput, ctx: ToolContext) => Promise<SpawnTaskOutcome>\n /**\n * The tool's simultaneous-children cap (`createSpawnTool({ maxConcurrent })`,\n * default 3). Hosts driving {@link runTask} directly size their worker\n * pools to this so the slot accounting never rejects an admitted task.\n */\n readonly maxConcurrent: number\n}\n\n/**\n * Input accepted by {@link SpawnTool.runTask} — the typed mirror of the\n * `execute` schema. Fields beyond `task` only take effect when the tool\n * was built with the matching capability (a subagent registry for\n * `subagent_type`, a model catalog for `model`).\n */\nexport interface SpawnTaskInput {\n /** Task prompt for the child agent. */\n task: string\n /** System prompt override for the child. Wins over a subagent preset's system. */\n system?: string\n /** Registered subagent type. Unknown types degrade to `general-purpose`. */\n subagent_type?: string\n /** Catalog model id to pin the child to. Unknown ids reject the task. */\n model?: string\n}\n\n/**\n * Structured outcome of one spawned child run — the host-facing shape\n * behind {@link SpawnTool.runTask}. `message` is the exact model-facing\n * string `execute` returns (header + stats + response), while `text` is\n * the child's bare final response so hosts don't have to parse the\n * formatted message.\n */\nexport interface SpawnTaskOutcome {\n /** Child label (`child-1`, …). Absent when no child slot was reserved (rejections, pre-aborted parent). */\n id?: string\n /**\n * Terminal status. `'rejected'` = refused before any child ran\n * (unknown model, depth/concurrency cap, unavailable provider).\n */\n status: 'completed' | 'aborted' | 'timeout' | 'error' | 'rejected'\n /** The child's final assistant text. Empty unless `status === 'completed'`. */\n text: string\n /** Model-facing formatted result — exactly what `execute` returns. */\n message: string\n /** Cumulative child stats (subtree-wide). Absent on rejection. */\n stats?: AgentStats\n}\n\n/** Narrow a {@link ToolDef} to a {@link SpawnTool} (i.e. it exposes `withSubagents`). */\nexport function isSpawnTool(tool: ToolDef): tool is SpawnTool {\n return typeof (tool as Partial<SpawnTool>).withSubagents === 'function'\n}\n\n/**\n * Mutable per-instance state shared across {@link SpawnTool.withSubagents}\n * rebuilds. Threading the SAME object into a rebuilt instance keeps the\n * concurrency counter, the cumulative child-stats accumulator, and the\n * sessionless child-label counter alive across a rebuild.\n */\ninterface SpawnInstanceState {\n children: Map<string, ChildAgent>\n stats: AgentStats\n /** Sessionless child-label fallback counter (see `reserveChildLabel`). */\n counter: number\n /** Concurrency counter — reserved synchronously in `execute`. */\n activeCount: number\n}\n\nfunction createSpawnState(): SpawnInstanceState {\n return {\n children: new Map<string, ChildAgent>(),\n stats: { totalIn: 0, totalOut: 0, totalCacheRead: 0, totalCacheCreation: 0, turns: 0, elapsed: 0 },\n counter: 0,\n activeCount: 0,\n }\n}\n\n/**\n * Create a configured spawn tool.\n *\n * State (`children`, `totalChildStats`, counters, active count) is scoped to\n * the returned instance. Multiple parent agents using the same instance will\n * share counters + stats + concurrency slots — call `createSpawnTool()` per\n * agent (or use the stateless default `spawn`) to keep them isolated.\n */\nexport function createSpawnTool(options: SpawnToolOptions = {}): SpawnTool {\n return buildSpawnTool(options, createSpawnState())\n}\n\n/**\n * Build a spawn-tool instance bound to `state`. Factored out of\n * {@link createSpawnTool} so {@link SpawnTool.withSubagents} can rebuild the\n * tool — recomputing the `subagent_type` enum from a merged registry —\n * while threading the SAME `state` so child counters / stats survive.\n */\nfunction buildSpawnTool(options: SpawnToolOptions, state: SpawnInstanceState): SpawnTool {\n const localChildren = state.children\n const localStats = state.stats\n const maxConcurrent = options.maxConcurrent ?? 3\n const maxDepth = options.maxDepth ?? 3\n const forwardHooks = options.forwardHooks ?? true\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 // Model catalog → optional `model` schema field. Empty/absent catalog\n // drops the field entirely, preserving the historical schema (children\n // always inherit the parent's model). Small catalogs embed the list in\n // the field description; large ones defer to `model_search` so the\n // schema doesn't balloon.\n const modelCatalog = options.models && options.models.length > 0 ? options.models : undefined\n const modelProperty = modelCatalog\n ? {\n model: {\n type: 'string' as const,\n description: buildModelFieldDescription(modelCatalog),\n },\n }\n : {}\n\n const tool: SpawnTool = {\n maxConcurrent,\n get children() { return localChildren },\n get totalChildStats() { return { ...localStats } },\n\n // Rebuild against a merged subagent registry. Precedence:\n // host base (`options.subagents`) < `extra` (adds) < `overrides`.\n // Overrides run last so an extension can edit a host/extension def\n // by name (or delete it by returning `undefined`). The rebuilt\n // instance reuses the closed-over `options` and the SAME `state`,\n // so concurrency slots, cumulative stats, and child-label counters\n // carry across the rebuild.\n withSubagents(extra, overrides) {\n const merged: SubagentRegistry = { ...options.subagents, ...extra }\n if (overrides) {\n for (const [name, fn] of Object.entries(overrides)) {\n const next = fn(merged[name])\n if (next === undefined)\n delete merged[name]\n else\n merged[name] = next\n }\n }\n return buildSpawnTool({ ...options, subagents: merged }, state)\n },\n\n // Rebuild with a model catalog (the `model` schema field + validation).\n // Same shared-state contract as `withSubagents`: concurrency slots,\n // cumulative stats, and child-label counters carry across the rebuild.\n // Hosts call this at agent-build time when the available models are\n // only known then (e.g. the TUI's provider chain).\n withModels(models) {\n return buildSpawnTool({ ...options, models }, state)\n },\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 inherits your tool set (including \\`spawn\\` itself, depth-capped) and your system prompt unless overridden, runs independently, and returns its final response. Do NOT spawn for sequential steps you could do yourself.${\n modelCatalog\n ? ' Children can run on a DIFFERENT model via the `model` field — cost-tier your delegation: mechanical, parallelizable, or high-volume search work belongs on cheaper/faster models, while synthesis and judgment stay on your own model.'\n : ''}`,\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. REPLACES the entire inherited system prompt — identity, doctrine, and environment info (cwd, platform, date) are all lost. Prefer omitting this and putting specialization in `task`; if you do override, restate the working directory and any constraints the child must keep.',\n },\n ...subagentTypeProperty,\n ...modelProperty,\n },\n required: ['task'],\n },\n },\n\n async execute(input: Record<string, unknown>, ctx: ToolContext): Promise<string> {\n // Schema-validated model input → typed task input: the one cast at the\n // LLM boundary. Via the named const (not `this`) so a destructured\n // `execute` keeps working.\n return (await tool.runTask(input as unknown as SpawnTaskInput, ctx)).message\n },\n\n async runTask(input: SpawnTaskInput, ctx: ToolContext): Promise<SpawnTaskOutcome> {\n const task = input.task\n const systemOverride = input.system\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\n // Model selection. Only honored when a catalog is configured —\n // without one the `model` field isn't in the schema, and an\n // unvalidated id would fail deep inside the child's first stream\n // instead of here where the model can self-correct.\n // Precedence: per-call `input.model` > `subagents[type].model` >\n // `options.model` > inherit the parent run's model (`ctx.model`).\n const requestedModel = modelCatalog && typeof input.model === 'string' && input.model.trim() !== ''\n ? input.model.trim()\n : undefined\n if (requestedModel && modelCatalog && !modelCatalog.some(e => e.model.id === requestedModel)) {\n const near = matchModelEntries(modelCatalog, requestedModel).slice(0, 8)\n const shown = near.length > 0 ? near : modelCatalog.slice(0, 8)\n return {\n status: 'rejected',\n text: '',\n message: `Cannot spawn: unknown model \"${requestedModel}\". ${near.length > 0 ? 'Closest matches' : 'Available'}: ${shown.map(e => e.model.id).join(', ')}${\n modelCatalog.length > shown.length ? ` … (${modelCatalog.length} total — use model_search to browse)` : ''}`,\n }\n }\n const chosenModelId = requestedModel ?? subagentDef?.model ?? options.model\n // Provider routing: a catalog-listed model runs on the provider\n // that serves it (matched by `Provider.name` against the run's\n // provider list). A model-requested id whose provider isn't in\n // this run is rejected — host-configured ids (subagent presets /\n // options.model) degrade to the parent's provider instead, since\n // the model can't fix host config by retrying.\n let childProvider = ctx.provider\n const chosenEntry = chosenModelId && modelCatalog\n ? modelCatalog.find(e => e.model.id === chosenModelId)\n : undefined\n if (chosenEntry) {\n const routed = resolveProviderByName(chosenEntry.provider, ctx)\n if (routed) {\n childProvider = routed\n }\n else if (requestedModel !== undefined) {\n return {\n status: 'rejected',\n text: '',\n message: `Cannot spawn: model \"${chosenModelId}\" is served by provider \"${chosenEntry.provider}\", which is not available in this run. Pick a model from an available provider${modelCatalog && modelCatalog.length > EMBEDDED_CATALOG_MAX ? ' (use model_search)' : ''}.`,\n }\n }\n }\n // The child's effective model. `ctx.model` (inherit) only applies when\n // the child stays on the parent's provider — a routed child would\n // reject a foreign model id, so it uses its own provider default\n // instead. Resolved HERE (not at `agent.run` time) so `spawn:before`\n // can announce the actual model to hosts (TUI spawn-start marker).\n const inheritedModel = childProvider === ctx.provider ? ctx.model : undefined\n const childModel = chosenModelId ?? inheritedModel\n const announcedModel = childModel ?? childProvider.meta.defaultModel\n\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 { status: 'rejected', text: '', message: `Cannot spawn: maxDepth=${maxDepth} reached (parent depth=${parentDepth}). Deepen the cap with createSpawnTool({ maxDepth }).` }\n }\n\n if (state.activeCount >= maxConcurrent) {\n return { status: 'rejected', text: '', message: `Cannot spawn: ${state.activeCount}/${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 { status: 'aborted', text: '', message: `[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-${++state.counter}`\n state.activeCount++\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 let responseText = ''\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: childProvider,\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, model: string, provider: string, tracingContext: Record<string, string> } = {\n id,\n task,\n depth: childDepth,\n // What the child actually runs on — hosts surface it next to the\n // spawn-start marker so cost-tiered delegation is visible.\n model: announcedModel,\n provider: childProvider.name,\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: per-call `input.model` (catalog-validated) >\n // `subagents[type].model` > spawn-tool `options.model` > parent\n // run's resolved model > provider default (inside `agent.run`).\n // Without the `ctx.model` fallback, a parent running on a per-run\n // model would silently spawn children on `provider.meta.defaultModel`\n // (e.g. a sonnet run spawning opus subagents). `childModel` is\n // resolved above, before the `spawn:before` hook announced it.\n const runPromise = agent.run({\n prompt: task,\n ...(childModel !== undefined ? { model: childModel } : {}),\n system: effectiveSystem,\n thinking: subagentDef?.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 responseText = response ?? ''\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, model: announcedModel, provider: childProvider.name, 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 model: announcedModel,\n provider: childProvider.name,\n error: destroyError,\n })\n }\n\n return { id, status: childRunStatus, text: responseText, message: result, ...(finalStats ? { stats: finalStats } : {}) }\n }\n finally {\n unbubble?.()\n state.activeCount--\n localChildren.delete(id)\n }\n },\n }\n return tool\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","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","import { definePreset } from '.'\nimport { edit, listFiles, multiEdit, readFile, shell, shellKill, waitTask, writeFile } from '../tools'\nimport { createSpawnTool } from '../tools/spawn'\n\n/**\n * Core tools available in every basic preset (without spawn).\n *\n * `edit` and `multi_edit` ship in the basic set because surgical edits are the\n * default modality for production agents — `write_file` is for full overwrites.\n * `glob` and `grep` are exported but opt-in: not every agent needs codebase\n * search, and shipping them by default would force `tool:gate` work onto\n * consumers that prefer the model to use `shell` + classic Unix tools.\n */\nexport const basicTools = { shell, shellKill, waitTask, readFile, writeFile, listFiles, edit, multiEdit }\n\nexport default definePreset({\n name: 'basic',\n system: 'You are a helpful assistant with access to shell, file reading, file writing, surgical and multi-edit tools, directory listing, and sub-agent spawning. Prefer `edit` / `multi_edit` for in-place changes and `write_file` for full file overwrites. Use them to accomplish tasks in the project directory.',\n // `tools` is a getter so each access (every `{ ...basic }` spread into\n // `createAgent`) mints a FRESH spawn tool. `createSpawnTool()` carries\n // per-instance state (running children, concurrency counter, child-stats\n // accumulator); a module-level singleton instance would be shared by every\n // agent in the process, breaking concurrent rollouts.\n //\n // `persist: true` shares the parent's session with every child agent — child\n // turns land in `session.turns` tagged with their own `runId`, and the run\n // itself is recorded in `session.runs` with `parentRunId` + `depth`. That's\n // what lets a reloaded TUI session reconstruct the full subagent tree (see\n // `eventsFromTurns` in `tui/store.ts`). Hosts that want children in-memory\n // only can construct their own preset with `createSpawnTool()`.\n get tools() {\n return { ...basicTools, spawn: createSpawnTool({ persist: true }) }\n },\n})\n","import type { AgentHooks, AgentOptions } from '../agent'\n\nexport type { AgentHookMap } from '../agent'\n\n/**\n * A preset is a reusable slice of `AgentOptions` — spread it into `createAgent()`\n * to configure tools, a default system prompt, aliases, behavior defaults, and\n * agent-lifetime hooks.\n *\n * `provider`, `execution`, `session`, and `mcpConnector` are excluded — they're\n * ambient / per-invocation runtime wiring (a custom `mcpConnector` is the MCP\n * connection seam, often closure-bound to a transport / auth provider), so\n * presets stay shareable and composable.\n *\n * ```ts\n * import { basic } from 'zidane/presets'\n * createAgent({ ...basic, provider })\n * ```\n *\n * ### Composing multiple presets\n *\n * Bare `...spread` is shallow — `{ ...a, ...b }` overwrites every key `b`\n * defines, including `hooks`. Use {@link composePresets} when you want\n * field-aware merging (per-event hook concat, tools shallow-merge, etc.):\n *\n * ```ts\n * createAgent({ ...composePresets(basic, telemetry, mine), provider })\n * ```\n */\nexport type Preset = Omit<Partial<AgentOptions>, 'provider' | 'execution' | 'session' | 'mcpConnector'>\n\n/**\n * Identity helper for type inference when defining a preset.\n */\nexport function definePreset(config: Preset): Preset {\n return config\n}\n\n/**\n * Field-aware composition of presets. Right-most preset wins for scalar fields;\n * objects shallow-merge; arrays and hook handler lists concatenate. Designed so\n * stacking presets does the obvious thing without the spread-collision footgun:\n *\n * - `name`, `system`, `eager`, `skills` → last-defined wins\n * - `tools`, `toolAliases`, `behavior` → shallow-merge (later keys override)\n * - `behavior.dedupTools`, `behavior.toolBudgets` → **deep-merge** (per-tool-name; later wins on collision)\n * - `mcpServers` → concat with last-wins on `name` collision\n * - `hooks` → per-event concat; every handler fires\n *\n * `hooks` always emerges as `event → handler[]` so downstream registration\n * (in `createAgent`) sees a uniform shape. Order of handlers within an event\n * follows preset order: earlier presets register first.\n *\n * `mcpServers` is deduped by `name` because shipping two servers with the same\n * name would trip the connector at runtime — a later preset overriding an\n * earlier preset's `github` server is the practical intent.\n *\n * `behavior.dedupTools` and `behavior.toolBudgets` get the same per-key deep-merge\n * because they are tool-name-keyed records — a preset that ships a dedup hasher\n * for one tool should not erase a hasher another preset ships for a different\n * tool. Last-wins still applies on a per-tool collision so a downstream preset\n * can override an upstream preset's policy for one specific tool. Other\n * `behavior` fields keep last-wins semantics.\n */\nexport function composePresets(...presets: Preset[]): Preset {\n const out: Preset = {}\n const hooksByEvent: { [K in keyof AgentHooks]?: AgentHooks[K][] } = {}\n // Keep mcpServers in source-order on first sight, but allow later\n // declarations to override earlier ones with the same `name`. A `Map`\n // keyed by name gives O(1) override + stable iteration.\n const mcpByName = new Map<string, NonNullable<Preset['mcpServers']>[number]>()\n\n for (const p of presets) {\n if (p.name !== undefined)\n out.name = p.name\n if (p.system !== undefined)\n out.system = p.system\n if (p.eager !== undefined)\n out.eager = p.eager\n if (p.skills !== undefined)\n out.skills = p.skills\n if (p.tools)\n out.tools = { ...out.tools, ...p.tools }\n if (p.toolAliases)\n out.toolAliases = { ...out.toolAliases, ...p.toolAliases }\n if (p.behavior) {\n // Top-level shallow-merge first; then deep-merge the two tool-name-keyed\n // sub-records so per-tool entries from earlier presets aren't clobbered.\n const merged: NonNullable<Preset['behavior']> = { ...out.behavior, ...p.behavior }\n if (out.behavior?.dedupTools || p.behavior.dedupTools) {\n merged.dedupTools = { ...out.behavior?.dedupTools, ...p.behavior.dedupTools }\n }\n if (out.behavior?.toolBudgets || p.behavior.toolBudgets) {\n merged.toolBudgets = { ...out.behavior?.toolBudgets, ...p.behavior.toolBudgets }\n }\n out.behavior = merged\n }\n if (p.mcpServers) {\n for (const server of p.mcpServers)\n mcpByName.set(server.name, server)\n }\n if (p.runTransforms)\n out.runTransforms = [...(out.runTransforms ?? []), ...p.runTransforms]\n if (p.behaviorTransforms)\n out.behaviorTransforms = [...(out.behaviorTransforms ?? []), ...p.behaviorTransforms]\n if (p.toolCatalogTransforms)\n out.toolCatalogTransforms = [...(out.toolCatalogTransforms ?? []), ...p.toolCatalogTransforms]\n if (p.mcpConfigTransforms)\n out.mcpConfigTransforms = [...(out.mcpConfigTransforms ?? []), ...p.mcpConfigTransforms]\n if (p.skillPolicies)\n out.skillPolicies = [...(out.skillPolicies ?? []), ...p.skillPolicies]\n if (p.toolResultMetadata)\n out.toolResultMetadata = [...(out.toolResultMetadata ?? []), ...p.toolResultMetadata]\n if (p.hooks) {\n for (const [event, handler] of Object.entries(p.hooks)) {\n if (handler === undefined)\n continue\n const list = Array.isArray(handler) ? handler : [handler]\n const key = event as keyof AgentHooks\n // Safe cast: we read the loose `AgentHookMap` shape (handler-or-array)\n // and re-emit only as arrays. Each `list` element matches the event's\n // handler signature by construction (the input was typed `AgentHookMap`).\n const bucket = (hooksByEvent[key] ??= []) as unknown[]\n bucket.push(...(list as unknown[]))\n }\n }\n }\n\n if (mcpByName.size > 0)\n out.mcpServers = [...mcpByName.values()]\n\n if (Object.keys(hooksByEvent).length > 0)\n out.hooks = hooksByEvent\n\n return out\n}\n\nexport { default as basic, basicTools } from './basic'\n","/**\n * Host runtime bridge — the late-bound capability surface behind\n * `ctx.runtime`, `ctx.sessions`, `ctx.appendNote`, and `ctx.workspace`.\n *\n * Extension contexts are built before the host mounts, so these methods\n * route through a holder the host populates post-mount (the same idiom as\n * the HUD and prompt bridge). Detached degradation is split by stakes:\n * queries and toggles (`runtime` / `sessions` / `appendNote`) return\n * honest `null` / `false` — extensions guard on the return value instead\n * of try/catching host plumbing — while `workspace` operations REJECT,\n * because a silent no-op `writeFile` masquerading as success is worse\n * than an error (see {@link ExtensionWorkspaceSurface}).\n *\n * Notes deliberately reuse the command-output vocabulary\n * ({@link CommandOutputBlock}: full markdown or collapsed diff-colorable\n * details, or `custom` blocks bound to a registered view) and persist as\n * display-only turns — excluded from the model by the same wire guard as\n * command output. What you append is what reloads: even `custom` blocks\n * persist DATA + a mandatory markdown fallback, never rendered output —\n * the renderer registry (`ctx.tui.registerRenderer`) is a pure view\n * layer resolved at render time, so a missing/disabled renderer degrades\n * to the fallback instead of corrupting history.\n */\n\nimport type { CommandOutputBlock } from '../chat/commands'\nimport type { ModelInfo } from '../chat/providers'\nimport type { ExecResult, ExecutionContext, ExecutionHandle, ListFilesEntry, ListFilesOptions } from '../contexts'\nimport type { ThinkingLevel } from '../types'\n\n/**\n * One row of `ctx.runtime.models()` — a model offered by a provider the\n * user has credentials for. `provider.key` is the registry key (stable id,\n * e.g. `anthropic`); `provider.name` is the display name. `model` mirrors\n * the host's {@link ModelInfo} (id, context window, cost, …).\n */\nexport interface ExtensionModelEntry {\n provider: { key: string, name: string }\n model: ModelInfo\n}\n\n/** One row of `ctx.sessions.list()`. */\nexport interface ExtensionSessionSummary {\n id: string\n title?: string\n updatedAt?: number\n}\n\n/**\n * The live `(execution, handle)` pair behind `ctx.workspace`. Resolved\n * per call — never cached by the facade — because the pair changes on\n * session switch, cwd change, and registry rebuilds; a captured pair\n * would point at a destroyed container.\n */\nexport interface ExtensionWorkspacePair {\n execution: ExecutionContext\n handle: ExecutionHandle\n}\n\n/** What a host attaches to power the runtime surfaces. */\nexport interface ExtensionHostRuntimeImpl {\n model: () => string | null\n /**\n * Model catalog across every provider the user has credentials for.\n * Optional so pre-catalog host impls keep typechecking; the facade\n * treats absence as an empty catalog.\n */\n models?: () => readonly ExtensionModelEntry[]\n thinking: () => ThinkingLevel | null\n setThinking: (level: ThinkingLevel) => boolean\n listSessions: () => Promise<readonly ExtensionSessionSummary[]>\n switchSession: (id: string) => Promise<boolean>\n createSession: () => Promise<boolean>\n appendNote: (blocks: readonly CommandOutputBlock[]) => boolean\n /**\n * Resolve the ACTIVE session's execution pair, spawning the handle if\n * no run has created it yet (`agent.ensureExecution()`). `null` when\n * no agent/session is live; REJECT when a live agent's spawn fails\n * (daemon down, spawn timeout) so the facade surfaces the real error\n * instead of a generic \"detached\". Optional so pre-workspace host\n * impls keep typechecking; the facade treats absence as detached.\n */\n workspace?: () => Promise<ExtensionWorkspacePair | null>\n /**\n * Side-effect-free availability signal (\"is there a live agent?\").\n * MUST NOT spawn: `workspace()` may boot a container on first call,\n * and `ctx.workspace.available()` — a guard extensions call casually,\n * even at setup — must never pay that price to answer a boolean.\n * Optional; when absent the facade falls back to `workspace()` (with\n * its side effects — implement this on any host with heavy spawns).\n */\n workspaceAvailable?: () => boolean\n}\n\nexport interface ExtensionHostRuntimeBridge {\n attach: (impl: ExtensionHostRuntimeImpl) => () => void\n current: () => ExtensionHostRuntimeImpl | null\n}\n\nexport function createExtensionHostRuntimeBridge(): ExtensionHostRuntimeBridge {\n let impl: ExtensionHostRuntimeImpl | null = null\n return {\n attach(next) {\n impl = next\n return () => {\n if (impl === next)\n impl = null\n }\n },\n current: () => impl,\n }\n}\n\n/** `ctx.runtime` — reads/controls over the host's active model state. */\nexport interface ExtensionRuntimeSurface {\n /** Active model id, or `null` when detached / none picked. */\n model: () => string | null\n /**\n * Every model available across the user's credentialed providers.\n * Empty when detached (headless, or before the renderer mounts).\n * Use it to build model pickers (e.g. a review command letting the\n * user pin subagents to specific models).\n */\n models: () => readonly ExtensionModelEntry[]\n /** Active thinking level, or `null` when detached / not applicable. */\n thinking: () => ThinkingLevel | null\n /** Set the thinking level. `false` when detached or unsupported by the model. */\n setThinking: (level: ThinkingLevel) => boolean\n}\n\n/** `ctx.sessions` — minimal, imperative-and-done session control. */\nexport interface ExtensionSessionsSurface {\n /** Sessions visible to the host's picker. Empty when detached. */\n list: () => Promise<readonly ExtensionSessionSummary[]>\n /** Switch to a session by id. `false` when detached or the switch failed. */\n switch: (id: string) => Promise<boolean>\n /** Start a fresh session. `false` when detached. */\n create: () => Promise<boolean>\n}\n\n/**\n * `ctx.workspace` — shell + file operations routed through the SESSION'S\n * execution context, so extension work happens wherever the agent's tools\n * run (host process, docker, remote sandbox) with the same transport\n * output caps and containment. The curated \"do work in the workspace\"\n * subset — lifecycle methods (`spawn` / `destroy` / background tasks)\n * are deliberately absent: the pair belongs to the agent.\n *\n * Unlike `ctx.runtime` / `ctx.sessions` (queries and toggles that degrade\n * to `null` / `false`), these are imperative operations whose silent\n * failure would corrupt caller logic — a no-op `writeFile` must not look\n * like success. Detached ⇒ every method REJECTS with a clear error.\n * Guard with {@link ExtensionWorkspaceSurface.available} or try/catch.\n */\nexport interface ExtensionWorkspaceSurface {\n /** `true` when a live session's execution pair is reachable. */\n available: () => Promise<boolean>\n /**\n * Run a shell command in the workspace. Resolves with the full\n * ExecResult (non-zero exits RESOLVE — inspect `exitCode`); rejects\n * only when detached or the transport itself fails. Output is\n * byte-capped by the context's transport (10MB default).\n */\n exec: (command: string, options?: { cwd?: string, env?: Record<string, string>, timeout?: number, signal?: AbortSignal }) => Promise<ExecResult>\n /** Read a file from the workspace's filesystem (whole-file — probe `fileSize` first for anything potentially large). */\n readFile: (path: string) => Promise<string>\n /** Write a file into the workspace's filesystem (parent dirs created). */\n writeFile: (path: string, content: string) => Promise<void>\n /** List a directory in the workspace — `ListFilesOptions` semantics (`glob: true` treats `path` as a pattern). */\n listFiles: (path: string, options?: ListFilesOptions) => Promise<ListFilesEntry[]>\n /** Byte size of a file, or `null` when unknowable — same contract as `ExecutionContext.fileSize`. */\n fileSize: (path: string) => Promise<number | null>\n}\n\n/**\n * Build the `ctx.workspace` facade over a host-runtime bridge. Every call\n * re-resolves the live pair (see {@link ExtensionWorkspacePair}); detached\n * calls reject with a message naming the guard.\n *\n * `isAvailable` is the host's side-effect-free availability signal,\n * tri-state by design: `true`/`false` answer directly, `undefined` means\n * \"no light signal wired\" and falls back to `resolve()` — accepting its\n * side effects (a container spawn, on heavy hosts). Wire it wherever\n * spawning is non-trivial.\n */\nexport function createWorkspaceSurface(\n resolve: () => Promise<ExtensionWorkspacePair | null> | ExtensionWorkspacePair | null,\n isAvailable?: () => boolean | undefined,\n): ExtensionWorkspaceSurface {\n const pair = async (): Promise<ExtensionWorkspacePair> => {\n // Spawn/transport failures from `resolve` propagate as-is — a docker\n // daemon being down must read as that, not as \"detached\".\n const resolved = await resolve()\n if (!resolved)\n throw new Error('ctx.workspace is detached — no live session/host. Guard with `await ctx.workspace.available()`.')\n return resolved\n }\n return {\n available: async () => {\n // Prefer the host's side-effect-free signal — `resolve()` may spawn\n // a container, which a boolean guard must never trigger.\n const light = isAvailable?.()\n if (light !== undefined)\n return light\n try {\n return await resolve() !== null\n }\n catch {\n return false\n }\n },\n exec: async (command, options) => {\n const { execution, handle } = await pair()\n return execution.exec(handle, command, options)\n },\n readFile: async (path) => {\n const { execution, handle } = await pair()\n return execution.readFile(handle, path)\n },\n writeFile: async (path, content) => {\n const { execution, handle } = await pair()\n return execution.writeFile(handle, path, content)\n },\n listFiles: async (path, options) => {\n const { execution, handle } = await pair()\n return execution.listFiles(handle, path, options)\n },\n fileSize: async (path) => {\n const { execution, handle } = await pair()\n return await execution.fileSize?.(handle, path) ?? null\n },\n }\n}\n","/**\n * Run each loaded extension's `setup(ctx)` and fold its contributions\n * into a single {@link ExtensionRegistry}.\n *\n * Two contracts the rest of zidane relies on:\n *\n * - **Isolation** — a throw inside `setup` lands on the offending\n * extension as `LoadedExtension.error` (so the Settings panel can\n * surface it) but never propagates. Every other extension still\n * gets to register.\n * - **Late-call gating** — once `setup` resolves, the context's\n * registration methods become no-ops. Extensions that schedule\n * work for later (a `setTimeout`, a Promise resolved by an\n * external event) can't smuggle contributions in after the\n * registry is sealed. A `logger.warn` is emitted so the author\n * can spot the misuse.\n *\n * Cross-extension collisions are resolved here too: last-write-wins\n * for keyed maps (tools / themes / agents / providers), append-order\n * for arrays (mcpServers / skills / system fragments / completion\n * providers / TUI / GUI surfaces). Hook handlers are bucketed per\n * event so every extension fires.\n */\n\nimport type { AgentHooks } from '../agent'\nimport type { AgentProfile } from '../chat/agents'\nimport type { Command } from '../chat/commands'\nimport type { CompletionProvider } from '../chat/completion'\nimport type { ContextExtensionRow, ContextExtensionSection } from '../chat/context-breakdown'\nimport type { InlineAutocompleteProvider } from '../chat/inline-autocomplete'\nimport type { ProviderDescriptor } from '../chat/providers'\nimport type { Theme } from '../chat/theme'\nimport type { Settings } from '../chat/types'\nimport type { Logger, LogSink } from '../logger'\nimport type { McpServerConfig } from '../mcp'\nimport type { Preset } from '../presets'\nimport type { SkillConfig } from '../skills'\nimport type { SubagentDef, SubagentOverride } from '../tools/spawn'\nimport type { ToolDef } from '../tools/types'\nimport type { SessionMessage } from '../types'\nimport type {\n ExtensionConfigCategory,\n ExtensionConfigItem,\n StoredExtensionConfigItem,\n} from './config'\nimport type { ExtensionHostRuntimeBridge } from './host-runtime'\nimport type { ExtensionHud, ExtensionHudWriter } from './hud'\nimport type { ExtensionPromptBridge } from './prompt-bridge'\nimport type {\n ExtensionContext,\n ExtensionContributions,\n ExtensionCustomRendererEntry,\n ExtensionDefinition,\n ExtensionFooterHint,\n ExtensionGuiPanel,\n ExtensionKeybinding,\n ExtensionModal,\n ExtensionOrderedContribution,\n ExtensionRegistry,\n ExtensionSource,\n ExtensionToolRendererEntry,\n LoadedExtension,\n} from './types'\nimport { homedir } from 'node:os'\nimport { resolve, sep } from 'node:path'\nimport {\n DEFAULT_KEYBINDINGS,\n isBareCharExtensionBinding,\n isReservedExtensionBinding,\n parseBindingSpec,\n} from '../chat/keybindings'\nimport { resolveStorageDirs } from '../chat/xdg'\nimport { errorMessage } from '../errors'\nimport { consoleSink, createLogger } from '../logger'\nimport { composePresets } from '../presets'\nimport { currentExtensionSession, deleteExtensionSessionState, getExtensionSessionState, setExtensionSessionState } from '../session'\nimport { isSpawnTool } from '../tools/spawn'\nimport { isValidExtensionConfigItem, resolveExtensionConfigValue } from './config'\nimport { createWorkspaceSurface } from './host-runtime'\nimport { withTimeout } from './with-timeout'\n\nexport interface BuildExtensionRegistryOptions {\n /**\n * Names the user has explicitly disabled. Discovered + present in\n * `loaded` but skipped during setup; surfaces in `inactive` so the\n * Settings panel can list them.\n */\n disabled?: ReadonlySet<string>\n /** Maximum time one extension's setup may take before it is marked inactive. */\n setupTimeoutMs?: number\n /**\n * Override the log sink. Defaults to a `consoleSink` writing to\n * stderr (which the TUI suppresses to keep the renderer clean —\n * only `ZIDANE_DEBUG=1` surfaces the lines).\n */\n sink?: LogSink\n /**\n * Extension HUD backing `ctx.tui.notify` / `setStatus` / `setBanner`.\n * Hosts with a live renderer (the TUI) pass one; headless hosts omit it\n * and the methods no-op. Extension footprints are wiped from it on\n * disposal.\n */\n hud?: ExtensionHud\n /**\n * Prompt bridge backing `ctx.submitPrompt`. Hosts with a submit pipeline\n * attach their handler post-mount; omitted → the method returns `false`.\n */\n promptBridge?: ExtensionPromptBridge\n /**\n * Host runtime bridge backing `ctx.runtime` / `ctx.sessions` /\n * `ctx.appendNote`. Late-bound: the host attaches its implementation\n * post-mount; detached methods return `null`/`false`.\n */\n hostRuntime?: ExtensionHostRuntimeBridge\n /**\n * Live settings getter backing `ctx.config.get`. `setup()` runs at\n * boot BEFORE the host's live settings are wired, so the context can't\n * capture a snapshot — it reads through this getter on every call.\n * During `setup` it typically returns a boot snapshot (or `undefined`);\n * inside hooks / tools it returns the host's live `Settings`. Omitted\n * → `ctx.config.get` always returns item defaults.\n */\n readSettings?: () => Partial<Settings> | undefined\n /** Runtime cwd used for ctx.paths / ctx.storage helpers. */\n cwd?: string\n projectRoot?: string\n storageDirs?: {\n configDir?: string\n dataDir: string\n stateDir?: string\n cacheDir: string\n }\n prefix?: string\n}\n\nconst DANGEROUS_RECORD_KEYS = new Set(['__proto__', 'prototype', 'constructor'])\nconst DEFAULT_TEARDOWN_TIMEOUT_MS = 5_000\n\nfunction createRecord<T>(): Record<string, T> {\n return Object.create(null) as Record<string, T>\n}\n\nfunction isSafeRecordKey(key: string): boolean {\n return key.length > 0 && !DANGEROUS_RECORD_KEYS.has(key)\n}\n\nfunction warnUnsafeKey(logger: Logger, method: string, key: string): void {\n logger.warn(`${method}: refused unsafe key`, { key })\n}\n\n/**\n * Build the merged registry. `loaded` typically comes from\n * `discoverExtensions()` plus any inline ones via `inlineLoadedExtension`.\n *\n * Disabled extensions stay in `inactive` (alongside any load errors)\n * so the Settings panel shows the full picture. The collapse-to-empty\n * `EMPTY_EXTENSION_REGISTRY` is used for the \"nothing loaded\" case.\n */\nexport async function buildExtensionRegistry(\n loaded: readonly LoadedExtension[],\n options: BuildExtensionRegistryOptions = {},\n): Promise<ExtensionRegistry> {\n const sink = options.sink ?? consoleSink({ minLevel: 'warn' })\n const disabled = options.disabled ?? new Set<string>()\n const setupTimeoutMs = options.setupTimeoutMs ?? 10_000\n\n const active: LoadedExtension[] = []\n const inactive: LoadedExtension[] = []\n const candidates: SetupCandidate[] = []\n /** Generation-scoped HUD writers, cleared (guarded) at dispose. */\n const hudWriters: ExtensionHudWriter[] = []\n const extensionNameOwners = new Set<string>()\n\n for (const entry of loaded) {\n if (extensionNameOwners.has(entry.name)) {\n inactive.push({ ...entry, error: `Duplicate extension name \"${entry.name}\" ignored; the earlier registration wins.` })\n continue\n }\n // Only entries that actually loaded a definition claim the name —\n // a disk entry that failed to load (disabled, untrusted, broken)\n // must not knock a later loadable extension into `inactive` as a\n // \"duplicate\".\n if (!entry.definition) {\n inactive.push(entry)\n continue\n }\n if (entry.definition.apiVersion !== undefined && entry.definition.apiVersion !== 1) {\n inactive.push({ ...entry, error: `Unsupported extension API version \"${entry.definition.apiVersion}\". This runtime supports apiVersion 1.` })\n continue\n }\n extensionNameOwners.add(entry.name)\n // Disabled (allowlist-off) extensions still run `setup()` — they are\n // trusted code that already imported — so their config schema is\n // declared and browsable in Settings before enabling. They classify\n // as `inactive` below, which keeps every behavioral contribution out\n // of the registry's active surface.\n const isDisabled = disabled.has(entry.name)\n\n const logger = createLogger(sink, { extension: entry.name })\n const gate: GateState = { open: true }\n // Per-extension sinks — captured on the LoadedExtension as\n // `contributions` so the host can filter on enable/disable\n // without re-running setup.\n const perExtSinks: ContextSinks = {\n tools: createRecord(),\n hooks: {},\n mcpServers: [],\n skills: [],\n systemFragments: [],\n contextBreakdownSections: [],\n contextPanelSections: [],\n toolResultMetadata: [],\n runTransforms: [],\n behaviorTransforms: [],\n providerMiddlewares: [],\n toolCatalogTransforms: [],\n mcpConfigTransforms: [],\n skillPolicies: [],\n promptTransforms: [],\n completionProviders: [],\n inlineAutocompleteProviders: [],\n commands: [],\n themes: createRecord(),\n agents: createRecord(),\n providers: createRecord(),\n subagents: createRecord(),\n subagentOverrides: createRecord(),\n configCategories: createRecord(),\n configItems: createRecord(),\n keybindings: [],\n footerHints: [],\n modals: [],\n renderers: [],\n toolRenderers: [],\n guiPanels: [],\n }\n const abort = new AbortController()\n // Generation-scoped writer: this registry build claims the extension's\n // HUD slots; a later build re-claims them, so THIS generation's\n // late writes / dispose-time clear can't touch the successor's paint.\n // Disabled extensions get NO writer: their setup runs (to declare the\n // config schema) but an inactive extension painting status lines or\n // transcript notifications would contradict its toggle.\n // Disabled extensions get NO runtime bridges: their setup runs only to\n // declare the config schema, so the HUD writer, prompt bridge, and host\n // runtime are all withheld. The context surfaces already `?.`-guard\n // these, so every runtime affordance (`tui.*`, `submitPrompt`,\n // `runtime`, `sessions`, `workspace`) degrades to an inert no-op — an\n // inactive extension can't paint status, inject prompts, or touch the\n // session. `ctx.disabled` lets the author also gate side-effecting\n // setup work (network probes, cache warm-up) that doesn't go through\n // ctx at all.\n const hudWriter = isDisabled ? undefined : options.hud?.claim(entry.name)\n if (hudWriter)\n hudWriters.push(hudWriter)\n const ctx = makeContext({\n definition: entry.definition,\n source: entry.source,\n dir: entry.dir,\n logger,\n gate,\n signal: abort.signal,\n sinks: perExtSinks,\n disabled: isDisabled,\n ...(options.readSettings !== undefined ? { readSettings: options.readSettings } : {}),\n ...(hudWriter !== undefined ? { hudWriter } : {}),\n ...(!isDisabled && options.promptBridge !== undefined ? { promptBridge: options.promptBridge } : {}),\n ...(!isDisabled && options.hostRuntime !== undefined ? { hostRuntime: options.hostRuntime } : {}),\n paths: resolveContextPaths(options),\n })\n candidates.push({\n entry: { ...entry, definition: entry.definition },\n logger,\n gate,\n abort,\n sinks: perExtSinks,\n ctx,\n setupTimeoutMs,\n disabled: isDisabled,\n })\n }\n\n const settled = await Promise.all(candidates.map(setupExtension))\n\n // Cross-extension state for collision warnings. Both maps are\n // populated in discovery order after setup settles, preserving the\n // original precedence while letting slow setup calls run in parallel.\n const keybindingIdOwners = new Map<string, string>()\n const keybindingChordOwners = new Map<string, { ext: string, id: string }>()\n const mcpServerNameOwners = new Map<string, string>()\n const rendererIdOwners = new Map<string, string>()\n\n for (const result of settled) {\n const { entry, logger, sinks: perExtSinks } = result.candidate\n if (result.ok) {\n // Disabled extensions skip the collision bookkeeping below — their\n // contributions never merge, so warning about (or claiming) chords,\n // ids, and server names on behalf of an inactive extension would\n // shadow the active ones that actually render.\n if (result.candidate.disabled) {\n inactive.push({ ...entry, contributions: captureContributions(perExtSinks), setupRan: true })\n continue\n }\n // Warn on keybinding collisions so a vibe-coded extension\n // doesn't ship an unreachable shortcut. Three buckets, all\n // resolved by \"first claimant wins, later loses\":\n // 1. Collisions with a built-in action's default chord — the\n // TUI dispatcher matches built-ins first and short-circuits.\n // 2. Collisions on the binding `id` (within or across\n // extensions) — `mergeContributions` keeps the first\n // registration in `active` order.\n // 3. Collisions on the default chord — the dispatcher would\n // fire the first match on that chord.\n // Users can resolve any of these via `keybindings.json`; we only\n // flag the AUTHOR's chosen default since that's what the\n // extension's docs likely advertise. Warnings only — the actual\n // id dedupe lives in `mergeContributions` so toggle-time\n // re-merges resolve against whatever subset is active.\n for (const binding of perExtSinks.keybindings) {\n const idOwner = keybindingIdOwners.get(binding.id)\n if (idOwner === entry.name) {\n logger.warn(\n 'duplicate keybinding id within this extension — the earlier registration wins',\n { id: binding.id },\n )\n continue\n }\n if (idOwner) {\n logger.warn(\n 'keybinding id collides with an earlier extension\\'s registration — the earlier registration wins',\n { id: binding.id, earlierExtension: idOwner },\n )\n continue\n }\n if (binding.default) {\n const builtin = findBuiltinByChord(binding.default)\n if (builtin) {\n logger.warn(\n 'keybinding default collides with a built-in action — users will need to rebind via keybindings.json',\n { id: binding.id, chord: binding.default, builtin },\n )\n }\n const normalized = canonicalChord(binding.default)\n const chordOwner = normalized ? keybindingChordOwners.get(normalized) : undefined\n if (chordOwner && chordOwner.ext === entry.name) {\n logger.warn(\n 'keybinding default collides with another binding in this extension — only the first will fire',\n { id: binding.id, chord: binding.default, earlierId: chordOwner.id },\n )\n }\n else if (chordOwner) {\n logger.warn(\n 'keybinding default collides with an earlier extension\\'s chord — the earlier registration wins',\n { id: binding.id, chord: binding.default, earlierExtension: chordOwner.ext, earlierId: chordOwner.id },\n )\n }\n if (normalized && !chordOwner)\n keybindingChordOwners.set(normalized, { ext: entry.name, id: binding.id })\n }\n keybindingIdOwners.set(binding.id, entry.name)\n }\n // Cross-extension MCP server name collisions — the merged preset\n // dedupes by name keeping the LATEST entry (see\n // `buildExtensionPreset`), so the later registration wins and the\n // earlier extension's server is shadowed. Same-extension\n // re-registrations are treated as deliberate overrides.\n for (const server of perExtSinks.mcpServers) {\n const owner = mcpServerNameOwners.get(server.name)\n if (owner && owner !== entry.name) {\n logger.warn(\n 'mcp server name collides with an earlier extension\\'s registration — this later registration wins',\n { name: server.name, earlierExtension: owner },\n )\n }\n mcpServerNameOwners.set(server.name, entry.name)\n }\n // Renderer-id collision warning — same registration-time split as\n // keybindings (merge dedupes silently; this is the author-facing\n // signal). Ids are namespaced, so only same-extension duplicates\n // can collide. Tool-renderer OVERLAP is deliberately not warned:\n // renderers chain (consulted in registration order, first non-null\n // wins, `null` falls through), so several extensions claiming the\n // same tool is a designed composition, not a conflict.\n for (const renderer of perExtSinks.renderers) {\n const owner = rendererIdOwners.get(renderer.id)\n if (owner)\n logger.warn('duplicate renderer id — the earlier registration wins', { id: renderer.id })\n else\n rendererIdOwners.set(renderer.id, entry.name)\n }\n active.push({ ...entry, contributions: captureContributions(perExtSinks), setupRan: true })\n }\n else {\n const message = result.error\n logger.error('setup threw', { error: message })\n // A DISABLED extension that throws during its schema-only setup run\n // stays toggleable: it isn't a load failure (its contributions were\n // going to be dropped regardless), and enabling triggers a full\n // reload that retries setup with real runtime bridges. Recording\n // `error` here would strand it in the errored bucket, which the\n // Settings toggle list excludes — making it impossible to enable.\n if (result.candidate.disabled)\n inactive.push({ ...entry, setupRan: true })\n else\n inactive.push({ ...entry, error: message, setupRan: true })\n }\n }\n\n let disposed = false\n const registry = mergeContributions(active, inactive, {\n dispose: async () => {\n if (disposed)\n return\n disposed = true\n // Wipe every extension's HUD footprint (status/banner) before\n // teardown — a disposed extension must leave no painted residue.\n // Writer `clear` is generation-guarded: if a newer registry already\n // re-claimed a name (swap/reload), its fresh paint survives.\n for (const writer of hudWriters)\n writer.clear()\n await disposeLoadedExtensions([...active, ...inactive], sink)\n },\n })\n return registry\n}\n\ninterface SetupCandidate {\n entry: LoadedExtension & { definition: ExtensionDefinition }\n logger: Logger\n gate: GateState\n abort: AbortController\n sinks: ContextSinks\n ctx: ExtensionContext\n setupTimeoutMs: number\n /** Allowlist-off: setup runs, contributions stay out of the active surface. */\n disabled: boolean\n}\n\n/** Freeze one extension's sinks into its `contributions` snapshot. */\nfunction captureContributions(perExtSinks: ContextSinks): ExtensionContributions {\n return {\n tools: perExtSinks.tools,\n hooks: perExtSinks.hooks,\n mcpServers: perExtSinks.mcpServers,\n skills: perExtSinks.skills,\n systemFragments: perExtSinks.systemFragments,\n contextBreakdownSections: perExtSinks.contextBreakdownSections,\n contextPanelSections: perExtSinks.contextPanelSections,\n toolResultMetadata: perExtSinks.toolResultMetadata,\n runTransforms: perExtSinks.runTransforms,\n behaviorTransforms: perExtSinks.behaviorTransforms,\n providerMiddlewares: perExtSinks.providerMiddlewares,\n toolCatalogTransforms: perExtSinks.toolCatalogTransforms,\n mcpConfigTransforms: perExtSinks.mcpConfigTransforms,\n skillPolicies: perExtSinks.skillPolicies,\n promptTransforms: perExtSinks.promptTransforms,\n completionProviders: perExtSinks.completionProviders,\n inlineAutocompleteProviders: perExtSinks.inlineAutocompleteProviders,\n commands: perExtSinks.commands,\n themes: perExtSinks.themes,\n agents: perExtSinks.agents,\n providers: perExtSinks.providers,\n subagents: perExtSinks.subagents,\n subagentOverrides: perExtSinks.subagentOverrides,\n configCategories: perExtSinks.configCategories,\n configItems: perExtSinks.configItems,\n tui: {\n keybindings: perExtSinks.keybindings,\n footerHints: perExtSinks.footerHints,\n modals: perExtSinks.modals,\n renderers: perExtSinks.renderers,\n toolRenderers: perExtSinks.toolRenderers,\n },\n gui: { panels: perExtSinks.guiPanels },\n }\n}\n\ntype SetupResult\n = | { ok: true, candidate: SetupCandidate }\n | { ok: false, candidate: SetupCandidate, error: string }\n\nasync function setupExtension(candidate: SetupCandidate): Promise<SetupResult> {\n const timeoutMessage = `setup timed out after ${candidate.setupTimeoutMs}ms`\n try {\n await withTimeout(\n Promise.resolve(candidate.entry.definition.setup(candidate.ctx)),\n candidate.setupTimeoutMs,\n timeoutMessage,\n () => {\n candidate.gate.open = false\n candidate.abort.abort(new Error(timeoutMessage))\n },\n )\n return { ok: true, candidate }\n }\n catch (err) {\n return { ok: false, candidate, error: errorMessage(err) }\n }\n finally {\n candidate.gate.open = false\n }\n}\n\nasync function disposeLoadedExtensions(entries: readonly LoadedExtension[], sink: LogSink): Promise<void> {\n const seen = new Set<string>()\n await Promise.all(entries.map(async (entry) => {\n if (!entry.setupRan || !entry.definition?.teardown || seen.has(entry.name))\n return\n seen.add(entry.name)\n const logger = createLogger(sink, { extension: entry.name })\n try {\n await withTimeout(\n Promise.resolve(entry.definition.teardown()),\n DEFAULT_TEARDOWN_TIMEOUT_MS,\n `teardown timed out after ${DEFAULT_TEARDOWN_TIMEOUT_MS}ms`,\n )\n }\n catch (err) {\n logger.error('teardown threw', { error: errorMessage(err) })\n }\n }))\n}\n\n/**\n * Fold every active extension's contributions into the merged\n * {@link ExtensionRegistry}. Pure given the input — call this with\n * any subset of `active` to recompute the merged surface (typically\n * after the user toggles an extension on/off, without re-running\n * setup).\n */\nexport function mergeContributions(\n active: readonly LoadedExtension[],\n inactive: readonly LoadedExtension[] = [],\n options: { dispose?: () => Promise<void> } = {},\n): ExtensionRegistry {\n const tools: Record<string, ToolDef> = createRecord()\n const hooks: { [E in keyof AgentHooks]?: Array<AgentHooks[E]> } = {}\n const mcpServers: McpServerConfig[] = []\n const skills: SkillConfig[] = []\n const systemFragments: string[] = []\n const contextBreakdownSections: NonNullable<ExtensionContributions['contextBreakdownSections']>[number][] = []\n const contextPanelSections: NonNullable<ExtensionContributions['contextPanelSections']>[number][] = []\n const toolResultMetadata: ExtensionContributions['toolResultMetadata'][number][] = []\n const runTransforms: ExtensionContributions['runTransforms'][number][] = []\n const behaviorTransforms: ExtensionContributions['behaviorTransforms'][number][] = []\n const providerMiddlewares: ExtensionContributions['providerMiddlewares'][number][] = []\n const toolCatalogTransforms: ExtensionContributions['toolCatalogTransforms'][number][] = []\n const mcpConfigTransforms: ExtensionContributions['mcpConfigTransforms'][number][] = []\n const skillPolicies: ExtensionContributions['skillPolicies'][number][] = []\n const promptTransforms: ExtensionContributions['promptTransforms'][number][] = []\n const completionProviders: CompletionProvider[] = []\n const inlineAutocompleteProviders: InlineAutocompleteProvider[] = []\n const seenInlineAutocompleteIds = new Set<string>()\n const commands: Command[] = []\n const seenCommandNames = new Set<string>()\n const themes: Record<string, Theme> = createRecord()\n const agents: Record<string, AgentProfile> = createRecord()\n const providers: Record<string, ProviderDescriptor> = createRecord()\n const subagents: Record<string, SubagentDef> = createRecord()\n const subagentOverrides: Record<string, SubagentOverride> = createRecord()\n const configCategories: Record<string, ExtensionConfigCategory> = createRecord()\n const configItems: Record<string, Record<string, StoredExtensionConfigItem>> = createRecord()\n const keybindings: ExtensionKeybinding[] = []\n const seenKeybindingIds = new Set<string>()\n const footerHints: ExtensionFooterHint[] = []\n const modals: ExtensionModal[] = []\n const renderers: ExtensionCustomRendererEntry[] = []\n const seenRendererIds = new Set<string>()\n const toolRenderers: ExtensionToolRendererEntry[] = []\n const guiPanels: ExtensionGuiPanel[] = []\n\n for (const entry of active) {\n const c = entry.contributions\n if (!c)\n continue\n Object.assign(tools, c.tools)\n for (const [event, list] of Object.entries(c.hooks)) {\n if (!list || list.length === 0)\n continue\n // Object.entries erases the per-event key↔handler-type correlation of\n // the hook map, so these casts re-assert what the source map's type\n // already guaranteed — both sides come from the same typed record, so\n // no unsound value can actually flow through.\n const key = event as keyof AgentHooks\n const bucket = (hooks[key] ??= []) as Array<typeof list[number]>\n bucket.push(...(list as Array<typeof list[number]>))\n }\n mcpServers.push(...c.mcpServers)\n skills.push(...c.skills)\n systemFragments.push(...c.systemFragments)\n contextBreakdownSections.push(...c.contextBreakdownSections)\n contextPanelSections.push(...c.contextPanelSections)\n toolResultMetadata.push(...c.toolResultMetadata)\n runTransforms.push(...c.runTransforms)\n behaviorTransforms.push(...c.behaviorTransforms)\n providerMiddlewares.push(...c.providerMiddlewares)\n toolCatalogTransforms.push(...c.toolCatalogTransforms)\n mcpConfigTransforms.push(...c.mcpConfigTransforms)\n skillPolicies.push(...c.skillPolicies)\n promptTransforms.push(...c.promptTransforms)\n completionProviders.push(...c.completionProviders)\n // First-wins dedupe by id (mirrors the command-name dedupe below).\n for (const provider of c.inlineAutocompleteProviders) {\n if (seenInlineAutocompleteIds.has(provider.id))\n continue\n seenInlineAutocompleteIds.add(provider.id)\n inlineAutocompleteProviders.push(provider)\n }\n // First-wins dedupe by name in `active` order, so a toggle-time re-merge\n // lets a previously-shadowed command surface once its winner is disabled\n // (mirrors the keybinding-id dedupe below). Case-insensitive to match\n // `parseCommandInvocation`.\n for (const command of c.commands) {\n const key = command.name.toLowerCase()\n if (seenCommandNames.has(key))\n continue\n seenCommandNames.add(key)\n // Aliases share the name namespace: reserve them so a later command\n // named like an earlier alias doesn't register unreachable.\n for (const alias of command.aliases ?? [])\n seenCommandNames.add(alias.toLowerCase())\n // Stamp the source extension as the completion group (unless the\n // command chose its own) — lets `/<extension>` list its commands.\n commands.push(command.group === undefined ? { ...command, group: entry.name } : command)\n }\n Object.assign(themes, c.themes)\n Object.assign(agents, c.agents)\n Object.assign(providers, c.providers)\n // Subagent adds — last-wins on name collision (mirrors tools / agents).\n Object.assign(subagents, c.subagents)\n // Subagent overrides — compose across extensions in registration\n // order, so a later extension's edit wraps the earlier one\n // (`fn(prev(def))`). Applied (by the host) after adds, last.\n for (const [name, fn] of Object.entries(c.subagentOverrides)) {\n const prev = subagentOverrides[name]\n subagentOverrides[name] = prev ? def => fn(prev(def)) : fn\n }\n // Config categories merge globally (last-wins on id collision,\n // mirrors themes). Config items stay namespaced under the owning\n // extension's name so two extensions can ship the same item key and\n // the Settings UIs can group by extension.\n Object.assign(configCategories, c.configCategories)\n if (Object.keys(c.configItems).length > 0) {\n const items = createRecord<StoredExtensionConfigItem>()\n Object.assign(items, c.configItems)\n configItems[entry.name] = items\n }\n // First-wins id dedupe in `active` order — applied here (not at\n // build time) so a toggle-time re-merge lets a previously-shadowed\n // binding surface once its winner is disabled. Collision warnings\n // are emitted at build time in `buildExtensionRegistry`.\n for (const binding of c.tui.keybindings) {\n if (seenKeybindingIds.has(binding.id))\n continue\n seenKeybindingIds.add(binding.id)\n keybindings.push(binding)\n }\n footerHints.push(...c.tui.footerHints)\n modals.push(...c.tui.modals)\n // Renderer ids are `<extension>/<id>`-namespaced at registration, so\n // cross-extension collisions are impossible; a duplicate here means\n // one extension registered the same id twice. Silent first-wins —\n // the WARNING fires at registration time in `buildExtensionRegistry`\n // (same split as keybindings: merge dedupes, build warns), so\n // toggle-time re-merges stay quiet.\n for (const renderer of c.tui.renderers) {\n if (seenRendererIds.has(renderer.id))\n continue\n seenRendererIds.add(renderer.id)\n renderers.push(renderer)\n }\n // Tool renderers merge in `active` order; the render-time resolver\n // walks in order and takes the first non-null — so \"first registered\n // wins\" falls out of ordering, no dedupe needed here.\n toolRenderers.push(...c.tui.toolRenderers)\n guiPanels.push(...c.gui.panels)\n }\n\n // Disabled-but-set-up extensions keep their CONFIG SCHEMA visible —\n // categories + items are inert metadata, and exposing them is what lets\n // the Settings UIs render (and persist values for) an extension's\n // settings BEFORE it's enabled. Behavioral contributions (tools, hooks,\n // providers, commands, …) stay gated to `active` above. First-wins\n // against active registrations so an inactive extension can never\n // relabel an active extension's category.\n for (const entry of inactive) {\n const c = entry.contributions\n if (entry.error || !c)\n continue\n for (const [id, category] of Object.entries(c.configCategories)) {\n if (!(id in configCategories))\n configCategories[id] = category\n }\n if (Object.keys(c.configItems).length > 0 && !(entry.name in configItems)) {\n const items = createRecord<StoredExtensionConfigItem>()\n Object.assign(items, c.configItems)\n configItems[entry.name] = items\n }\n }\n\n return {\n loaded: active,\n inactive,\n tools,\n hooks,\n mcpServers,\n skills,\n systemFragments,\n contextBreakdownSections,\n contextPanelSections,\n toolResultMetadata,\n runTransforms,\n behaviorTransforms,\n providerMiddlewares,\n toolCatalogTransforms,\n mcpConfigTransforms,\n skillPolicies,\n promptTransforms,\n completionProviders,\n inlineAutocompleteProviders,\n commands,\n themes,\n agents,\n providers,\n subagents,\n subagentOverrides,\n configCategories,\n configItems,\n tuiContributions: { keybindings, footerHints, modals, renderers, toolRenderers },\n guiContributions: { panels: guiPanels },\n dispose: options.dispose ?? (async () => {}),\n }\n}\n\n/**\n * Find a built-in `KeyAction` whose default chord matches `chord`. Used\n * by the collision warning above. Returns `null` when no built-in\n * uses that chord. Comparison goes through the same parser as runtime\n * dispatch, so aliases like `control` / `esc` canonicalize correctly.\n */\nfunction findBuiltinByChord(chord: string): string | null {\n const target = canonicalChord(chord)\n if (!target)\n return null\n for (const [action, builtinChord] of Object.entries(DEFAULT_KEYBINDINGS)) {\n if (!builtinChord)\n continue\n if (canonicalChord(builtinChord) === target)\n return action\n }\n return null\n}\n\nfunction canonicalChord(spec: string): string | null {\n const parsed = parseBindingSpec(spec)\n if (!parsed)\n return null\n return JSON.stringify(parsed)\n}\n\n/**\n * Collapse the extension registry's agent-side surface into a single\n * {@link Preset} ready to spread into `createAgent()`. Hooks bucket\n * per-event (every handler fires, copied into fresh arrays), tools\n * shallow-merge (last extension wins on canonical-name collision),\n * mcpServers concat with name-dedup (latest wins — mirrors\n * `composePresets`), skills concat. System-prompt fragments DON'T land\n * here — hosts that re-derive `system` per session inject them via\n * `system:transform` instead, so we don't fight them by also baking\n * the fragments into `preset.system`.\n */\nexport function buildExtensionPreset(registry: ExtensionRegistry): Preset {\n const preset: Preset = {}\n if (Object.keys(registry.tools).length > 0)\n preset.tools = { ...registry.tools }\n if (registry.mcpServers.length > 0) {\n const byName = new Map<string, typeof registry.mcpServers[number]>()\n for (const server of registry.mcpServers)\n byName.set(server.name, server)\n preset.mcpServers = [...byName.values()]\n }\n if (registry.skills.length > 0) {\n preset.skills = { catalog: [...registry.skills] }\n }\n // Hook handler arrays are copied so a consumer mutating the preset\n // can't poison the shared registry (and vice versa).\n const hookEntries = Object.entries(registry.hooks)\n if (hookEntries.length > 0) {\n const hooks: Record<string, unknown[]> = {}\n for (const [event, list] of hookEntries)\n hooks[event] = [...(list as readonly unknown[])]\n preset.hooks = hooks as Preset['hooks']\n }\n if (registry.runTransforms.length > 0)\n preset.runTransforms = [...registry.runTransforms]\n if (registry.behaviorTransforms.length > 0)\n preset.behaviorTransforms = [...registry.behaviorTransforms]\n if (registry.toolCatalogTransforms.length > 0)\n preset.toolCatalogTransforms = [...registry.toolCatalogTransforms]\n if (registry.mcpConfigTransforms.length > 0)\n preset.mcpConfigTransforms = [...registry.mcpConfigTransforms]\n if (registry.skillPolicies.length > 0)\n preset.skillPolicies = [...registry.skillPolicies]\n if (registry.toolResultMetadata.length > 0)\n preset.toolResultMetadata = [...registry.toolResultMetadata]\n return preset\n}\n\n/**\n * Compose extension agent-loop contributions under a host/profile preset and\n * apply extension subagent additions/overrides to the winning spawn tool.\n */\nexport function composeExtensionPreset(registry: ExtensionRegistry, explicitPreset: Preset = {}): Preset {\n const composed = composePresets(buildExtensionPreset(registry), explicitPreset)\n const spawn = composed.tools?.spawn\n if (\n spawn\n && isSpawnTool(spawn)\n && (Object.keys(registry.subagents).length > 0 || Object.keys(registry.subagentOverrides).length > 0)\n ) {\n composed.tools = {\n ...composed.tools,\n spawn: spawn.withSubagents(registry.subagents, registry.subagentOverrides),\n }\n }\n return composed\n}\n\n/**\n * Filter the extension registry by the user's enable allowlist. Returns\n * the original registry by reference when `enabled` is `undefined` (the\n * fresh-install default — every loaded extension is active), or a fresh\n * registry rebuilt via {@link mergeContributions} when the user has\n * explicitly toggled something off.\n *\n * The identity check is intentional — callers can compare the result\n * against the input registry to skip recomposing the preset when\n * nothing has changed.\n */\nexport function filterExtensionRegistry(\n registry: ExtensionRegistry,\n enabled: readonly string[] | undefined,\n): ExtensionRegistry {\n if (enabled === undefined)\n return registry\n const allow = new Set(enabled)\n const filteredActive = registry.loaded.filter(ext => allow.has(ext.name))\n // No change → return original. This is the common case once the\n // user has a stable allowlist (or a brand-new extension hasn't\n // been added yet); skipping the rebuild keeps re-renders cheap.\n if (filteredActive.length === registry.loaded.length)\n return registry\n // Build the inactive set: original inactive (load errors / pre-\n // existing disabled) plus the newly-filtered-out actives. We keep\n // them around so the Settings panel still surfaces every known\n // extension regardless of toggle state.\n const filteredOut = registry.loaded.filter(ext => !allow.has(ext.name))\n return mergeContributions(filteredActive, [...registry.inactive, ...filteredOut])\n}\n\n/** Dispose resources owned by an extension registry. Idempotent per registry. */\nexport async function disposeExtensionRegistry(registry: ExtensionRegistry): Promise<void> {\n await registry.dispose()\n}\n\n/** Shape returned by {@link partitionExtensionCatalog}. */\nexport interface ExtensionCatalogPartition {\n /**\n * Rows the user can toggle: active extensions plus disabled-but-\n * loadable ones, sorted by name so the list order is stable across\n * boots regardless of where each entry landed in the registry.\n */\n toggleable: readonly LoadedExtension[]\n /** Entries whose import or `setup()` failed — read-only diagnostics. */\n errored: readonly LoadedExtension[]\n /**\n * Every known extension name — toggleable AND errored. Use this as\n * the GC key set for the persisted `enabledExtensions` allowlist so\n * an untrusted / temporarily-broken extension isn't permanently\n * dropped from the allowlist by an unrelated toggle.\n */\n allNames: readonly string[]\n}\n\n/**\n * Partition a registry's `loaded` + `inactive` lists into the shape the\n * Settings toggle UIs render. Pure; shared by the TUI's standalone\n * extensions modal and the Settings-modal tab so the rules (and the\n * allowlist-GC key set) stay in one place.\n */\nexport function partitionExtensionCatalog(\n loaded: readonly LoadedExtension[],\n inactive: readonly LoadedExtension[],\n): ExtensionCatalogPartition {\n const errored = inactive.filter(e => !!e.error)\n const disabledOk = inactive.filter(e => !e.error)\n const toggleable = [...loaded, ...disabledOk].sort((a, b) => a.name.localeCompare(b.name))\n return {\n toggleable,\n errored,\n allNames: [...toggleable.map(e => e.name), ...errored.map(e => e.name)],\n }\n}\n\n// ---------------------------------------------------------------------------\n// Context factory\n// ---------------------------------------------------------------------------\n\ninterface GateState { open: boolean }\n\ninterface ContextSinks {\n tools: Record<string, ToolDef>\n hooks: { [E in keyof AgentHooks]?: Array<AgentHooks[E]> }\n mcpServers: McpServerConfig[]\n skills: SkillConfig[]\n systemFragments: string[]\n contextBreakdownSections: ExtensionContributions['contextBreakdownSections'][number][]\n contextPanelSections: ExtensionContributions['contextPanelSections'][number][]\n toolResultMetadata: ExtensionContributions['toolResultMetadata'][number][]\n runTransforms: ExtensionContributions['runTransforms'][number][]\n behaviorTransforms: ExtensionContributions['behaviorTransforms'][number][]\n providerMiddlewares: ExtensionContributions['providerMiddlewares'][number][]\n toolCatalogTransforms: ExtensionContributions['toolCatalogTransforms'][number][]\n mcpConfigTransforms: ExtensionContributions['mcpConfigTransforms'][number][]\n skillPolicies: ExtensionContributions['skillPolicies'][number][]\n promptTransforms: ExtensionContributions['promptTransforms'][number][]\n completionProviders: CompletionProvider[]\n inlineAutocompleteProviders: InlineAutocompleteProvider[]\n commands: Command[]\n themes: Record<string, Theme>\n agents: Record<string, AgentProfile>\n providers: Record<string, ProviderDescriptor>\n subagents: Record<string, SubagentDef>\n subagentOverrides: Record<string, SubagentOverride>\n configCategories: Record<string, ExtensionConfigCategory>\n configItems: Record<string, StoredExtensionConfigItem>\n keybindings: ExtensionKeybinding[]\n footerHints: ExtensionFooterHint[]\n modals: ExtensionModal[]\n renderers: ExtensionCustomRendererEntry[]\n toolRenderers: ExtensionToolRendererEntry[]\n guiPanels: ExtensionGuiPanel[]\n}\n\ninterface MakeContextOptions {\n definition: ExtensionDefinition\n source: ExtensionSource\n dir?: string\n logger: Logger\n gate: GateState\n signal: AbortSignal\n sinks: ContextSinks\n /** Allowlist-off: `ctx.disabled` is true and runtime bridges are withheld. */\n disabled?: boolean\n /** Live settings getter backing `ctx.config.get` (see {@link BuildExtensionRegistryOptions.readSettings}). */\n readSettings?: () => Partial<Settings> | undefined\n paths: ExtensionContext['paths']\n /** This extension's generation-scoped HUD writer (claimed by the registry build). */\n hudWriter?: ExtensionHudWriter\n /** Prompt bridge (see {@link BuildExtensionRegistryOptions.promptBridge}). */\n promptBridge?: ExtensionPromptBridge\n /** Host runtime bridge (see {@link BuildExtensionRegistryOptions.hostRuntime}). */\n hostRuntime?: ExtensionHostRuntimeBridge\n}\n\nfunction makeContext(opts: MakeContextOptions): ExtensionContext {\n const { definition, source, dir, logger, gate, signal, sinks, readSettings, paths, hudWriter, promptBridge, hostRuntime, disabled = false } = opts\n\n function guardOpen(method: string): boolean {\n if (gate.open)\n return true\n logger.warn('registration after setup() resolved — call ignored', { method })\n return false\n }\n\n function ordered<T>(value: T): ExtensionOrderedContribution<T> {\n return { extensionName: definition.name, source, value }\n }\n\n const registerTool: ExtensionContext['registerTool'] = (name, tool) => {\n if (!guardOpen('registerTool'))\n return\n if (typeof name !== 'string' || !name) {\n logger.warn('registerTool: name must be a non-empty string')\n return\n }\n if (!isSafeRecordKey(name)) {\n warnUnsafeKey(logger, 'registerTool', name)\n return\n }\n sinks.tools[name] = tool\n }\n\n const registerHook: ExtensionContext['registerHook'] = (event, handler) => {\n if (!guardOpen('registerHook'))\n return\n if (typeof handler !== 'function') {\n logger.warn('registerHook: handler must be a function', { event: String(event) })\n return\n }\n // Bucket per event. Each bucket holds handlers compatible with this\n // event's signature; the cast collapses the union member down to its\n // own array slot.\n const bucket = (sinks.hooks[event] ??= []) as Array<typeof handler>\n bucket.push(handler)\n }\n\n const registerMcpServer: ExtensionContext['registerMcpServer'] = (config) => {\n if (!guardOpen('registerMcpServer'))\n return\n if (!config || typeof config.name !== 'string') {\n logger.warn('registerMcpServer: missing name')\n return\n }\n sinks.mcpServers.push(config)\n }\n\n const registerSkill: ExtensionContext['registerSkill'] = (skill) => {\n if (!guardOpen('registerSkill'))\n return\n if (!skill || typeof skill.name !== 'string') {\n logger.warn('registerSkill: missing name')\n return\n }\n // Default the skill's source to the extension's own scope so a\n // project / user extension's skills carry the right badge —\n // `ExtensionSource` and `SkillSource` share the same vocabulary.\n sinks.skills.push({ ...skill, source: skill.source ?? source })\n }\n\n const registerCompletionProvider: ExtensionContext['registerCompletionProvider'] = (provider) => {\n if (!guardOpen('registerCompletionProvider'))\n return\n if (!provider || typeof provider.id !== 'string' || typeof provider.trigger !== 'string') {\n logger.warn('registerCompletionProvider: missing id or trigger')\n return\n }\n if (Array.from(provider.trigger).length !== 1) {\n logger.warn('registerCompletionProvider: trigger must be exactly one character', { id: provider.id, trigger: provider.trigger })\n return\n }\n sinks.completionProviders.push(provider)\n }\n\n const registerInlineAutocompleteProvider: ExtensionContext['registerInlineAutocompleteProvider'] = (provider) => {\n if (!guardOpen('registerInlineAutocompleteProvider'))\n return\n if (!provider || typeof provider.id !== 'string' || provider.id.length === 0) {\n logger.warn('registerInlineAutocompleteProvider: missing id')\n return\n }\n if (typeof provider.suggest !== 'function') {\n logger.warn('registerInlineAutocompleteProvider: needs a suggest()', { id: provider.id })\n return\n }\n sinks.inlineAutocompleteProviders.push(provider)\n }\n\n const registerCommand: ExtensionContext['registerCommand'] = (command) => {\n if (!guardOpen('registerCommand'))\n return\n if (!command || typeof command.name !== 'string' || command.name.length === 0) {\n logger.warn('registerCommand: missing name')\n return\n }\n if (/\\s/.test(command.name) || command.name.startsWith('/')) {\n logger.warn('registerCommand: name must be a single token without a leading slash', { name: command.name })\n return\n }\n if (typeof command.run !== 'function' && !(Array.isArray(command.steps) && command.steps.length > 0)) {\n logger.warn('registerCommand: needs a run() or a non-empty steps[]', { name: command.name })\n return\n }\n if (command.aliases !== undefined) {\n const bad = !Array.isArray(command.aliases)\n || command.aliases.some(a => typeof a !== 'string' || a.length === 0 || /\\s/.test(a) || a.startsWith('/'))\n if (bad) {\n logger.warn('registerCommand: aliases must be single tokens without a leading slash', { name: command.name })\n return\n }\n }\n sinks.commands.push(command)\n }\n\n const registerTheme: ExtensionContext['registerTheme'] = (theme) => {\n if (!guardOpen('registerTheme'))\n return\n if (!theme || typeof theme.id !== 'string') {\n logger.warn('registerTheme: missing id')\n return\n }\n if (!isSafeRecordKey(theme.id)) {\n warnUnsafeKey(logger, 'registerTheme', theme.id)\n return\n }\n sinks.themes[theme.id] = theme\n }\n\n const registerAgent: ExtensionContext['registerAgent'] = (profile) => {\n if (!guardOpen('registerAgent'))\n return\n if (!profile || typeof profile.id !== 'string') {\n logger.warn('registerAgent: missing id')\n return\n }\n if (!isSafeRecordKey(profile.id)) {\n warnUnsafeKey(logger, 'registerAgent', profile.id)\n return\n }\n sinks.agents[profile.id] = profile\n }\n\n const registerSubagent: ExtensionContext['registerSubagent'] = (def) => {\n if (!guardOpen('registerSubagent'))\n return\n if (!def || typeof def.name !== 'string' || !def.name) {\n logger.warn('registerSubagent: missing name')\n return\n }\n if (!isSafeRecordKey(def.name)) {\n warnUnsafeKey(logger, 'registerSubagent', def.name)\n return\n }\n // Store keyed by name; drop the redundant `name` field from the\n // stored def so the registry map key is the single source of truth.\n const { name, ...rest } = def\n sinks.subagents[name] = rest\n }\n\n const registerSubagentOverride: ExtensionContext['registerSubagentOverride'] = (name, fn) => {\n if (!guardOpen('registerSubagentOverride'))\n return\n if (typeof name !== 'string' || !name) {\n logger.warn('registerSubagentOverride: name must be a non-empty string')\n return\n }\n if (!isSafeRecordKey(name)) {\n warnUnsafeKey(logger, 'registerSubagentOverride', name)\n return\n }\n if (typeof fn !== 'function') {\n logger.warn('registerSubagentOverride: fn must be a function', { name })\n return\n }\n // Compose within this extension in registration order — a later\n // override for the same name wraps the earlier one (`fn(prev(def))`).\n const prev = sinks.subagentOverrides[name]\n sinks.subagentOverrides[name] = prev ? def => fn(prev(def)) : fn\n }\n\n const registerProvider: ExtensionContext['registerProvider'] = (descriptor) => {\n if (!guardOpen('registerProvider'))\n return\n if (!descriptor || typeof descriptor.key !== 'string') {\n logger.warn('registerProvider: missing key')\n return\n }\n if (!isSafeRecordKey(descriptor.key)) {\n warnUnsafeKey(logger, 'registerProvider', descriptor.key)\n return\n }\n sinks.providers[descriptor.key] = descriptor\n }\n\n const registerSystemPromptFragment: ExtensionContext['registerSystemPromptFragment'] = (text) => {\n if (!guardOpen('registerSystemPromptFragment'))\n return\n if (typeof text !== 'string' || text.length === 0)\n return\n sinks.systemFragments.push(text)\n }\n\n const registerContextBreakdownSection: ExtensionContext['registerContextBreakdownSection'] = (contributor) => {\n if (!guardOpen('registerContextBreakdownSection'))\n return\n if (typeof contributor !== 'function') {\n logger.warn('registerContextBreakdownSection: contributor must be a function')\n return\n }\n const wrapped = (ctx => contributor(ctx)) as typeof contributor & { __zidaneExtensionName?: string }\n Object.defineProperty(wrapped, '__zidaneExtensionName', {\n value: definition.name,\n enumerable: false,\n })\n sinks.contextBreakdownSections.push(wrapped)\n }\n\n const registerContextPanelSection: ExtensionContext['registerContextPanelSection'] = (section) => {\n if (!guardOpen('registerContextPanelSection'))\n return\n if (!section || typeof section.id !== 'string' || !section.id || typeof section.title !== 'string') {\n logger.warn('registerContextPanelSection: missing id or title')\n return\n }\n if (!isSafeRecordKey(section.id)) {\n warnUnsafeKey(logger, 'registerContextPanelSection', section.id)\n return\n }\n const contributor: ExtensionContributions['contextPanelSections'][number] = async (ctx) => {\n const sections: ContextExtensionSection[] = []\n const rows = typeof section.rows === 'function' ? section.rows(ctx) : section.rows\n const loaded = section.load ? await section.load(ctx) : undefined\n if (rows && rows.length > 0) {\n sections.push({ id: section.id, label: section.title, ...(section.accentColor ? { accentColor: section.accentColor } : {}), rows })\n }\n if (!loaded)\n return sections\n if (Array.isArray(loaded)) {\n if (loaded.every(row => row && typeof row === 'object' && 'value' in row)) {\n sections.push({ id: section.id, label: section.title, ...(section.accentColor ? { accentColor: section.accentColor } : {}), rows: loaded as readonly ContextExtensionRow[] })\n }\n else {\n sections.push(...loaded as readonly ContextExtensionSection[])\n }\n return sections\n }\n sections.push(loaded as ContextExtensionSection)\n return sections\n }\n Object.defineProperty(contributor, '__zidaneExtensionName', {\n value: definition.name,\n enumerable: false,\n })\n sinks.contextPanelSections.push(contributor)\n }\n\n const registerToolResultMetadata: ExtensionContext['registerToolResultMetadata'] = (registration) => {\n if (!guardOpen('registerToolResultMetadata'))\n return\n if (!registration || typeof registration.namespace !== 'string' || !registration.namespace) {\n logger.warn('registerToolResultMetadata: missing namespace')\n return\n }\n if (!isSafeRecordKey(registration.namespace)) {\n warnUnsafeKey(logger, 'registerToolResultMetadata', registration.namespace)\n return\n }\n sinks.toolResultMetadata.push(ordered(registration))\n }\n\n const registerRunTransform: ExtensionContext['registerRunTransform'] = (transform) => {\n if (!guardOpen('registerRunTransform'))\n return\n if (typeof transform !== 'function') {\n logger.warn('registerRunTransform: transform must be a function')\n return\n }\n sinks.runTransforms.push(ordered(transform))\n }\n\n const registerBehaviorTransform: ExtensionContext['registerBehaviorTransform'] = (transform) => {\n if (!guardOpen('registerBehaviorTransform'))\n return\n if (typeof transform !== 'function') {\n logger.warn('registerBehaviorTransform: transform must be a function')\n return\n }\n sinks.behaviorTransforms.push(ordered(transform))\n }\n\n const registerProviderMiddleware: ExtensionContext['registerProviderMiddleware'] = (middleware) => {\n if (!guardOpen('registerProviderMiddleware'))\n return\n if (typeof middleware !== 'function') {\n logger.warn('registerProviderMiddleware: middleware must be a function')\n return\n }\n sinks.providerMiddlewares.push(ordered(middleware))\n }\n\n const registerToolCatalogTransform: ExtensionContext['registerToolCatalogTransform'] = (transform) => {\n if (!guardOpen('registerToolCatalogTransform'))\n return\n if (typeof transform !== 'function') {\n logger.warn('registerToolCatalogTransform: transform must be a function')\n return\n }\n sinks.toolCatalogTransforms.push(ordered(transform))\n }\n\n const registerMcpConfigTransform: ExtensionContext['registerMcpConfigTransform'] = (transform) => {\n if (!guardOpen('registerMcpConfigTransform'))\n return\n if (typeof transform !== 'function') {\n logger.warn('registerMcpConfigTransform: transform must be a function')\n return\n }\n sinks.mcpConfigTransforms.push(ordered(transform))\n }\n\n const registerSkillPolicy: ExtensionContext['registerSkillPolicy'] = (policy) => {\n if (!guardOpen('registerSkillPolicy'))\n return\n if (!policy || typeof policy.name !== 'string' || !policy.name) {\n logger.warn('registerSkillPolicy: missing name')\n return\n }\n sinks.skillPolicies.push(ordered(policy))\n }\n\n const registerToolPolicy: ExtensionContext['registerToolPolicy'] = (policy) => {\n if (!guardOpen('registerToolPolicy'))\n return\n if (!policy || typeof policy.decide !== 'function') {\n logger.warn('registerToolPolicy: decide must be a function')\n return\n }\n const only = policy.tools ? new Set(policy.tools) : null\n // Rides `tool:gate`: earlier verdicts (built-in guards, approvals,\n // earlier policies) win, and any `patch` is re-validated against the\n // tool's inputSchema by the loop AFTER gating — an invalid patch\n // becomes a validation error, never an execution with unsound args.\n const gate: AgentHooks['tool:gate'] = async (gateCtx) => {\n if (gateCtx.block || gateCtx.result !== undefined)\n return\n if (only && !only.has(gateCtx.name))\n return\n let verdict: Awaited<ReturnType<typeof policy.decide>>\n try {\n verdict = await policy.decide({\n tool: gateCtx.name,\n input: gateCtx.input,\n turnId: gateCtx.turnId,\n callId: gateCtx.callId,\n })\n }\n catch (err) {\n // Isolation: a throwing policy must not take the run down.\n logger.warn('tool policy threw; allowing call', { tool: gateCtx.name, error: String(err) })\n return\n }\n if (verdict === undefined || verdict === 'allow')\n return\n if ('block' in verdict) {\n gateCtx.block = true\n gateCtx.reason = verdict.block\n return\n }\n // Full input replacement — in place, per the tool:gate mutation contract.\n for (const key of Object.keys(gateCtx.input))\n delete gateCtx.input[key]\n Object.assign(gateCtx.input, verdict.patch)\n }\n const bucket = (sinks.hooks['tool:gate'] ??= [])\n bucket.push(gate)\n }\n\n const registerToolResultTransform: ExtensionContext['registerToolResultTransform'] = (transform) => {\n if (!guardOpen('registerToolResultTransform'))\n return\n if (typeof transform !== 'function') {\n logger.warn('registerToolResultTransform: transform must be a function')\n return\n }\n const hook: AgentHooks['tool:transform'] = async (transformCtx) => {\n let patch: Awaited<ReturnType<typeof transform>>\n try {\n patch = await transform({\n tool: transformCtx.name,\n input: transformCtx.input,\n result: transformCtx.result,\n isError: transformCtx.isError,\n })\n }\n catch (err) {\n logger.warn('tool result transform threw; keeping result', { tool: transformCtx.name, error: String(err) })\n return\n }\n if (!patch)\n return\n if (patch.result !== undefined)\n transformCtx.result = patch.result\n if (patch.isError !== undefined)\n transformCtx.isError = patch.isError\n }\n const bucket = (sinks.hooks['tool:transform'] ??= [])\n bucket.push(hook)\n }\n\n const registerContextTransform: ExtensionContext['registerContextTransform'] = (transform) => {\n if (!guardOpen('registerContextTransform'))\n return\n if (typeof transform !== 'function') {\n logger.warn('registerContextTransform: transform must be a function')\n return\n }\n // Sugar over a provider middleware: rewrite `options.messages` on the\n // way into every stream call. Failures keep the original messages.\n registerProviderMiddleware(provider => ({\n ...provider,\n stream: async (streamOptions, callbacks) => {\n let next: readonly SessionMessage[] = streamOptions.messages\n try {\n next = await transform(streamOptions.messages) ?? streamOptions.messages\n }\n catch (err) {\n logger.warn('context transform threw; keeping messages', { error: String(err) })\n }\n return provider.stream(next === streamOptions.messages ? streamOptions : { ...streamOptions, messages: [...next] }, callbacks)\n },\n }))\n }\n\n const registerPromptTransform: ExtensionContext['registerPromptTransform'] = (transform) => {\n if (!guardOpen('registerPromptTransform'))\n return\n if (typeof transform !== 'function') {\n logger.warn('registerPromptTransform: transform must be a function')\n return\n }\n sinks.promptTransforms.push(ordered(transform))\n }\n\n const registerConfigCategory: ExtensionContext['registerConfigCategory'] = (category) => {\n if (!guardOpen('registerConfigCategory'))\n return\n if (!category || typeof category.id !== 'string' || !category.id || typeof category.label !== 'string') {\n logger.warn('registerConfigCategory: missing id or label')\n return\n }\n if (!isSafeRecordKey(category.id)) {\n warnUnsafeKey(logger, 'registerConfigCategory', category.id)\n return\n }\n sinks.configCategories[category.id] = category\n }\n\n const registerConfigItem: ExtensionContext['registerConfigItem'] = (categoryIdOrItem, maybeItem) => {\n if (!guardOpen('registerConfigItem'))\n return\n // Two call forms: `registerConfigItem(item)` (implicit category =\n // extension name) and `registerConfigItem(categoryId, item)`.\n const explicitCategory = typeof categoryIdOrItem === 'string' ? categoryIdOrItem : undefined\n const item = (typeof categoryIdOrItem === 'string' ? maybeItem : categoryIdOrItem) as ExtensionConfigItem | undefined\n if (!item || typeof item.key !== 'string' || !item.key) {\n logger.warn('registerConfigItem: missing item key')\n return\n }\n if (!isSafeRecordKey(item.key)) {\n warnUnsafeKey(logger, 'registerConfigItem', item.key)\n return\n }\n if (typeof item.label !== 'string') {\n logger.warn('registerConfigItem: missing label', { key: item.key })\n return\n }\n if (!isValidExtensionConfigItem(item)) {\n logger.warn('registerConfigItem: invalid item shape for its type', { key: item.key, type: (item as { type?: unknown }).type })\n return\n }\n const category = explicitCategory && explicitCategory.length > 0 ? explicitCategory : definition.name\n if (!isSafeRecordKey(category)) {\n warnUnsafeKey(logger, 'registerConfigItem: category', category)\n return\n }\n sinks.configItems[item.key] = { ...item, category } as StoredExtensionConfigItem\n }\n\n const configSurface: ExtensionContext['config'] = {\n get: (key) => {\n const item = sinks.configItems[key]\n if (!item) {\n logger.warn('config.get: unknown key — register it via registerConfigItem first', { key })\n return undefined\n }\n const persisted = readSettings?.()?.extensionConfig?.[definition.name]?.[key]\n return resolveExtensionConfigValue(item, persisted)\n },\n }\n\n const storageSurface: ExtensionContext['storage'] = {\n path: (scope, ...segments) => {\n const base = scope === 'cache'\n ? paths.cacheDir\n : scope === 'data'\n ? paths.dataDir\n : scope === 'project' || scope === 'session'\n ? (paths.projectRoot ?? paths.dataDir)\n : (paths.configDir ?? paths.dataDir)\n const root = resolve(base, 'extensions', definition.name)\n const target = resolve(root, ...segments)\n // Containment guard: `..` segments must not let an extension escape its\n // own storage root. Clamp back to the root instead of handing out a path\n // outside it.\n if (target !== root && !target.startsWith(root + sep))\n return root\n return target\n },\n }\n\n const sessionSurface: ExtensionContext['session'] = {\n get: key => getExtensionSessionState(currentExtensionSession(), definition.name, key),\n set: (key, value) => setExtensionSessionState(currentExtensionSession(), definition.name, key, value),\n delete: key => deleteExtensionSessionState(currentExtensionSession(), definition.name, key),\n }\n\n const registerKeybinding: ExtensionContext['tui']['registerKeybinding'] = (binding) => {\n if (!guardOpen('tui.registerKeybinding'))\n return\n if (!binding || typeof binding.id !== 'string' || typeof binding.onTrigger !== 'function') {\n logger.warn('tui.registerKeybinding: missing id or onTrigger')\n return\n }\n // The framework reserves the chords it owns — `escape` (abort /\n // back / exit semantics) and `ctrl+c` (quit). If an extension\n // bound those, the user could get trapped with no working \"out\".\n // We refuse the registration outright + log; the binding's\n // metadata (label, description) is dropped, the runtime stays\n // unaffected.\n if (binding.default && isReservedExtensionBinding(binding.default)) {\n logger.warn(\n 'tui.registerKeybinding: refused — `escape` and `ctrl+c` are framework-reserved chords',\n { id: binding.id, chord: binding.default },\n )\n return\n }\n // A default that doesn't parse, or a bare printable character\n // (which would fire on every matching keystroke while the user\n // types), degrades to unbound-but-rebindable: the id stays\n // registered so the user can bind a valid chord in\n // `keybindings.json`.\n if (binding.default && !parseBindingSpec(binding.default)) {\n logger.warn(\n 'tui.registerKeybinding: default chord is unparseable — registering unbound (rebindable via keybindings.json)',\n { id: binding.id, chord: binding.default },\n )\n sinks.keybindings.push({ ...binding, default: '' })\n return\n }\n if (binding.default && isBareCharExtensionBinding(binding.default)) {\n logger.warn(\n 'tui.registerKeybinding: bare-character default would fire while typing — registering unbound (rebind with ctrl/alt/meta via keybindings.json)',\n { id: binding.id, chord: binding.default },\n )\n sinks.keybindings.push({ ...binding, default: '' })\n return\n }\n sinks.keybindings.push(binding)\n }\n\n const registerFooterHint: ExtensionContext['tui']['registerFooterHint'] = (hint) => {\n if (!guardOpen('tui.registerFooterHint'))\n return\n if (!hint || typeof hint.key !== 'string' || typeof hint.label !== 'string') {\n logger.warn('tui.registerFooterHint: missing key or label')\n return\n }\n sinks.footerHints.push(hint)\n }\n\n const registerModal: ExtensionContext['tui']['registerModal'] = (modal) => {\n if (!guardOpen('tui.registerModal'))\n return\n if (!modal || typeof modal.id !== 'string') {\n logger.warn('tui.registerModal: missing id')\n return\n }\n sinks.modals.push(modal)\n }\n\n const registerRenderer: ExtensionContext['tui']['registerRenderer'] = (id, render) => {\n if (!guardOpen('tui.registerRenderer'))\n return\n if (typeof id !== 'string' || !/^[\\w.-]+$/.test(id)) {\n logger.warn('tui.registerRenderer: id must be a bare [A-Za-z0-9_.-]+ token (it is auto-namespaced)', { id })\n return\n }\n if (typeof render !== 'function') {\n logger.warn('tui.registerRenderer: render must be a function', { id })\n return\n }\n // Namespacing makes cross-extension collisions structurally\n // impossible — blocks reference `<extension>/<id>`.\n sinks.renderers.push({ id: `${definition.name}/${id}`, render })\n }\n\n const registerToolRenderer: ExtensionContext['tui']['registerToolRenderer'] = (registration) => {\n if (!guardOpen('tui.registerToolRenderer'))\n return\n if (!registration || typeof registration.render !== 'function') {\n logger.warn('tui.registerToolRenderer: render must be a function')\n return\n }\n if (registration.tools !== undefined && (!Array.isArray(registration.tools) || registration.tools.some(t => typeof t !== 'string'))) {\n logger.warn('tui.registerToolRenderer: tools must be an array of tool names')\n return\n }\n // Owner attached here (not author-provided): `useSessionState` inside\n // an anonymous tool renderer persists under the extension's namespace.\n sinks.toolRenderers.push({ ...registration, owner: definition.name })\n }\n\n const registerPanel: ExtensionContext['gui']['registerPanel'] = (panel) => {\n if (!guardOpen('gui.registerPanel'))\n return\n if (!panel || typeof panel.id !== 'string') {\n logger.warn('gui.registerPanel: missing id')\n return\n }\n sinks.guiPanels.push(panel)\n }\n\n const ctx: ExtensionContext = {\n name: definition.name,\n ...(definition.version !== undefined ? { version: definition.version } : {}),\n ...(dir !== undefined ? { dir } : {}),\n scope: source,\n logger,\n signal,\n paths,\n storage: storageSurface,\n session: sessionSurface,\n registerTool,\n registerHook,\n registerMcpServer,\n registerSkill,\n registerCompletionProvider,\n registerInlineAutocompleteProvider,\n registerCommand,\n registerTheme,\n registerAgent,\n registerSubagent,\n registerSubagentOverride,\n registerProvider,\n registerSystemPromptFragment,\n registerContextBreakdownSection,\n registerContextPanelSection,\n registerToolResultMetadata,\n registerRunTransform,\n registerBehaviorTransform,\n registerProviderMiddleware,\n registerToolCatalogTransform,\n registerMcpConfigTransform,\n registerSkillPolicy,\n registerToolPolicy,\n registerToolResultTransform,\n registerContextTransform,\n registerPromptTransform,\n registerConfigCategory,\n registerConfigItem,\n config: configSurface,\n disabled,\n tui: {\n registerKeybinding,\n registerFooterHint,\n registerModal,\n registerRenderer,\n registerToolRenderer,\n // HUD surface — deliberately NOT gate-guarded: unlike\n // registrations, these are runtime affordances meant to be called\n // from hooks / commands / timers for the extension's whole lifetime.\n notify: (text, opts) => {\n hudWriter?.notify(text, opts)\n },\n setStatus: (text, opts) => {\n hudWriter?.setStatus(text, opts)\n },\n setBanner: (lines, opts) => {\n hudWriter?.setBanner(lines, opts)\n },\n },\n gui: {\n registerPanel,\n },\n // Runtime affordances like the HUD surface — not gate-guarded.\n submitPrompt: (text, submitOpts) => promptBridge?.submit(text, submitOpts) ?? false,\n runtime: {\n model: () => hostRuntime?.current()?.model() ?? null,\n models: () => hostRuntime?.current()?.models?.() ?? [],\n thinking: () => hostRuntime?.current()?.thinking() ?? null,\n setThinking: level => hostRuntime?.current()?.setThinking(level) ?? false,\n },\n sessions: {\n list: async () => hostRuntime?.current()?.listSessions() ?? [],\n switch: async id => hostRuntime?.current()?.switchSession(id) ?? false,\n create: async () => hostRuntime?.current()?.createSession() ?? false,\n },\n appendNote: (blocks) => {\n // Ergonomics: bare renderer ids in custom blocks are auto-prefixed\n // with THIS extension's namespace (command `emit` requires the full\n // form — the executor has no extension identity there).\n const namespaced = blocks.map(block =>\n block.custom && !block.custom.renderer.includes('/')\n ? { ...block, custom: { ...block.custom, renderer: `${definition.name}/${block.custom.renderer}` } }\n : block)\n return hostRuntime?.current()?.appendNote(namespaced) ?? false\n },\n // Shell + file ops through the SESSION'S execution context — the same\n // transports (and output caps / containment) the agent's tools use,\n // resolved live per call. Not gate-guarded: a runtime affordance for\n // the extension's whole lifetime, like the HUD and submitPrompt.\n workspace: createWorkspaceSurface(\n async () => await hostRuntime?.current()?.workspace?.() ?? null,\n () => hostRuntime?.current()?.workspaceAvailable?.(),\n ),\n }\n return ctx\n}\n\nfunction resolveContextPaths(options: BuildExtensionRegistryOptions): ExtensionContext['paths'] {\n const cwd = options.cwd ?? process.cwd()\n const dirs = options.storageDirs ?? resolveStorageDirs({\n prefix: options.prefix ?? process.env.ZIDANE_PREFIX ?? '.zidane',\n home: homedir(),\n })\n return {\n cwd,\n ...(options.projectRoot ? { projectRoot: options.projectRoot } : {}),\n dataDir: dirs.dataDir,\n cacheDir: dirs.cacheDir,\n ...(dirs.configDir ? { configDir: dirs.configDir } : {}),\n ...(dirs.stateDir ? { stateDir: dirs.stateDir } : {}),\n }\n}\n","/**\n * Shared discover → disabled-set → build pipeline.\n *\n * Three hosts run the identical sequence: the TUI launcher (`runTui`),\n * the TUI's cwd-change rebuild, and the GUI's per-cwd registry cache.\n * Centralizing it keeps the allowlist-gating contract — the `disabled`\n * set spans BOTH disk-discovered and inline candidates — in one place\n * so the three callers can't drift on it.\n */\n\nimport type { Settings } from '../chat/types'\nimport type { LogSink } from '../logger'\nimport type { ExtensionHostRuntimeBridge } from './host-runtime'\nimport type { ExtensionHud } from './hud'\nimport type { ExtensionPromptBridge } from './prompt-bridge'\nimport type { ExtensionRegistry, LoadedExtension } from './types'\nimport { discoverExtensions } from './discovery'\nimport { buildExtensionRegistry } from './register'\n\nexport interface DiscoverAndBuildExtensionRegistryOptions {\n /** Discovery cwd — project roots are walked up from here. Defaults to `process.cwd()`. */\n cwd?: string\n /** User home directory. Defaults to `os.homedir()`. Mainly a test seam. */\n home?: string\n /** Storage prefix (e.g. `.zidane`). Defaults to `.zidane` inside discovery. */\n prefix?: string\n /**\n * Extra extension scan roots, prepended ahead of the default project/user\n * roots. Each root contains extension package dirs (`<root>/<name>/index.ts`).\n * Treated as user-scoped/trusted because the host/user opted into them\n * explicitly.\n */\n extensionScanPaths?: readonly string[]\n /**\n * User enable allowlist (`Settings.enabledExtensions`). `undefined`\n * means \"fresh install — everything enabled\". When set it gates BOTH\n * disk and inline candidates: a name absent from the list lands in the\n * build's `disabled` set, so it classifies as `inactive` and its\n * contributions never reach the registry's active surface. Its\n * `setup()` still runs (trusted code) so its config schema is\n * browsable in Settings before enabling.\n */\n enabledAllowlist?: readonly string[]\n /** Whether project-scoped extensions are trusted (allowed to run their code). */\n trustProjectExtensions?: boolean\n /** Host-supplied inline extensions, already `inlineLoadedExtension()`-wrapped. */\n inlineExtensions?: readonly LoadedExtension[]\n /** Log sink for `setup()` lifecycle. Defaults to stderr inside `buildExtensionRegistry`. */\n sink?: LogSink\n /** Extension HUD for `ctx.tui.notify` / `setStatus` / `setBanner` (TUI hosts). */\n hud?: ExtensionHud\n /** Prompt bridge for `ctx.submitPrompt` (hosts with a submit pipeline). */\n promptBridge?: ExtensionPromptBridge\n /** Host runtime bridge for `ctx.runtime` / `ctx.sessions` / `ctx.appendNote`. */\n hostRuntime?: ExtensionHostRuntimeBridge\n /**\n * Live settings getter backing `ctx.config.get` — threaded verbatim to\n * {@link BuildExtensionRegistryOptions.readSettings}. Hosts pass a getter\n * returning their current `Settings` (TUI: a live mirror; GUI: the DB\n * reader) so extension config reads inside hooks / tools are live.\n */\n readSettings?: () => Partial<Settings> | undefined\n /** Abort discovery before its imports dispatch. */\n signal?: AbortSignal\n}\n\n/**\n * Discover disk extensions, fold in the inline ones, compute the\n * allowlist-driven `disabled` set, and build the merged\n * {@link ExtensionRegistry}.\n *\n * The `disabled` set honors the allowlist for inline extensions too:\n * the TUI treats inline as toggleable everywhere else (Settings tab,\n * live filtering), so both ride the same inactive-classification path.\n */\nexport async function discoverAndBuildExtensionRegistry(\n opts: DiscoverAndBuildExtensionRegistryOptions = {},\n): Promise<ExtensionRegistry> {\n const discovered = await discoverExtensions({\n ...(opts.cwd !== undefined ? { cwd: opts.cwd } : {}),\n ...(opts.home !== undefined ? { home: opts.home } : {}),\n ...(opts.prefix !== undefined ? { prefix: opts.prefix } : {}),\n ...(opts.extensionScanPaths !== undefined ? { extensionScanPaths: opts.extensionScanPaths } : {}),\n ...(opts.trustProjectExtensions !== undefined ? { trustProjectExtensions: opts.trustProjectExtensions } : {}),\n ...(opts.signal !== undefined ? { signal: opts.signal } : {}),\n })\n const candidates = [...discovered, ...(opts.inlineExtensions ?? [])]\n const allowlist = opts.enabledAllowlist\n const disabled = allowlist === undefined\n ? undefined\n : new Set(candidates.map(e => e.name).filter(name => !allowlist.includes(name)))\n return buildExtensionRegistry(candidates, {\n ...(opts.sink !== undefined ? { sink: opts.sink } : {}),\n ...(opts.hud !== undefined ? { hud: opts.hud } : {}),\n ...(opts.promptBridge !== undefined ? { promptBridge: opts.promptBridge } : {}),\n ...(opts.hostRuntime !== undefined ? { hostRuntime: opts.hostRuntime } : {}),\n ...(disabled ? { disabled } : {}),\n ...(opts.readSettings !== undefined ? { readSettings: opts.readSettings } : {}),\n ...(opts.cwd !== undefined ? { cwd: opts.cwd } : {}),\n ...(opts.prefix !== undefined ? { prefix: opts.prefix } : {}),\n })\n}\n","/**\n * Host-module sharing for extension imports — what makes \"an extension\n * loaded into the app uses the app's modules\" ACTUALLY true.\n *\n * By default it is not: a dynamically imported extension file resolves\n * its bare imports (`react`, `@opentui/react`, …) by walking\n * node_modules from the EXTENSION file's directory — not from the host.\n * Depending on where the extension lives, `import 'react'` finds\n * nothing, or worse, a SECOND react copy — elements still render (host\n * tags are strings and the element symbol is a global `Symbol.for`),\n * but hooks hit a null dispatcher and throw \"Invalid hook call\", which\n * surfaces as a failed renderer. This bit the dev loop itself: the dev\n * example resolved `tui/node_modules/react` while the reconciler ran\n * the root copy.\n *\n * The fix is interception, not resolution: {@link registerExtensionHostModules}\n * registers a Bun virtual-module plugin that maps the given specifiers\n * to the host's LIVE module objects (`loader: 'object'` — the actual\n * instances, no paths involved, so it works identically for installed\n * extensions and inside compiled binaries). Extensions then write plain\n * `import { useState } from 'react'` and get the host's react, period.\n *\n * Layering: this module is renderer-neutral (no react import here) —\n * the HOST hands its modules in. The TUI registers react + OpenTUI at\n * startup, before the boot registry's discovery imports run; headless\n * hosts simply don't register UI modules (a UI-importing extension then\n * fails to load there, exactly as before). First registration per\n * specifier wins; later calls for the same specifier are no-ops.\n */\n\n/** Bun's plugin surface, narrowed from `globalThis` (same pattern as `contexts/glob.ts`) — this module must typecheck and no-op cleanly on non-Bun runtimes. */\ninterface BunPluginGlobal {\n plugin: (plugin: { name: string, setup: (build: { module: (specifier: string, cb: () => { exports: Record<string, unknown>, loader: 'object' } | Promise<{ exports: Record<string, unknown>, loader: 'object' }>) => void }) => void }) => void\n}\n\n/**\n * A host module, eager or lazy. Lazy (`() => Promise<module>`) exists for\n * self-referencing hosts: `zidane/tui` maps to ITSELF, which can only be\n * expressed as a deferred dynamic import (the module is still evaluating\n * when it registers).\n */\nexport type HostModuleSource = object | (() => Promise<object>)\n\nconst registered = new Set<string>()\n\n/**\n * Map bare import specifiers to the host's live module objects for every\n * subsequently-imported module (extensions included). No-op outside Bun\n * and for already-registered specifiers. Call BEFORE the first extension\n * import (the TUI does, at startup).\n */\nexport function registerExtensionHostModules(modules: Record<string, HostModuleSource>): void {\n const bun = (globalThis as { Bun?: Partial<BunPluginGlobal> }).Bun\n if (typeof bun?.plugin !== 'function')\n return\n const fresh = Object.entries(modules).filter(([specifier]) => !registered.has(specifier))\n if (fresh.length === 0)\n return\n for (const [specifier] of fresh)\n registered.add(specifier)\n bun.plugin({\n name: `zidane-host-modules(${fresh.map(([s]) => s).join(',')})`,\n setup(build) {\n for (const [specifier, source] of fresh) {\n build.module(specifier, async () => {\n const mod = typeof source === 'function' ? await source() : source\n return {\n // Spread the namespace into a plain record; module namespaces\n // don't expose `default` enumerably in every case, so carry it\n // explicitly (falling back to the namespace itself for\n // consumers doing `import React from 'react'` against a CJS\n // interop shape).\n exports: { ...(mod as Record<string, unknown>), default: (mod as { default?: unknown }).default ?? mod },\n loader: 'object' as const,\n }\n })\n }\n },\n })\n}\n","/**\n * Extension HUD — the fire-and-forget presence an extension paints around\n * the transcript, behind `ctx.tui.notify` / `setStatus` / `setBanner`.\n *\n * A small subscribable channel (same pattern as the log ring buffer and\n * `live-settings`): extension contexts WRITE at any point in their\n * lifetime (setup, hooks, commands, timers), the host renderer SUBSCRIBES\n * and paints:\n *\n * - status → one dim line per extension in the transcript-tail slot\n * (next to the streaming throbber), optionally with a\n * loader glyph and an auto-clear timeout,\n * - banner → one block of lines per extension in the above-prompt slot\n * (stacked with the todo indicator), optionally auto-clearing,\n * - notify → a one-shot transcript line (info/warn/error toned).\n *\n * State is keyed by extension name — an extension owns exactly one status\n * and one banner (set `null` to clear), so there are no ids to leak.\n * Writes go through generation-scoped {@link ExtensionHudWriter}s claimed\n * per registry build: registry swaps (cwd change, `/reload`) re-claim\n * under the same names, superseding the outgoing generation so its late\n * writes, timers, and dispose-time `clear` can't touch the fresh paint.\n * Notifications raised before a renderer attaches (e.g. during pre-mount\n * `setup`) are buffered (bounded) and flushed on attach.\n *\n * Hosts without a UI simply never create a HUD — the ctx methods degrade\n * to no-ops.\n */\n\nexport type ExtensionNotifyTone = 'info' | 'warn' | 'error'\n\nexport interface ExtensionNotification {\n extension: string\n text: string\n tone: ExtensionNotifyTone\n}\n\n/** One extension's status-line entry. */\nexport interface ExtensionHudStatus {\n text: string\n /** Render a working glyph next to the text (long-running work). */\n loader: boolean\n}\n\nexport interface ExtensionHudState {\n /** Extension name → its single status entry. Insertion-ordered. */\n readonly statuses: Readonly<Record<string, ExtensionHudStatus>>\n /** Extension name → its banner lines. Insertion-ordered. */\n readonly banners: Readonly<Record<string, readonly string[]>>\n}\n\nconst MAX_BUFFERED_NOTIFICATIONS = 20\n\nexport interface ExtensionStatusOptions {\n /** Show a working glyph next to the text. Default false. */\n loader?: boolean\n /** Auto-clear after this many ms (e.g. a transient \"✓ done\"). */\n timeout?: number\n}\n\nexport interface ExtensionBannerOptions {\n /** Auto-clear after this many ms. */\n timeout?: number\n}\n\nexport interface ExtensionNotifyOptions {\n /** Line tone. Default `'info'`. */\n tone?: ExtensionNotifyTone\n}\n\n/**\n * A generation-scoped writer over one extension's HUD slots. A NEWER claim\n * for the same extension supersedes this one: its status/banner writes,\n * pending auto-clear timers, and `clear` become no-ops (one-shot `notify`\n * stays live — a superseded instance saying \"shutting down\" is still worth\n * showing).\n */\nexport interface ExtensionHudWriter {\n setStatus: (text: string | null, opts?: ExtensionStatusOptions) => void\n setBanner: (lines: readonly string[] | null, opts?: ExtensionBannerOptions) => void\n notify: (text: string, opts?: ExtensionNotifyOptions) => void\n /**\n * Wipe this extension's footprint AND retire this writer — subsequent\n * status/banner writes become inert (`notify` stays live for farewell\n * lines). Called at registry dispose so teardown closures and leaked\n * timers can't repaint after the extension is gone. No-op once\n * superseded by a newer claim.\n */\n clear: () => void\n}\n\nexport interface ExtensionHud {\n /**\n * Claim the (single) writer for an extension. Claimed once per registry\n * build; supersedes any prior claim for the same name, which is what\n * makes swap-then-dispose ordering safe.\n */\n claim: (extension: string) => ExtensionHudWriter\n /** Force-wipe an extension's footprint regardless of generation. */\n clearExtension: (extension: string) => void\n getState: () => ExtensionHudState\n /** Re-render trigger for state changes (status/banner). */\n subscribe: (listener: () => void) => () => void\n /**\n * Attach the one-shot notification sink (the TUI's transcript appender).\n * Buffered pre-attach notifications flush immediately, oldest first.\n * Returns a detach fn; only one sink at a time (last attach wins).\n */\n onNotify: (sink: (notification: ExtensionNotification) => void) => () => void\n}\n\nexport function createExtensionHud(): ExtensionHud {\n const statuses: Record<string, ExtensionHudStatus> = {}\n const banners: Record<string, readonly string[]> = {}\n /** Current writer generation per extension — the newest claim owns the slots. */\n const owners: Record<string, symbol> = {}\n let state: ExtensionHudState = { statuses: {}, banners: {} }\n const listeners = new Set<() => void>()\n let notifySink: ((n: ExtensionNotification) => void) | null = null\n const buffered: ExtensionNotification[] = []\n\n const commit = (): void => {\n state = { statuses: { ...statuses }, banners: { ...banners } }\n for (const listener of listeners) {\n try {\n listener()\n }\n catch {\n // A renderer callback must never break extension writes.\n }\n }\n }\n\n const wipe = (extension: string): void => {\n const had = extension in statuses || extension in banners\n delete statuses[extension]\n delete banners[extension]\n if (had)\n commit()\n }\n\n const emitNotification = (notification: ExtensionNotification): void => {\n if (notifySink) {\n try {\n notifySink(notification)\n }\n catch {\n // The transcript appender must never throw into extension code.\n }\n return\n }\n buffered.push(notification)\n if (buffered.length > MAX_BUFFERED_NOTIFICATIONS)\n buffered.splice(0, buffered.length - MAX_BUFFERED_NOTIFICATIONS)\n }\n\n return {\n claim(extension) {\n const generation = Symbol(extension)\n owners[extension] = generation\n const owns = (): boolean => owners[extension] === generation\n // Auto-clear timers, per slot. Rearmed on every write, cancelled on\n // clear, and generation-guarded like every other write path.\n let statusTimer: ReturnType<typeof setTimeout> | undefined\n let bannerTimer: ReturnType<typeof setTimeout> | undefined\n const disarm = (timer: ReturnType<typeof setTimeout> | undefined): void => {\n if (timer !== undefined)\n clearTimeout(timer)\n }\n\n const setStatus = (text: string | null, opts?: ExtensionStatusOptions): void => {\n if (!owns())\n return\n disarm(statusTimer)\n statusTimer = undefined\n if (text === null || text === '')\n delete statuses[extension]\n else\n statuses[extension] = { text, loader: opts?.loader ?? false }\n commit()\n if (text && opts?.timeout !== undefined && opts.timeout > 0)\n statusTimer = setTimeout(setStatus, opts.timeout, null)\n }\n\n const setBanner = (lines: readonly string[] | null, opts?: ExtensionBannerOptions): void => {\n if (!owns())\n return\n disarm(bannerTimer)\n bannerTimer = undefined\n const kept = lines?.filter(line => line.length > 0)\n if (!kept || kept.length === 0)\n delete banners[extension]\n else\n banners[extension] = kept\n commit()\n if (kept && kept.length > 0 && opts?.timeout !== undefined && opts.timeout > 0)\n bannerTimer = setTimeout(setBanner, opts.timeout, null)\n }\n\n return {\n setStatus,\n setBanner,\n notify(text, opts) {\n emitNotification({ extension, text, tone: opts?.tone ?? 'info' })\n },\n clear() {\n disarm(statusTimer)\n disarm(bannerTimer)\n statusTimer = undefined\n bannerTimer = undefined\n if (owns()) {\n // Retire the generation: a disposed extension's late writes\n // (teardown closures, leaked intervals) must be inert. On a\n // swap/reload the successor already owns the name, so this\n // branch never runs and the fresh paint survives.\n delete owners[extension]\n wipe(extension)\n }\n },\n }\n },\n clearExtension: wipe,\n getState: () => state,\n subscribe(listener) {\n listeners.add(listener)\n return () => listeners.delete(listener)\n },\n onNotify(sink) {\n notifySink = sink\n while (buffered.length > 0) {\n const next = buffered.shift()\n if (next) {\n try {\n sink(next)\n }\n catch {}\n }\n }\n return () => {\n if (notifySink === sink)\n notifySink = null\n }\n },\n }\n}\n","/**\n * Extension import — fetch from GitHub or npm, validate, and install into\n * the roots {@link discoverExtensions} already scans.\n *\n * Host-agnostic by construction: no UI, no settings access, no renderer.\n * The TUI's `Settings → Extensions` install flow (`ctrl+g`) and the GUI\n * call the same {@link installExtensionFromSpec} orchestrator; hosts own scope\n * selection, overwrite confirmation, allowlist updates, and the\n * post-install registry rebuild (the `/reload` path).\n *\n * Pipeline invariants:\n *\n * - **Validate before touching user roots.** The candidate is imported\n * and type-checked (same `isExtensionDefinition` + apiVersion rules\n * as discovery) inside the temp dir; a failure installs nothing.\n * Importing executes the extension's top-level code once — the\n * explicit install request is the consent; it's the same code that\n * would run at next boot.\n * - **Folder name = definition name, by construction.** The target dir\n * is named from the validated `definition.name`, so discovery's\n * name-match invariant can't be violated by an install.\n * - **Dependency installs never run lifecycle scripts** (bun's default;\n * the npm/pnpm/yarn fallbacks get `--ignore-scripts`).\n * - Failures throw {@link ExtensionInstallError} with a machine-readable\n * `code` so hosts can react (e.g. `exists` → confirm-and-retry with\n * `overwrite`), never half-install.\n *\n * Recognized package shapes (both fetch targets):\n *\n * - root `index.{ts,tsx,mjs,js}` → a single-extension repo/package\n * - `extensions/<name>/index.*` → a multi-extension collection;\n * `extension` selects one (mandatory when several exist)\n */\n\nimport type { FetchSourceResult, InstallIo, InstallScope, InstallSource } from '../install/source'\nimport { cpSync, existsSync, lstatSync, mkdirSync, mkdtempSync, readdirSync, readFileSync, renameSync, rmSync, writeFileSync } from 'node:fs'\nimport { tmpdir } from 'node:os'\nimport { basename, dirname, join, resolve } from 'node:path'\nimport { pathToFileURL } from 'node:url'\nimport { projectUserPaths } from '../chat/project-user-paths'\nimport { errorMessage } from '../errors'\nimport { defaultExec, defaultWhich, fetchInstallSource, InstallSourceError, lastLine, parseInstallSource, resolveNpmVersion, resolveSubdirRoot } from '../install/source'\nimport { findEntryFile, isExtensionDefinition, readExtensionManifest } from './discovery'\nimport { withTimeout } from './with-timeout'\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport type ExtensionInstallScope = InstallScope\n\n/** The shared install-source shape — see `src/install/source.ts` for the grammar. */\nexport type ExtensionInstallSource = InstallSource\n\nexport type ExtensionInstallErrorCode\n = | 'invalid-source'\n | 'fetch-failed'\n | 'no-extension'\n | 'multiple-candidates'\n | 'deps-failed'\n | 'validate-failed'\n | 'exists'\n | 'no-package-manager'\n | 'not-installed'\n | 'no-provenance'\n\nexport class ExtensionInstallError extends InstallSourceError {\n declare readonly code: ExtensionInstallErrorCode\n constructor(code: ExtensionInstallErrorCode, message: string, candidates?: readonly string[]) {\n super(code, message, candidates)\n this.name = 'ExtensionInstallError'\n }\n}\n\n/**\n * Re-raise a shared-layer {@link InstallSourceError} in the extension\n * error vocabulary so every consumer keeps a single `err.code` switch\n * over {@link ExtensionInstallErrorCode}. The base layer only throws\n * `'invalid-source'` / `'fetch-failed'` — both members of the extension\n * union.\n */\nfunction rethrowAsExtensionError(err: unknown): never {\n if (err instanceof InstallSourceError && !(err instanceof ExtensionInstallError))\n throw new ExtensionInstallError(err.code as ExtensionInstallErrorCode, err.message, err.candidates)\n throw err\n}\n\nconst PROVENANCE_FILE = '.install.json'\n\n/** What `.install.json` records — enough to replay the install for updates. */\nexport interface ExtensionInstallProvenance {\n /** Resolved provenance, e.g. `npm:foo@1.2.3` / `github:owner/repo@main`. */\n source: string\n /** The raw spec the user passed — what an update re-resolves. */\n spec: string\n scope: ExtensionInstallScope\n /** Candidate selected from a multi-extension source, when one was. */\n extension?: string\n /** Exact commit SHA (GitHub via git clone) — anchors the update no-op check. */\n commit?: string\n installedAt: string\n}\n\n/** One installable extension found inside a fetched tree. */\nexport interface ExtensionInstallCandidate {\n /** Proposed name (manifest name, else collection dirname, else repo/pkg basename). */\n name: string\n /** Absolute directory inside the fetched tree. */\n dir: string\n}\n\nexport interface ExtensionInstallResult {\n /** Final name — the validated `definition.name`. */\n name: string\n /** Absolute installed directory. */\n dir: string\n scope: ExtensionInstallScope\n /** Human-readable provenance, e.g. `npm:foo@1.2.3` / `github:owner/repo@main`. */\n provenance: string\n /** Exact commit SHA when known (GitHub via git clone). */\n commit?: string\n warnings: readonly string[]\n}\n\n/**\n * Injectable side-effect seams — tests swap these to stay offline; hosts\n * normally omit the whole object. Shared shape with the skills installer.\n */\nexport type ExtensionInstallIo = InstallIo\n\nexport interface InstallExtensionFromSpecOptions {\n /** Raw source spec — see {@link parseExtensionInstallSource}. */\n source: string\n /** Candidate name to install when the source contains several extensions. */\n extension?: string\n scope: ExtensionInstallScope\n /** Replace an existing install of the same name. Default false → `exists` error. */\n overwrite?: boolean\n cwd?: string\n home?: string\n /** Storage prefix (e.g. `.zidane`). Matches discovery's default. */\n prefix?: string\n onProgress?: (message: string) => void\n io?: ExtensionInstallIo\n /** Timeout for the validation import. Default 10s (matches discovery). */\n importTimeoutMs?: number\n /**\n * Advanced: exact extensions root to install into, bypassing the\n * scope → `.{prefix}/extensions` resolution. Used by\n * {@link updateInstalledExtension} to replace an install in place\n * (e.g. one living under `.agents/extensions`).\n */\n installRoot?: string\n}\n\n// ---------------------------------------------------------------------------\n// Source spec parsing + fetch — thin wrappers over the shared install\n// layer (`src/install/source.ts`), re-raised in the extension error\n// vocabulary. The grammar, fetch pipeline, caps, and hardening live\n// there so the skills installer shares them verbatim.\n// ---------------------------------------------------------------------------\n\n/** Classify a raw source string. See `parseInstallSource` for the grammar. */\nexport function parseExtensionInstallSource(raw: string): ExtensionInstallSource {\n try {\n return parseInstallSource(raw)\n }\n catch (err) {\n rethrowAsExtensionError(err)\n }\n}\n\nexport type ExtensionFetchResult = FetchSourceResult\n\n/**\n * Materialize the source into `destDir` (created by the caller, assumed\n * empty). Returns the provenance string plus, for git clones, the exact\n * commit SHA. See `fetchInstallSource` for transport details and caps.\n */\nexport async function fetchExtensionSource(\n source: ExtensionInstallSource,\n destDir: string,\n io: ExtensionInstallIo = {},\n onProgress?: (message: string) => void,\n): Promise<ExtensionFetchResult> {\n try {\n return await fetchInstallSource(source, destDir, io, onProgress)\n }\n catch (err) {\n rethrowAsExtensionError(err)\n }\n}\n\n// ---------------------------------------------------------------------------\n// Candidate enumeration\n// ---------------------------------------------------------------------------\n\n/**\n * List the installable extensions inside a fetched tree. `fallbackName`\n * seeds the single-extension case when no manifest names it (repo or\n * unscoped package basename).\n */\nexport function listExtensionCandidates(dir: string, fallbackName: string): ExtensionInstallCandidate[] {\n if (findEntryFile(dir))\n return [{ name: readExtensionManifest(dir).manifest?.name ?? sanitizeName(fallbackName), dir }]\n const collection = join(dir, 'extensions')\n const out: ExtensionInstallCandidate[] = []\n let children: string[]\n try {\n children = readdirSync(collection)\n }\n catch {\n return out\n }\n for (const child of children.sort()) {\n if (child.startsWith('.'))\n continue\n const childDir = join(collection, child)\n try {\n if (!lstatSync(childDir).isDirectory())\n continue\n }\n catch {\n continue\n }\n if (findEntryFile(childDir))\n out.push({ name: child, dir: childDir })\n }\n return out\n}\n\n/** Basename-ish fallback: strip npm scope, keep folder-safe chars. */\nfunction sanitizeName(raw: string): string {\n const base = raw.includes('/') ? (raw.split('/').pop() as string) : raw\n return base.replace(/[^\\w.-]+/g, '-').replace(/^[.-]+/, '') || 'extension'\n}\n\n// ---------------------------------------------------------------------------\n// Orchestrator\n// ---------------------------------------------------------------------------\n\n/**\n * The full fetch → enumerate → deps → validate → copy pipeline. See the\n * module doc for invariants. Throws {@link ExtensionInstallError}; a throw\n * means nothing was written to the target roots.\n */\nexport async function installExtensionFromSpec(opts: InstallExtensionFromSpecOptions): Promise<ExtensionInstallResult> {\n const io = opts.io ?? {}\n const onProgress = opts.onProgress\n const source = parseExtensionInstallSource(opts.source)\n const warnings: string[] = []\n\n const stage = mkdtempSync(join(tmpdir(), 'zidane-ext-install-'))\n try {\n const fetchedInto = join(stage, 'fetched')\n mkdirSync(fetchedInto, { recursive: true })\n const { provenance, commit } = await fetchExtensionSource(source, fetchedInto, io, onProgress)\n\n // A `:subdir` source re-roots the candidate search. Physical (not\n // just string-level) containment lives in `resolveSubdirRoot` — a\n // malicious repo shipping a symlink (`evil -> /`) must not walk the\n // candidate search (and the validation IMPORT) onto the local\n // filesystem. Structural escapes are already rejected at parse time;\n // this is the belt-and-braces layer.\n let candidateRoot = fetchedInto\n let fallbackName = source.kind === 'github' ? source.repo : source.pkg\n if (source.kind === 'github' && source.subdir) {\n const rooted = resolveSubdirRoot(fetchedInto, source.subdir)\n if (!rooted.ok) {\n if (rooted.reason === 'missing')\n throw new ExtensionInstallError('no-extension', `\"${source.subdir}\" is not a directory in ${source.owner}/${source.repo}${source.ref ? `@${source.ref}` : ''}.`)\n throw new ExtensionInstallError(\n 'invalid-source',\n rooted.reason === 'escape-symlink'\n ? `Subdirectory \"${source.subdir}\" escapes the repository root through a symlink.`\n : `Subdirectory \"${source.subdir}\" escapes the repository root.`,\n )\n }\n candidateRoot = rooted.dir\n fallbackName = basename(resolve(fetchedInto, source.subdir))\n }\n const candidates = listExtensionCandidates(candidateRoot, fallbackName)\n if (candidates.length === 0) {\n throw new ExtensionInstallError('no-extension', `No extension found in ${opts.source} — expected a root index.{ts,tsx,mjs,js} or extensions/<name>/index.* directories.`)\n }\n let candidate: ExtensionInstallCandidate\n if (opts.extension) {\n const match = candidates.find(c => c.name === opts.extension)\n if (!match) {\n throw new ExtensionInstallError(\n 'no-extension',\n `No extension named \"${opts.extension}\" in ${opts.source}. Available: ${candidates.map(c => c.name).join(', ')}`,\n candidates.map(c => c.name),\n )\n }\n candidate = match\n }\n else if (candidates.length > 1) {\n throw new ExtensionInstallError(\n 'multiple-candidates',\n `${opts.source} contains ${candidates.length} extensions — pass one of: ${candidates.map(c => c.name).join(', ')}`,\n candidates.map(c => c.name),\n )\n }\n else {\n candidate = candidates[0] as ExtensionInstallCandidate\n }\n\n // Dependencies BEFORE validation — the import may need them.\n const pm = await installDependencies(candidate.dir, io, onProgress)\n if (pm)\n warnings.push(...pm.warnings)\n\n onProgress?.('validating extension module…')\n const definition = await validateCandidate(candidate, opts.importTimeoutMs ?? 10_000)\n\n const targetRoot = opts.installRoot ?? resolveInstallRoot(opts)\n const targetDir = join(targetRoot, definition.name)\n // Overwrites are fully live: discovery evicts the extension's module\n // graph before each import, so the next reload runs the new code.\n if (existsSync(targetDir) && !opts.overwrite)\n throw new ExtensionInstallError('exists', `${targetDir} already exists. Pass overwrite to replace it.`)\n\n onProgress?.(`installing to ${targetDir}…`)\n mkdirSync(targetRoot, { recursive: true })\n // Transactional target phase — the \"never half-install\" invariant. An\n // overwritten previous version is parked under a dot-name (invisible\n // to the scanner) and restored if anything past this point throws\n // (e.g. the in-place deps install failing on a full disk); on success\n // it is deleted. A fresh install that fails is wiped back to absence.\n const backupDir = existsSync(targetDir)\n ? join(targetRoot, `.${definition.name}.prev-${process.pid}-${Date.now()}`)\n : null\n if (backupDir)\n renameSync(targetDir, backupDir)\n try {\n cpSync(candidate.dir, targetDir, {\n recursive: true,\n // Materialize symlinked files: the stage dir they'd point into is\n // deleted after install, so copied links would dangle. (The\n // extension's code already ran at validation with the user's\n // consent — dereferencing discloses nothing new.)\n dereference: true,\n filter: src => !/(?:^|[\\\\/])(?:\\.git|node_modules)(?:$|[\\\\/])/.test(src),\n })\n // Re-install deps in place (node_modules excluded from the copy —\n // symlink layouts don't survive a cp).\n await installDependencies(targetDir, io, onProgress)\n\n const provenanceRecord: ExtensionInstallProvenance = {\n source: provenance,\n spec: opts.source,\n scope: opts.scope,\n ...(opts.extension !== undefined ? { extension: opts.extension } : {}),\n ...(commit !== undefined ? { commit } : {}),\n installedAt: new Date().toISOString(),\n }\n writeFileSync(join(targetDir, PROVENANCE_FILE), `${JSON.stringify(provenanceRecord, null, 2)}\\n`)\n }\n catch (err) {\n rmSync(targetDir, { recursive: true, force: true })\n if (backupDir)\n renameSync(backupDir, targetDir)\n throw err\n }\n if (backupDir)\n rmSync(backupDir, { recursive: true, force: true })\n\n return { name: definition.name, dir: targetDir, scope: opts.scope, provenance, ...(commit !== undefined ? { commit } : {}), warnings }\n }\n finally {\n rmSync(stage, { recursive: true, force: true })\n }\n}\n\n/** Read `.install.json` provenance from an installed extension dir, or null. */\nexport function readExtensionInstallProvenance(dir: string): ExtensionInstallProvenance | null {\n const raw = readJson(join(dir, PROVENANCE_FILE)) as Partial<ExtensionInstallProvenance> | null\n if (!raw || typeof raw.spec !== 'string' || typeof raw.source !== 'string')\n return null\n return {\n source: raw.source,\n spec: raw.spec,\n scope: raw.scope === 'project' ? 'project' : 'user',\n ...(typeof raw.extension === 'string' ? { extension: raw.extension } : {}),\n ...(typeof raw.commit === 'string' ? { commit: raw.commit } : {}),\n installedAt: typeof raw.installedAt === 'string' ? raw.installedAt : '',\n }\n}\n\nexport interface InstalledExtensionTargetOptions {\n /** The installed extension's directory (e.g. `LoadedExtension.dir`). */\n dir: string\n cwd?: string\n home?: string\n prefix?: string\n /** Extra trusted roots (`ZIDANE_EXTENSION_DIRS` / host scan paths). */\n extensionScanPaths?: readonly string[]\n}\n\n/**\n * Containment guard shared by update/remove: `dir` must be a DIRECT child\n * of a recognized extensions root (the four `.{agents,prefix}/extensions`\n * search paths, plus any host scan roots). Refuses anything else — these\n * functions delete directories and must never be pointable at arbitrary\n * paths (an inline/builtin extension has no dir; a hand-typed path is not\n * an install).\n */\nfunction assertInstalledExtensionDir(opts: InstalledExtensionTargetOptions): { dir: string, root: string, scope: ExtensionInstallScope } {\n const dir = resolve(opts.dir)\n const roots: Array<{ path: string, scope: ExtensionInstallScope }> = [\n ...projectUserPaths({\n subPath: 'extensions',\n ...(opts.cwd !== undefined ? { cwd: opts.cwd } : {}),\n ...(opts.home !== undefined ? { home: opts.home } : {}),\n ...(opts.prefix !== undefined ? { prefix: opts.prefix } : {}),\n }).map(p => ({ path: p.path, scope: p.source })),\n // Host scan roots are user-opted-in — user scope by convention.\n ...(opts.extensionScanPaths ?? []).map(p => ({ path: resolve(p), scope: 'user' as const })),\n ]\n const root = roots.find(r => dirname(dir) === r.path)\n if (!root)\n throw new ExtensionInstallError('not-installed', `${dir} is not inside a recognized extensions root — refusing to touch it.`)\n return { dir, root: root.path, scope: root.scope }\n}\n\n/**\n * Delete an installed extension's directory. Works on any on-disk\n * extension (hand-authored ones too — no provenance required); the\n * containment guard is the safety boundary. The caller owns follow-up\n * (allowlist cleanup, registry reload).\n */\nexport function removeInstalledExtension(opts: InstalledExtensionTargetOptions): { dir: string } {\n const { dir } = assertInstalledExtensionDir(opts)\n if (!existsSync(dir))\n throw new ExtensionInstallError('not-installed', `${dir} does not exist.`)\n rmSync(dir, { recursive: true, force: true })\n return { dir }\n}\n\n/**\n * Cheap \"is there anything new?\" probe, no fetch of content:\n *\n * - GitHub (with a recorded commit): `git ls-remote` resolves the ref's\n * current SHA in one round-trip — equal SHA means up to date.\n * - npm: one registry metadata GET re-resolves the version/dist-tag —\n * an unchanged resolved provenance string means up to date.\n *\n * `null` = \"can't tell cheaply\" (no recorded commit, git missing,\n * ls-remote/parse failure) — the caller falls through to a full update,\n * which errors honestly if the network is actually down.\n */\nasync function checkRemoteUnchanged(\n source: ExtensionInstallSource,\n previous: ExtensionInstallProvenance,\n io: ExtensionInstallIo,\n): Promise<boolean | null> {\n if (source.kind === 'npm') {\n try {\n const { version } = await resolveNpmVersion(source, io.fetch ?? fetch)\n return `npm:${source.pkg}@${version}` === previous.source\n }\n catch {\n return null\n }\n }\n if (!previous.commit)\n return null\n const which = io.which ?? defaultWhich\n const exec = io.exec ?? defaultExec\n if (!which('git'))\n return null\n const url = `https://github.com/${source.owner}/${source.repo}.git`\n const refspecs = source.ref ? [`refs/heads/${source.ref}`, `refs/tags/${source.ref}`] : ['HEAD']\n // GIT_TERMINAL_PROMPT=0 — never let git prompt for credentials from\n // inside the TUI; an unreachable remote degrades to \"can't tell\" (null).\n const res = await exec('git', ['ls-remote', url, ...refspecs], { env: { GIT_TERMINAL_PROMPT: '0' } })\n if (!res.ok)\n return null\n const sha = res.stdout.trim().split('\\n')[0]?.split('\\t')[0]?.trim()\n if (!sha || !/^[0-9a-f]{40}$/.test(sha))\n return null\n return sha === previous.commit\n}\n\n/**\n * Re-run the recorded install for an extension: re-resolves the original\n * spec (so `owner/repo` follows the branch and `pkg` follows `latest`,\n * while pinned refs/versions stay pinned) and replaces the install\n * IN PLACE (same root the extension currently lives in, even a\n * `.agents/extensions` one). Requires `.install.json` provenance —\n * hand-authored extensions have nothing to update from.\n *\n * When the remote is provably unchanged (recorded commit matches\n * `git ls-remote`, or the npm version re-resolves identically), the\n * reinstall is skipped entirely and the result carries `upToDate: true`.\n */\nexport async function updateInstalledExtension(\n opts: InstalledExtensionTargetOptions & {\n onProgress?: (message: string) => void\n io?: ExtensionInstallIo\n },\n): Promise<ExtensionInstallResult & { previous: ExtensionInstallProvenance, upToDate: boolean }> {\n const { dir, root, scope } = assertInstalledExtensionDir(opts)\n const provenance = readExtensionInstallProvenance(dir)\n if (!provenance)\n throw new ExtensionInstallError('no-provenance', `${dir} has no .install.json — it wasn't installed from a source, so there is nothing to update from.`)\n\n const source = parseExtensionInstallSource(provenance.spec)\n opts.onProgress?.(`checking ${provenance.source} for changes…`)\n if (await checkRemoteUnchanged(source, provenance, opts.io ?? {}) === true) {\n return {\n name: basename(dir),\n dir,\n scope,\n provenance: provenance.source,\n ...(provenance.commit !== undefined ? { commit: provenance.commit } : {}),\n warnings: [],\n previous: provenance,\n upToDate: true,\n }\n }\n\n const result = await installExtensionFromSpec({\n source: provenance.spec,\n ...(provenance.extension !== undefined ? { extension: provenance.extension } : {}),\n // Scope from where the extension ACTUALLY lives (the containment\n // root), not the recorded field — a stale/corrupt provenance scope\n // would otherwise self-perpetuate through every future update.\n scope,\n overwrite: true,\n installRoot: root,\n ...(opts.cwd !== undefined ? { cwd: opts.cwd } : {}),\n ...(opts.home !== undefined ? { home: opts.home } : {}),\n ...(opts.prefix !== undefined ? { prefix: opts.prefix } : {}),\n ...(opts.onProgress !== undefined ? { onProgress: opts.onProgress } : {}),\n ...(opts.io !== undefined ? { io: opts.io } : {}),\n })\n // The upstream may have renamed its definition — the fresh install then\n // landed in a sibling folder. Remove the superseded dir so an update\n // never leaves two generations installed side by side.\n if (result.dir !== dir)\n rmSync(dir, { recursive: true, force: true })\n return { ...result, previous: provenance, upToDate: false }\n}\n\n/** The scope's canonical `.{prefix}/extensions` write root. */\nfunction resolveInstallRoot(opts: { scope: ExtensionInstallScope, cwd?: string, home?: string, prefix?: string }): string {\n const prefix = (opts.prefix ?? '.zidane').replace(/^\\./, '')\n const paths = projectUserPaths({\n subPath: 'extensions',\n ...(opts.cwd !== undefined ? { cwd: opts.cwd } : {}),\n ...(opts.home !== undefined ? { home: opts.home } : {}),\n ...(opts.prefix !== undefined ? { prefix: opts.prefix } : {}),\n })\n const root = paths.find(p => p.source === opts.scope && p.path.includes(`.${prefix}`))\n if (!root)\n throw new Error(`unreachable: projectUserPaths yields a .${prefix} root per scope`)\n return root.path\n}\n\nasync function validateCandidate(\n candidate: ExtensionInstallCandidate,\n timeoutMs: number,\n): Promise<{ name: string }> {\n const entry = findEntryFile(candidate.dir)\n if (!entry)\n throw new ExtensionInstallError('no-extension', `${candidate.dir} has no index.{ts,tsx,mjs,js} entry.`)\n let mod: unknown\n try {\n // Cache-bust: the same staged path could theoretically repeat across\n // retries in one process (mkdtemp makes that unlikely; belt+braces).\n mod = await withTimeout(\n import(`${pathToFileURL(entry).href}?zidane-install=${Date.now()}`) as Promise<unknown>,\n timeoutMs,\n `Timed out importing ${entry} after ${timeoutMs}ms.`,\n )\n }\n catch (err) {\n throw new ExtensionInstallError('validate-failed', `Importing the extension failed: ${errorMessage(err)}`)\n }\n const m = (mod ?? {}) as Record<string, unknown>\n const definition = isExtensionDefinition(m.default) ? m.default : isExtensionDefinition(m.extension) ? m.extension : null\n if (!definition)\n throw new ExtensionInstallError('validate-failed', `Module at ${entry} does not export an ExtensionDefinition (expected \\`default\\` or named \\`extension\\`).`)\n if (definition.apiVersion !== undefined && definition.apiVersion !== 1)\n throw new ExtensionInstallError('validate-failed', `Unsupported extension API version \"${definition.apiVersion}\". This runtime supports apiVersion 1.`)\n if (!/^[\\w.-]+$/.test(definition.name) || definition.name.startsWith('.'))\n throw new ExtensionInstallError('validate-failed', `Extension name \"${definition.name}\" is not folder-safe ([A-Za-z0-9_.-], no leading dot).`)\n const manifest = readExtensionManifest(candidate.dir).manifest\n if (manifest?.apiVersion !== undefined && manifest.apiVersion !== 1)\n throw new ExtensionInstallError('validate-failed', `Unsupported extension API version \"${manifest.apiVersion}\" in manifest. This runtime supports apiVersion 1.`)\n if (manifest?.name && manifest.name !== definition.name)\n throw new ExtensionInstallError('validate-failed', `Manifest name \"${manifest.name}\" does not match the module's exported name \"${definition.name}\".`)\n return { name: definition.name }\n}\n\n// ---------------------------------------------------------------------------\n// Dependency install\n// ---------------------------------------------------------------------------\n\nconst PM_COMMANDS: ReadonlyArray<{ cmd: string, args: readonly string[] }> = [\n // bun skips lifecycle scripts by default (trustedDependencies opt-in).\n { cmd: 'bun', args: ['install'] },\n { cmd: 'npm', args: ['install', '--ignore-scripts', '--no-audit', '--no-fund'] },\n { cmd: 'pnpm', args: ['install', '--ignore-scripts'] },\n { cmd: 'yarn', args: ['install', '--ignore-scripts'] },\n]\n\n/** Install deps when `dir/package.json` declares any. Returns null when skipped. */\nasync function installDependencies(\n dir: string,\n io: ExtensionInstallIo,\n onProgress?: (message: string) => void,\n): Promise<{ warnings: string[] } | null> {\n const pkg = readJson(join(dir, 'package.json')) as { dependencies?: Record<string, string> } | null\n if (!pkg?.dependencies || Object.keys(pkg.dependencies).length === 0)\n return null\n const exec = io.exec ?? defaultExec\n const which = io.which ?? defaultWhich\n const pm = PM_COMMANDS.find(p => which(p.cmd))\n if (!pm) {\n throw new ExtensionInstallError('no-package-manager', 'The extension declares dependencies but no package manager (bun/npm/pnpm/yarn) was found on PATH.')\n }\n onProgress?.(`installing dependencies with ${pm.cmd}…`)\n const res = await exec(pm.cmd, pm.args, { cwd: dir })\n if (!res.ok)\n throw new ExtensionInstallError('deps-failed', `${pm.cmd} install failed: ${lastLine(res.stderr) || lastLine(res.stdout)}`)\n return { warnings: [] }\n}\n\n// ---------------------------------------------------------------------------\n// Default IO\n// ---------------------------------------------------------------------------\n\n// ---------------------------------------------------------------------------\n// Shared helpers\n// ---------------------------------------------------------------------------\n\nfunction readJson(path: string): unknown {\n try {\n return JSON.parse(readFileSync(path, 'utf8')) as unknown\n }\n catch {\n return null\n }\n}\n","/**\n * Prompt bridge — the host hand-off behind `ctx.submitPrompt`.\n *\n * Extensions submit prompts at any point in their lifetime; the host (the\n * TUI, post-mount) attaches the single handler that routes them into its\n * real submit machinery (run-or-queue gate, `Agent.steer`, `Agent.followUp`).\n * Same holder idiom as the HUD. Prompts are deliberately NOT\n * buffered pre-attach: silently replaying stale prompts after mount would\n * be surprising — `submit` returns `false` so callers can react.\n */\n\nexport interface ExtensionPromptDelivery {\n /**\n * `'auto'` (default) — run now when idle, join the follow-up queue when a\n * run is active. `'steer'` — inject into the CURRENT run between turns\n * (falls back to `auto` when idle). `'followUp'` — queue until the current\n * run finishes (falls back to `auto` when idle).\n */\n delivery?: 'auto' | 'steer' | 'followUp'\n}\n\nexport interface ExtensionPromptBridge {\n /** Route a prompt to the host. `false` when no host handler is attached. */\n submit: (text: string, opts?: ExtensionPromptDelivery) => boolean\n /** Attach the host handler (last attach wins). Returns a detach fn. */\n attach: (handler: (text: string, opts?: ExtensionPromptDelivery) => void) => () => void\n}\n\nexport function createExtensionPromptBridge(): ExtensionPromptBridge {\n let handler: ((text: string, opts?: ExtensionPromptDelivery) => void) | null = null\n return {\n submit(text, opts) {\n if (!handler || text.trim().length === 0)\n return false\n try {\n handler(text, opts)\n return true\n }\n catch {\n // The host submit path must never throw into extension code.\n return false\n }\n },\n attach(next) {\n handler = next\n return () => {\n if (handler === next)\n handler = null\n }\n },\n }\n}\n","/**\n * Types for the extension system.\n *\n * Extensions are user-authored modules dropped at\n * `<scope>/.{agents,zidane}/extensions/<name>/index.ts` (or `.tsx`, `.js`,\n * `.mjs`). Each module exports a single `ExtensionDefinition` (default\n * export preferred, `extension` named export accepted) produced by\n * {@link defineExtension}.\n *\n * The shape mirrors what other zidane subsystems already accept:\n *\n * - Renderer-agnostic surface — tools, AgentHooks, MCP servers,\n * skills, completion providers, themes, agent profiles, system-\n * prompt fragments — all directly composable into the existing\n * {@link Preset} + {@link ResolvedConfig} machinery.\n * - `ctx.tui.*` — TUI-specific contributions (extra keybindings,\n * footer hints, modal overlays). Skipped when running under a\n * non-TUI host.\n * - `ctx.gui.*` — reserved namespace for a future GUI host. Calls\n * are recorded but unused until the GUI lands; the slot exists\n * so vibe-coded extensions can target it ahead of time without\n * re-shaping their setup later.\n *\n * Failure semantics are isolation-first: any throw inside `setup`\n * surfaces on the loaded extension as `error` so the Settings UI can\n * show the message, but the rest of the boot keeps going.\n */\n\nimport type { ReactNode } from 'react'\nimport type { AgentHooks } from '../agent'\nimport type { AgentProfile } from '../chat/agents'\nimport type { Command, CommandOutputBlock } from '../chat/commands'\nimport type { CompletionProvider } from '../chat/completion'\nimport type { ContextBreakdownContributionContext, ContextBreakdownContributor, ContextExtensionRow, ContextExtensionSection } from '../chat/context-breakdown'\nimport type { InlineAutocompleteProvider } from '../chat/inline-autocomplete'\nimport type { ProviderDescriptor } from '../chat/providers'\nimport type { Theme } from '../chat/theme'\nimport type { Logger } from '../logger'\nimport type { McpServerConfig } from '../mcp'\nimport type { Provider } from '../providers'\nimport type { SkillConfig } from '../skills'\nimport type { ExtensionSubagentDef, SubagentDef, SubagentOverride } from '../tools/spawn'\nimport type { ToolDef } from '../tools/types'\nimport type { AgentBehavior, AgentRunOptions, JsonValue, SessionMessage, ToolResultContent, ToolResultMetadata } from '../types'\nimport type {\n ExtensionConfigCategory,\n ExtensionConfigItem,\n ExtensionConfigSurface,\n StoredExtensionConfigItem,\n} from './config'\nimport type { ExtensionRuntimeSurface, ExtensionSessionsSurface, ExtensionWorkspaceSurface } from './host-runtime'\nimport type { ExtensionBannerOptions, ExtensionNotifyOptions, ExtensionStatusOptions } from './hud'\n\n// ---------------------------------------------------------------------------\n// Authoring API\n// ---------------------------------------------------------------------------\n\n/**\n * Where the extension came from. Mirrors {@link SkillSource} so a Settings\n * panel can show \"project\" vs \"user\" badges using the same vocabulary.\n *\n * - `'project'` — discovered under `<git-root>/.{agents,zidane}/extensions/`.\n * - `'user'` — discovered under `~/.{agents,zidane}/extensions/`.\n * - `'inline'` — host passed it programmatically (e.g. via\n * `RunTuiOptions.extensions` / `inlineLoadedExtension`).\n * - `'builtin'` — reserved; no built-in extensions ship today.\n */\nexport type ExtensionSource = 'project' | 'user' | 'inline' | 'builtin'\n\n/** Stable extension API version supported by this runtime. */\nexport type ExtensionApiVersion = 1\n\n/** Declarative capability labels surfaced by discovery/settings UIs. */\nexport type ExtensionCapability\n = | 'tools'\n | 'hooks'\n | 'mcps'\n | 'skills'\n | 'providers'\n | 'themes'\n | 'agents'\n | 'completions'\n | 'context'\n | 'tui'\n | 'gui'\n | 'storage'\n | 'session'\n | 'metadata'\n | 'transforms'\n\n/** Descriptive permission labels. These document intent; they are not a sandbox. */\nexport type ExtensionPermission\n = | 'session:read'\n | 'session:write'\n | 'storage:read'\n | 'storage:write'\n | 'mcp:configure'\n | 'provider:wrap'\n | 'tools:transform'\n | 'skills:activate'\n\nexport interface ExtensionManifest {\n apiVersion?: ExtensionApiVersion\n name?: string\n displayName?: string\n description?: string\n permissions?: readonly ExtensionPermission[]\n capabilities?: readonly ExtensionCapability[]\n}\n\nexport type ExtensionStorageScope = 'user' | 'project' | 'session' | 'cache' | 'data'\n\nexport interface ExtensionPaths {\n cwd: string\n projectRoot?: string\n dataDir: string\n cacheDir: string\n configDir?: string\n stateDir?: string\n}\n\nexport interface ExtensionStorageSurface {\n path: (scope: ExtensionStorageScope, ...segments: string[]) => string\n}\n\nexport interface ExtensionSessionStateSurface {\n get: <T = JsonValue>(key: string) => T | undefined\n set: (key: string, value: JsonValue) => Promise<void>\n delete: (key: string) => Promise<void>\n}\n\nexport interface ExtensionRuntimeEnv {\n extensionName: string\n source: ExtensionSource\n}\n\nexport type ExtensionRunTransform = (\n options: AgentRunOptions,\n env: ExtensionRuntimeEnv,\n) => AgentRunOptions | void\n\nexport type ExtensionBehaviorTransform = (\n behavior: AgentBehavior,\n env: ExtensionRuntimeEnv,\n) => AgentBehavior | void\n\nexport type ExtensionProviderMiddleware = (\n provider: Provider,\n env: ExtensionRuntimeEnv & { descriptor: ProviderDescriptor },\n) => Provider\n\n/**\n * Verdict from an {@link ExtensionToolPolicy}. `'allow'` (or returning\n * nothing) lets the call through; `{ block }` refuses it with the reason\n * surfaced to the model; `{ patch }` REPLACES the call's input — the loop\n * re-validates the patched input against the tool's schema before\n * execution (an invalid patch becomes a validation error, never a run\n * with unsound args).\n */\nexport type ExtensionToolVerdict\n = | 'allow'\n | { block: string }\n | { patch: Record<string, unknown> }\n\n/** A pre-execution gate over tool calls (see `ctx.registerToolPolicy`). */\nexport interface ExtensionToolPolicy {\n /** Restrict to these tool names. Omit to see every call. */\n tools?: readonly string[]\n /** Decide per call. Runs before execution; earlier verdicts win. */\n decide: (call: {\n tool: string\n input: Readonly<Record<string, unknown>>\n turnId: string\n callId: string\n }) => ExtensionToolVerdict | undefined | Promise<ExtensionToolVerdict | undefined>\n}\n\n/** Partial patch over a finished tool result (see `ctx.registerToolResultTransform`). */\nexport interface ExtensionToolResultPatch {\n result?: string | ToolResultContent[]\n isError?: boolean\n}\n\n/**\n * Middleware over finished tool results — chained in registration order,\n * each transform sees the previous one's patch applied.\n */\nexport type ExtensionToolResultTransform = (outcome: {\n tool: string\n input: Readonly<Record<string, unknown>>\n result: string | ToolResultContent[]\n isError: boolean\n}) => ExtensionToolResultPatch | undefined | Promise<ExtensionToolResultPatch | undefined>\n\n/**\n * Rewrite the wire messages right before each provider call (sugar over a\n * provider middleware). Return a replacement array or nothing to keep.\n * Runs on already-wire messages, so display-only turns are excluded by\n * construction.\n */\nexport type ExtensionContextTransform = (\n messages: readonly SessionMessage[],\n) => readonly SessionMessage[] | undefined | Promise<readonly SessionMessage[] | undefined>\n\n/**\n * Intercept a submitted prompt before skill-reference resolution (and after\n * `/` command interception). Return a replacement string to transform,\n * `{ handled: true }` to consume it entirely (show feedback via the HUD\n * surface), or nothing to pass through. Chained in registration order.\n */\nexport type ExtensionPromptTransform = (\n prompt: string,\n) => string | { handled: true } | undefined | Promise<string | { handled: true } | undefined>\n\nexport type ExtensionToolCatalogTransform = (\n tools: Readonly<Record<string, ToolDef>>,\n env: ExtensionRuntimeEnv & { phase: 'run' | 'pre-run', mcpToolNames: ReadonlySet<string> },\n) => Readonly<Record<string, ToolDef>> | void\n\nexport type ExtensionMcpConfigTransform = (\n servers: readonly McpServerConfig[],\n env: ExtensionRuntimeEnv,\n) => readonly McpServerConfig[] | void\n\nexport interface ExtensionSkillPolicy {\n name: string\n autoActivate?: boolean\n when?: (ctx: ExtensionRuntimeEnv & {\n skills: readonly SkillConfig[]\n run: Pick<AgentRunOptions, 'model' | 'prompt' | 'parentRunId' | 'depth'>\n }) => boolean | Promise<boolean>\n}\n\nexport interface ExtensionContextPanelSection {\n id: string\n title: string\n accentColor?: string\n rows?: readonly ContextExtensionRow[] | ((ctx: ContextBreakdownContributionContext) => readonly ContextExtensionRow[])\n load?: (ctx: ContextBreakdownContributionContext) =>\n | ContextExtensionSection\n | readonly ContextExtensionSection[]\n | readonly ContextExtensionRow[]\n | null\n | undefined\n | Promise<ContextExtensionSection | readonly ContextExtensionSection[] | readonly ContextExtensionRow[] | null | undefined>\n actions?: readonly { id: string, label: string }[]\n}\n\nexport interface ExtensionToolResultMetadataRegistration {\n namespace: string\n schema?: unknown\n contextRows?: (ctx: {\n namespace: string\n entries: readonly { value: JsonValue, turn: unknown, block: unknown }[]\n turns: readonly unknown[]\n }) => readonly ContextExtensionRow[] | ContextExtensionSection | readonly ContextExtensionSection[] | null | undefined\n summarize?: (metadata: ToolResultMetadata) => JsonValue | undefined\n}\n\nexport interface ExtensionOrderedContribution<T> {\n extensionName: string\n source: ExtensionSource\n value: T\n}\n\n/**\n * A TUI keybinding contributed by an extension. The `id` is namespaced\n * by convention (`<ext-name>.<action>`) so two extensions registering\n * the same logical action don't collide. `onTrigger` runs synchronously\n * inside the TUI keyboard dispatcher — heavy work should hop to its\n * own microtask.\n *\n * `default` is a spec string in the same grammar as `keybindings.json`\n * (e.g. `'ctrl+shift+j'`); pass `''` to leave the action unbound by\n * default while still registering the id so users can rebind it via\n * their personal `keybindings.json`.\n */\nexport interface ExtensionKeybinding {\n /** Globally-unique id, namespaced by extension. */\n id: string\n /** Default chord spec. Empty string = unbound by default. */\n default: string\n /** Short label used in shortcut hint rows. */\n label: string\n /** Longer description rendered in the keybindings panel. */\n description: string\n /** Fires when the chord matches. The dispatcher passes a minimal env. */\n onTrigger: (ctx: ExtensionKeybindingDispatchContext) => void | Promise<void>\n}\n\n/**\n * Argument the TUI dispatcher hands to {@link ExtensionKeybinding.onTrigger}.\n * Intentionally tiny — the extension can capture whatever it needs from\n * `setup`'s closure. Extra fields may be added in minor releases; treat\n * unknown ones as forward-compat noise.\n */\nexport interface ExtensionKeybindingDispatchContext {\n /** Stable extension name (matches {@link ExtensionDefinition.name}). */\n extensionName: string\n /** Open a modal previously registered via `ctx.tui.registerModal`. */\n openModal: (modalId: string) => void\n /** Close any open modal. */\n closeModal: () => void\n}\n\n/** Footer hint entry contributed by an extension. */\nexport interface ExtensionFooterHint {\n /** Display string for the chord (e.g. `'^J'`, `'⌘+k'`). */\n key: string\n /** Short label rendered next to the chord. */\n label: string\n}\n\n/**\n * A modal contribution's body — either an EAGER node or a LOADER thunk:\n *\n * - {@link ReactNode} — built in `setup()`, so the JSX that produces it\n * is evaluated at registration time and lands on the import graph of\n * **every** host that loads the extension (headless / embedded too).\n * - `() => ...` — a loader invoked only when the modal first opens.\n * Use `() => import('./panel')` to keep the modal's UI (and its\n * `react/jsx-runtime` JSX edge) off the import graph of non-TUI hosts.\n * A loader resolving to a module (`{ default }`) is unwrapped to its\n * `default`; a bare node / promise-of-node passes through.\n */\nexport type ExtensionModalNode\n = | ReactNode\n | (() => ReactNode | Promise<ReactNode> | Promise<{ default: ReactNode }>)\n\n/**\n * A modal overlay the extension wants the TUI shell to mount. `id`\n * matches {@link ExtensionKeybindingDispatchContext.openModal}; `node`\n * is rendered inside the active `<ModalRoot>` when opened.\n *\n * `node` is an {@link ExtensionModalNode}: an eager node builds its UI at\n * `setup()` (on the import graph everywhere the extension loads); a loader\n * (`() => import('./panel')`) runs only when the modal opens, keeping the\n * modal's UI off the import graph of headless / embedded hosts. A loader\n * returning a module (`{ default }`) is unwrapped to its `default`.\n *\n * The node may import `@opentui/react` directly — extensions running\n * under a non-TUI host will simply never see `openModal` called.\n */\nexport interface ExtensionModal {\n id: string\n node: ExtensionModalNode\n}\n\n/**\n * Props handed to an {@link ExtensionToolRenderer} at a tool-RESULT row.\n * `text` is the model-facing result (post output-caps). Deliberately\n * minimal: fields land here only once a host actually populates them\n * (paired call `input` is the named candidate) — a declared-but-never-set\n * field would invite authors to write dead branches against it.\n */\nexport interface ExtensionToolRenderProps {\n tool: string\n callId?: string\n text: string\n}\n\n/**\n * Custom view for tool-result rows. Return a node to REPLACE the default\n * rendering, or `null` to fall through to it — renderers can be selective\n * (e.g. only test output) without owning every result of a tool.\n *\n * View-layer only: nothing a renderer paints is persisted. The row's\n * underlying turn data is unchanged, so uninstalling the extension (or a\n * renderer throwing — each row is error-bounded) falls back to the\n * default rendering. TUI-scoped like modals; non-TUI hosts never call it.\n *\n * Visibility: tool renderers RE-SKIN result rows, they don't resurrect\n * them — a user hiding tool outputs (`showToolResults: false`) hides\n * custom views too, by design (the global noise preference outranks\n * extension presentation). For output that must stay visible\n * unconditionally, emit a `custom` BLOCK instead (always shown, like\n * command output).\n */\nexport type ExtensionToolRenderer = (props: ExtensionToolRenderProps) => ReactNode | null\n\nexport interface ExtensionToolRendererRegistration {\n /** Tool names to offer this renderer for. Omit = every tool. */\n tools?: readonly string[]\n render: ExtensionToolRenderer\n}\n\n/**\n * Renderer for `custom` blocks (command `emit` / `ctx.appendNote` with\n * `custom: { renderer, data }`). Receives the block's persisted JSON-safe\n * `data`. Same view-layer contract as {@link ExtensionToolRenderer}: the\n * block's `text` is the MANDATORY markdown fallback rendered whenever\n * this renderer is unavailable (uninstalled, disabled, non-TUI host) or\n * throws.\n *\n * Synchronous by design. For async data (fetch, file reads), return a\n * component that does the work internally with `useState`/`useEffect` —\n * the node mounts as a real React subtree, so hooks, loading states, and\n * effect cleanup all work normally.\n */\nexport type ExtensionCustomRenderer = (data: JsonValue | undefined) => ReactNode\n\n/** A registered custom renderer. `id` is namespaced: `<extension>/<id>`. */\nexport interface ExtensionCustomRendererEntry {\n id: string\n render: ExtensionCustomRenderer\n}\n\n/**\n * A merged tool-renderer entry. `owner` (the registering extension) is\n * attached at registration — tool renderers are otherwise anonymous, and\n * `useSessionState` inside one needs a namespace to persist under.\n */\nexport interface ExtensionToolRendererEntry extends ExtensionToolRendererRegistration {\n owner: string\n}\n\n/**\n * Reserved GUI-side surfaces. None are consumed today; the type exists\n * so extensions targeting \"both renderers\" don't have to wait for the\n * GUI to land before declaring their slot. Shape will solidify when\n * the GUI host materialises.\n */\nexport interface ExtensionGuiPanel {\n /** Stable id consumed by the eventual GUI sidebar. */\n id: string\n /** Display label. */\n label: string\n /** Renderer-provided render thunk. Opaque until the GUI host defines it. */\n render: (...args: unknown[]) => unknown\n}\n\n/**\n * Mutable registration surface handed to `setup(ctx)`. Calls are\n * collected; the loader folds them into the {@link ExtensionRegistry}\n * returned to `resolveConfig`. Method semantics:\n *\n * - Last-write-wins on canonical-key collisions across the SAME\n * extension (a setup that registers two tools called `foo` keeps\n * the second).\n * - Cross-extension collisions are resolved by registration order\n * downstream — the registry exposes the merged shape; `composePresets`\n * decides who wins on the agent side.\n *\n * The context is single-use — calling it after `setup` returns is a no-op\n * (the loader closes the gate so a late `setTimeout` can't mutate state).\n */\nexport interface ExtensionContext {\n /** Stable extension name. Mirrors {@link ExtensionDefinition.name}. */\n readonly name: string\n /** Optional `version` field from the definition. */\n readonly version?: string\n /** Absolute path to the extension's directory (when loaded from disk). */\n readonly dir?: string\n /** Where the extension came from. */\n readonly scope: ExtensionSource\n /** Pre-scoped logger — namespaced as `extension:<name>`. */\n readonly logger: Logger\n /**\n * Cooperative setup cancellation. Flips when the host times out setup or\n * disposes the owning registry while setup is still running.\n */\n readonly signal: AbortSignal\n readonly paths: ExtensionPaths\n readonly storage: ExtensionStorageSurface\n readonly session: ExtensionSessionStateSurface\n\n // -- renderer-agnostic ---------------------------------------------------\n\n /**\n * Register a tool. Keyed by `name`; collides with built-in tools and\n * other extension tools on identical names. Last call wins within an\n * extension; cross-extension overwrites are decided by load order.\n */\n registerTool: (name: string, tool: ToolDef) => void\n /**\n * Subscribe to an {@link AgentHooks} event. Handlers are folded into\n * the agent preset that the TUI / GUI host spreads into\n * `createAgent` — they fire for the **parent** agent only.\n *\n * Subagents (spawned via the built-in `spawn` tool) get their own\n * fresh `createAgent()` call with the active agent profile's preset,\n * NOT the extension preset. To observe subagent activity, subscribe\n * to the parent-side `child:*` mirror events\n * (`child:tool:before`, `child:tool:after`, `child:stream:text`, …),\n * which bubble through the parent's hook bus with `{ childId, depth }`\n * fields stamped on the context.\n *\n * Use this for run-spanning observability (logging, metrics,\n * policy gates). For run-scoped hooks pass them via\n * `AgentRunOptions.hooks` from inside a registered tool.\n */\n registerHook: <E extends keyof AgentHooks>(event: E, handler: AgentHooks[E]) => void\n /**\n * Register an MCP server programmatically — equivalent to dropping\n * an entry into `mcps.json` but without touching disk. Composed at\n * agent build time alongside the discovered `mcps.json` entries;\n * discovered config wins on `name` collision (the user's checked-in\n * file is more specific than an extension default). Cross-extension\n * collisions resolve by registration order (last wins, with a\n * warning emitted at boot).\n */\n registerMcpServer: (config: McpServerConfig) => void\n /**\n * Register a skill programmatically — same shape `defineSkill()`\n * produces. Lands in the agent's skill catalog alongside disk-\n * discovered ones. The skill's name is force-included in the user's\n * `Settings.enabledSkills` allowlist at agent build time, so an\n * extension's skill never gets silently filtered out by an\n * allowlist the user authored before installing the extension.\n */\n registerSkill: (skill: SkillConfig) => void\n /**\n * Register a prompt-completion provider — same shape used by the\n * built-in `@` (files) and `/` (skills) providers. Triggers must\n * be a single character; collisions across providers are caller-\n * resolved (first registration wins in the TUI).\n */\n registerCompletionProvider: (provider: CompletionProvider) => void\n /**\n * Register an inline (ghost-text) autocomplete provider — suggests a\n * plain suffix at the prompt cursor, rendered dim and accepted with\n * Tab. Distinct from trigger-based completion popovers: no popup, no\n * list — first provider (built-in path completion, then extensions in\n * registration order) returning a suffix wins. Deduped by `id`,\n * first registration wins.\n */\n registerInlineAutocompleteProvider: (provider: InlineAutocompleteProvider) => void\n /**\n * Register a `/` command — a workflow invoked by typing `/<name> [args]`\n * as the whole prompt. Names must be unique across the merged registry;\n * collisions are resolved first-registration-wins with a warning. See\n * {@link Command}.\n */\n registerCommand: (command: Command) => void\n /**\n * Register a {@link Theme}. The theme becomes available in the\n * Settings → Theme picker; ids must be unique across the merged\n * theme registry.\n */\n registerTheme: (theme: Theme) => void\n /**\n * Register an additional agent profile alongside the built-in Build /\n * Plan pair. Profiles show up in the agent picker; ids must be\n * unique across the merged registry.\n */\n registerAgent: (profile: AgentProfile) => void\n /**\n * Register a spawnable subagent — a named overlay the model selects via\n * the `spawn` tool's `subagent_type` field. The def carries its own\n * `name` (the registry key). Keyed by name; last call wins within an\n * extension, cross-extension collisions resolve by load order\n * (last-wins). The host merges these onto the spawn tool's base\n * registry as **adds** (extension adds beat host base on name\n * collision; see {@link registerSubagentOverride} for editing).\n */\n registerSubagent: (def: ExtensionSubagentDef) => void\n /**\n * Edit an existing subagent (host- or extension-provided) by name. The\n * function receives the current def (`undefined` when the name is\n * unknown) and returns the replacement — or `undefined` to delete it.\n * Overrides apply AFTER adds, so they win over both host base and\n * extension {@link registerSubagent} entries. Two extensions overriding\n * the same name compose in registration order.\n */\n registerSubagentOverride: (name: string, fn: SubagentOverride) => void\n /**\n * Register an additional provider descriptor. Composes with the\n * host's provider registry (defaults to {@link BUILTIN_PROVIDERS}\n * or whatever the host passed in `ChatOptions.providers`). On\n * collision by `descriptor.key`, the host's entry wins — extensions\n * can ADD providers but can't silently shadow the host's wiring.\n */\n registerProvider: (descriptor: ProviderDescriptor) => void\n /**\n * Append a fragment to the agent's system prompt. Fragments are\n * concatenated (separator: `\\n\\n`) in extension load order and\n * surfaced via a `system:transform` hook installed on every fresh\n * agent build — works for every profile (Build / Plan / host-\n * supplied) and re-attaches automatically on session activation /\n * profile switch / model swap so the fragments never drop out.\n */\n registerSystemPromptFragment: (text: string) => void\n /**\n * Register an extension-owned context panel section. The core passes\n * conversation turns to contributors and renders their returned rows\n * opaquely; extensions own all metadata interpretation.\n */\n registerContextBreakdownSection: (contributor: ContextBreakdownContributor) => void\n /** Register a richer context panel section. `registerContextBreakdownSection` remains as a compatibility alias. */\n registerContextPanelSection: (section: ExtensionContextPanelSection) => void\n registerToolResultMetadata: (registration: ExtensionToolResultMetadataRegistration) => void\n registerRunTransform: (transform: ExtensionRunTransform) => void\n registerBehaviorTransform: (transform: ExtensionBehaviorTransform) => void\n registerProviderMiddleware: (middleware: ExtensionProviderMiddleware) => void\n registerToolCatalogTransform: (transform: ExtensionToolCatalogTransform) => void\n registerMcpConfigTransform: (transform: ExtensionMcpConfigTransform) => void\n registerSkillPolicy: (policy: ExtensionSkillPolicy) => void\n /**\n * Gate tool calls before execution: allow, block (reason surfaces to the\n * model), or patch the input — patches are re-validated against the\n * tool's schema by the loop. Earlier verdicts (built-in guards, approval\n * flow, earlier policies) win. Rides the `tool:gate` hook.\n */\n registerToolPolicy: (policy: ExtensionToolPolicy) => void\n /**\n * Middleware over finished tool results (chained, registration order).\n * Rides the `tool:transform` hook, so byte budgets still apply after.\n */\n registerToolResultTransform: (transform: ExtensionToolResultTransform) => void\n /**\n * Rewrite the wire messages before each provider call — sugar over\n * `registerProviderMiddleware`, so it composes with other middleware.\n */\n registerContextTransform: (transform: ExtensionContextTransform) => void\n /**\n * Intercept submitted prompts (after `/` command interception, before\n * skill-reference resolution): transform the text or consume it.\n */\n registerPromptTransform: (transform: ExtensionPromptTransform) => void\n /**\n * Declare a config CATEGORY — a labelled grouping the Settings UIs\n * render as a sub-header. Keyed by `id` across the merged registry\n * (last registration wins on collision). Optional: items that name no\n * category fall into the extension's implicit category (id = extension\n * name); declare a category with that id to relabel the implicit group.\n */\n registerConfigCategory: (category: ExtensionConfigCategory) => void\n /**\n * Declare a typed config ITEM. Two call forms:\n *\n * - `registerConfigItem(item)` — lands in this extension's implicit\n * category (id = extension name).\n * - `registerConfigItem(categoryId, item)` — lands in `categoryId`\n * (this extension's own or another's; a brand-new id renders with\n * the id as its label until a matching `registerConfigCategory`).\n *\n * Keyed by `item.key`, namespaced per extension — two extensions can\n * both ship a `verbose` item without colliding. Last call wins within\n * an extension. The value is persisted under\n * `Settings.extensionConfig[<ext>][<key>]` and read back default-first\n * via `ctx.config.get` / the core resolver.\n */\n registerConfigItem: (categoryIdOrItem: string | ExtensionConfigItem, maybeItem?: ExtensionConfigItem) => void\n /**\n * Read THIS extension's config values, default-first. See\n * {@link ExtensionConfigSurface.get}. Safe to call during `setup`\n * (returns defaults / boot snapshot) and inside hooks / tools (returns\n * live values).\n */\n readonly config: ExtensionConfigSurface\n\n /**\n * `true` when this build runs `setup()` only to declare the config\n * schema for a DISABLED (allowlist-off) extension — so its settings are\n * browsable in Settings before the user enables it. In this mode every\n * behavioral contribution is dropped and the runtime surfaces (`tui.*`,\n * `submitPrompt`, `runtime`, `sessions`, `workspace`, `appendNote`) are\n * inert no-ops. Side-effecting setup work (network probes, spawning\n * processes, warming caches) MUST be gated on `!ctx.disabled` — a\n * disabled extension should touch nothing until enabled.\n */\n readonly disabled: boolean\n\n // -- TUI-specific --------------------------------------------------------\n\n /**\n * TUI namespace — wiring under `@opentui/react`. Skipped silently\n * by non-TUI hosts: `registerKeybinding` / `registerFooterHint` /\n * `registerModal` calls are recorded but never consumed when\n * running under a GUI shell.\n */\n readonly tui: ExtensionTuiSurface\n\n /**\n * Submit a prompt to the host as if the user typed it — runs immediately\n * when idle, joins the queue when busy, or (opt-in) steers / follows up\n * the active run. Returns `false` when no host is attached (headless, or\n * before the renderer mounts) so callers can degrade. The prompt goes\n * through the normal submit pipeline: commands, transforms, skills.\n */\n readonly submitPrompt: (text: string, opts?: { delivery?: 'auto' | 'steer' | 'followUp' }) => boolean\n\n /**\n * Reads/controls over the host's active model state. Late-bound: methods\n * return `null`/`false` until the host attaches (and on headless hosts).\n */\n readonly runtime: ExtensionRuntimeSurface\n\n /** Minimal session control (list/switch/create). Late-bound like `runtime`. */\n readonly sessions: ExtensionSessionsSurface\n\n /**\n * Shell + file operations routed through the SESSION'S execution\n * context — work happens wherever the agent's tools run (host process,\n * docker, remote sandbox), with the same transport output caps and\n * containment. Hand-rolled `child_process` in an extension always runs\n * on the HOST and silently diverges under sandboxed contexts; this is\n * the sandbox-correct way to shell out.\n *\n * Late-bound like `runtime`, but with REJECTING degradation: these are\n * imperative operations whose silent failure would corrupt caller\n * logic, so detached calls reject instead of returning `null`/`false`.\n * Guard with `await ctx.workspace.available()` or try/catch. Calling\n * before the session's first run spawns the execution handle on demand\n * (same lifecycle seam as `agent.run()`, `execution:ready` included).\n */\n readonly workspace: ExtensionWorkspaceSurface\n\n /**\n * Append a durable, display-only note to the transcript — the same block\n * vocabulary as command output (`text` markdown, or collapsed `detail`\n * rows), persisted and reload-rendered identically, never sent to the\n * model. Returns `false` when no host is attached. Blocks render at\n * append time — `custom` blocks additionally bind a registered view\n * (`ctx.tui.registerRenderer`), persisting only data + a mandatory\n * markdown fallback, never rendered output.\n */\n readonly appendNote: (blocks: readonly CommandOutputBlock[]) => boolean\n\n // -- GUI-specific (reserved) ---------------------------------------------\n\n /**\n * GUI namespace — placeholder for the future GUI shell. Calls land in\n * the registry's `guiContributions` slot so extensions can target it\n * eagerly; the GUI host (when it lands) reads them via a parallel\n * surface to {@link ExtensionTuiSurface}.\n */\n readonly gui: ExtensionGuiSurface\n}\n\n/** TUI-only registration surface. See {@link ExtensionContext.tui}. */\nexport interface ExtensionTuiSurface {\n registerKeybinding: (binding: ExtensionKeybinding) => void\n registerFooterHint: (hint: ExtensionFooterHint) => void\n /** Register a modal overlay — the keybinding's `onTrigger` opens it by `id`. */\n registerModal: (modal: ExtensionModal) => void\n /**\n * Register a custom renderer for `custom` blocks. The bare `id` is\n * auto-namespaced to `<extension>/<id>`; blocks reference the\n * namespaced form (`ctx.appendNote` auto-prefixes bare ids for you;\n * command `emit` requires the full form). See\n * {@link ExtensionCustomRenderer} for the fallback contract.\n */\n registerRenderer: (id: string, render: ExtensionCustomRenderer) => void\n /**\n * Register a custom view for tool-result rows. Overlapping claims on\n * the same tool: first registered wins (collision logged). See\n * {@link ExtensionToolRenderer}.\n */\n registerToolRenderer: (registration: ExtensionToolRendererRegistration) => void\n /**\n * Fire-and-forget transcript line (`info` / `warn` / `error` toned).\n * Ephemeral — rendered live, never persisted, never sent to the model.\n * Buffered briefly when raised before the renderer mounts. No-op on\n * hosts without a HUD.\n */\n notify: (text: string, opts?: ExtensionNotifyOptions) => void\n /**\n * This extension's single status line, rendered dim in the transcript\n * tail (the streaming-throbber slot). `null` / `''` clears it. Pass\n * `loader: true` for a working glyph, `timeout` (ms) to auto-clear\n * (e.g. a transient \"✓ done\"). Callable at any point in the extension's\n * lifetime; wiped automatically on disposal. No-op on hosts without a HUD.\n */\n setStatus: (text: string | null, opts?: ExtensionStatusOptions) => void\n /**\n * This extension's banner — a short block of lines above the prompt\n * (stacked with the todo indicator, same subtle styling). `null` or an\n * empty array clears it; `timeout` (ms) auto-clears. Wiped automatically\n * on disposal. No-op on hosts without a HUD.\n */\n setBanner: (lines: readonly string[] | null, opts?: ExtensionBannerOptions) => void\n}\n\n/** GUI-only registration surface. Shape will firm up when the GUI host ships. */\nexport interface ExtensionGuiSurface {\n registerPanel: (panel: ExtensionGuiPanel) => void\n}\n\n/**\n * The shape an extension module exports. Produced by {@link defineExtension}.\n * Stored verbatim once loaded; the loader never mutates the definition itself.\n */\nexport interface ExtensionDefinition {\n /** Stable, kebab-case-ish name. Mirrors the on-disk folder name. */\n name: string\n /** Stable API version this extension targets. Omitted means v1 for backward compatibility. */\n apiVersion?: ExtensionApiVersion\n /** Optional display name shown in host UIs. */\n displayName?: string\n /** Descriptive permission labels. Trust is still enforced by the host. */\n permissions?: readonly ExtensionPermission[]\n /** Declarative capability labels shown before setup runs. */\n capabilities?: readonly ExtensionCapability[]\n /** Optional semver-ish marker — surfaced in the Settings list. */\n version?: string\n /** One-line description shown in the Settings → Extensions list. */\n description?: string\n /**\n * Called once per registry build — boot, `/reload`, or a cwd-change\n * rebuild. The context is the only registration surface: registration\n * calls after `setup` resolves are dropped with a `ctx.logger` warning\n * (runtime affordances like `ctx.tui.*` / `submitPrompt` stay live).\n *\n * May return a promise; the loader awaits it before considering the\n * extension \"loaded\". Errors are caught and surfaced as\n * {@link LoadedExtension.error}.\n */\n setup: (ctx: ExtensionContext) => void | Promise<void>\n /**\n * Optional teardown called when the registry that ran this extension's\n * setup is disposed. Keep it idempotent; hosts may dispose defensively.\n */\n teardown?: () => void | Promise<void>\n}\n\n// ---------------------------------------------------------------------------\n// Loader plumbing\n// ---------------------------------------------------------------------------\n\n/** Result of attempting to load one extension. */\nexport interface LoadedExtension {\n /** Definition exported by the module. Absent when load failed. */\n definition?: ExtensionDefinition\n /** Stable name used for toggle persistence. Synthesised from the dir name on early errors. */\n name: string\n /** Absolute path to the extension's directory. Absent for inline extensions. */\n dir?: string\n /** Where it came from. */\n source: ExtensionSource\n /** Loader / setup error, when one occurred. Absent on success. */\n error?: string\n /** Optional manifest metadata read before import/setup. */\n manifest?: ExtensionManifest\n /**\n * Per-extension contributions captured during `setup`. Absent when\n * setup hasn't run yet (or failed). Populated by\n * `buildExtensionRegistry` so the host can filter contributions by\n * the user's enable list without re-running setup.\n */\n contributions?: ExtensionContributions\n /** True once `setup()` was invoked, even if it later failed or timed out. */\n setupRan?: boolean\n}\n\n/** Per-extension contributions recorded during `setup`. */\nexport interface ExtensionContributions {\n tools: Readonly<Record<string, ToolDef>>\n hooks: { readonly [E in keyof AgentHooks]?: ReadonlyArray<AgentHooks[E]> }\n mcpServers: readonly McpServerConfig[]\n skills: readonly SkillConfig[]\n systemFragments: readonly string[]\n contextBreakdownSections: readonly ContextBreakdownContributor[]\n contextPanelSections: readonly ContextBreakdownContributor[]\n toolResultMetadata: readonly ExtensionOrderedContribution<ExtensionToolResultMetadataRegistration>[]\n runTransforms: readonly ExtensionOrderedContribution<ExtensionRunTransform>[]\n behaviorTransforms: readonly ExtensionOrderedContribution<ExtensionBehaviorTransform>[]\n providerMiddlewares: readonly ExtensionOrderedContribution<ExtensionProviderMiddleware>[]\n toolCatalogTransforms: readonly ExtensionOrderedContribution<ExtensionToolCatalogTransform>[]\n mcpConfigTransforms: readonly ExtensionOrderedContribution<ExtensionMcpConfigTransform>[]\n skillPolicies: readonly ExtensionOrderedContribution<ExtensionSkillPolicy>[]\n promptTransforms: readonly ExtensionOrderedContribution<ExtensionPromptTransform>[]\n completionProviders: readonly CompletionProvider[]\n inlineAutocompleteProviders: readonly InlineAutocompleteProvider[]\n commands: readonly Command[]\n themes: Readonly<Record<string, Theme>>\n agents: Readonly<Record<string, AgentProfile>>\n providers: Readonly<Record<string, ProviderDescriptor>>\n /** Spawnable subagents (adds), keyed by name (name stripped from the stored def). */\n subagents: Readonly<Record<string, SubagentDef>>\n /** Subagent edits, keyed by target name. Composed in registration order. */\n subagentOverrides: Readonly<Record<string, SubagentOverride>>\n /** Config categories declared by this extension, keyed by category id. */\n configCategories: Readonly<Record<string, ExtensionConfigCategory>>\n /** Config items declared by this extension, keyed by item key (each carries its resolved `category`). */\n configItems: Readonly<Record<string, StoredExtensionConfigItem>>\n tui: TuiContributions\n gui: GuiContributions\n}\n\n/**\n * Merged contributions across every loaded + enabled extension. Each\n * sub-field is the cross-extension union of the corresponding entry\n * on {@link ExtensionContributions}:\n *\n * - **Agent-loop surface** (`tools`, `hooks`, `mcpServers`, `skills`,\n * `systemFragments`) — folded into a single `Preset` by\n * `buildExtensionPreset` in `chat/config.ts` and surfaced on\n * `ResolvedConfig.extensionPreset`, which the TUI spreads into\n * `createAgent` via `composePresets(extensionPreset, profile.preset)`.\n * - **Config-level registries** (`themes`, `agents`, `providers`) —\n * merged into `ResolvedConfig.{providers,agents}` and the theme\n * picker; host-supplied entries always win on key collision.\n * - **UI-bound** (`completionProviders`, `tuiContributions`,\n * `guiContributions`) — passed through verbatim for the active\n * renderer to wire up.\n * - **Subagent dispatch + typed config** (`subagents`,\n * `subagentOverrides`, `configCategories`, `configItems`) — surfaced\n * on `ResolvedConfig` (`extensionSubagents` feed `spawn.withSubagents`;\n * `extensionConfig*` drive the settings UIs + the `ctx.config.get`\n * resolver). See each field's inline doc below.\n */\nexport interface ExtensionRegistry {\n /** Successfully loaded + enabled extensions (one entry per ext). */\n loaded: readonly LoadedExtension[]\n /**\n * Skipped / failed extensions — kept around so the Settings modal\n * can show \"couldn't load: ...\" next to the toggle. Includes\n * disabled-but-discovered ones too (so the user can flip them on).\n */\n inactive: readonly LoadedExtension[]\n /** Tools registered across every active extension, keyed by canonical name. */\n tools: Readonly<Record<string, ToolDef>>\n /** Hook handlers, bucketed per event. */\n hooks: { readonly [E in keyof AgentHooks]?: ReadonlyArray<AgentHooks[E]> }\n /** MCP servers registered programmatically. */\n mcpServers: readonly McpServerConfig[]\n /** Programmatic skills. */\n skills: readonly SkillConfig[]\n /** System-prompt fragments, in registration order. */\n systemFragments: readonly string[]\n /** Extension-owned context panel contributors. */\n contextBreakdownSections: readonly ContextBreakdownContributor[]\n /** Rich context panel contributors, normalized to the existing contributor shape. */\n contextPanelSections: readonly ContextBreakdownContributor[]\n toolResultMetadata: readonly ExtensionOrderedContribution<ExtensionToolResultMetadataRegistration>[]\n runTransforms: readonly ExtensionOrderedContribution<ExtensionRunTransform>[]\n behaviorTransforms: readonly ExtensionOrderedContribution<ExtensionBehaviorTransform>[]\n providerMiddlewares: readonly ExtensionOrderedContribution<ExtensionProviderMiddleware>[]\n toolCatalogTransforms: readonly ExtensionOrderedContribution<ExtensionToolCatalogTransform>[]\n mcpConfigTransforms: readonly ExtensionOrderedContribution<ExtensionMcpConfigTransform>[]\n skillPolicies: readonly ExtensionOrderedContribution<ExtensionSkillPolicy>[]\n /** Prompt transforms, registration-ordered (consumed by the host submit path). */\n promptTransforms: readonly ExtensionOrderedContribution<ExtensionPromptTransform>[]\n /** Completion providers (TUI/GUI shared). */\n completionProviders: readonly CompletionProvider[]\n /** Inline ghost-text autocomplete providers, deduped by id (first wins). */\n inlineAutocompleteProviders: readonly InlineAutocompleteProvider[]\n /** `/` commands, deduped by name (first registration wins). */\n commands: readonly Command[]\n /** Themes contributed by extensions, keyed by theme id. */\n themes: Readonly<Record<string, Theme>>\n /** Agent profiles, keyed by profile id. */\n agents: Readonly<Record<string, AgentProfile>>\n /** Provider descriptors, keyed by descriptor key. */\n providers: Readonly<Record<string, ProviderDescriptor>>\n /** Spawnable subagents (adds) across every active extension, keyed by name. */\n subagents: Readonly<Record<string, SubagentDef>>\n /** Subagent edits, keyed by target name (composed in registration order). */\n subagentOverrides: Readonly<Record<string, SubagentOverride>>\n /**\n * Merged config categories across every active extension, keyed by\n * category id (global; last registration wins on collision).\n */\n configCategories: Readonly<Record<string, ExtensionConfigCategory>>\n /**\n * Merged config items, namespaced `extName → itemKey → item`. The\n * outer key preserves per-extension ownership (so two extensions can\n * declare the same item key) and groups the Settings UIs' rendering;\n * each item carries the `category` it belongs to.\n */\n configItems: Readonly<Record<string, Readonly<Record<string, StoredExtensionConfigItem>>>>\n /** TUI-specific contributions. */\n tuiContributions: TuiContributions\n /** GUI-specific contributions (reserved). */\n guiContributions: GuiContributions\n /** Dispose resources owned by setup() for this registry. Idempotent. */\n dispose: () => Promise<void>\n}\n\n/** TUI-side merged contributions. */\nexport interface TuiContributions {\n keybindings: readonly ExtensionKeybinding[]\n footerHints: readonly ExtensionFooterHint[]\n modals: readonly ExtensionModal[]\n /** Custom-block renderers, ids namespaced `<extension>/<id>`. */\n renderers: readonly ExtensionCustomRendererEntry[]\n /** Tool-result row renderers, in registration order (first match wins). */\n toolRenderers: readonly ExtensionToolRendererEntry[]\n}\n\n/** GUI-side merged contributions. */\nexport interface GuiContributions {\n panels: readonly ExtensionGuiPanel[]\n}\n\n/**\n * Empty registry — useful as a zero-value when no extensions are\n * loaded. Frozen (shallow + nested) so a consumer can't accidentally\n * mutate the shared sentinel and poison every other reader.\n */\nexport const EMPTY_EXTENSION_REGISTRY: ExtensionRegistry = Object.freeze({\n loaded: Object.freeze([]) as readonly LoadedExtension[],\n inactive: Object.freeze([]) as readonly LoadedExtension[],\n tools: Object.freeze({}) as Readonly<Record<string, ToolDef>>,\n hooks: Object.freeze({}) as ExtensionRegistry['hooks'],\n mcpServers: Object.freeze([]) as readonly McpServerConfig[],\n skills: Object.freeze([]) as readonly SkillConfig[],\n systemFragments: Object.freeze([]) as readonly string[],\n contextBreakdownSections: Object.freeze([]) as readonly ContextBreakdownContributor[],\n contextPanelSections: Object.freeze([]) as readonly ContextBreakdownContributor[],\n toolResultMetadata: Object.freeze([]) as readonly ExtensionOrderedContribution<ExtensionToolResultMetadataRegistration>[],\n runTransforms: Object.freeze([]) as readonly ExtensionOrderedContribution<ExtensionRunTransform>[],\n behaviorTransforms: Object.freeze([]) as readonly ExtensionOrderedContribution<ExtensionBehaviorTransform>[],\n providerMiddlewares: Object.freeze([]) as readonly ExtensionOrderedContribution<ExtensionProviderMiddleware>[],\n toolCatalogTransforms: Object.freeze([]) as readonly ExtensionOrderedContribution<ExtensionToolCatalogTransform>[],\n mcpConfigTransforms: Object.freeze([]) as readonly ExtensionOrderedContribution<ExtensionMcpConfigTransform>[],\n skillPolicies: Object.freeze([]) as readonly ExtensionOrderedContribution<ExtensionSkillPolicy>[],\n promptTransforms: Object.freeze([]) as readonly ExtensionOrderedContribution<ExtensionPromptTransform>[],\n completionProviders: Object.freeze([]) as readonly CompletionProvider[],\n inlineAutocompleteProviders: Object.freeze([]) as readonly InlineAutocompleteProvider[],\n commands: Object.freeze([]) as readonly Command[],\n themes: Object.freeze({}) as Readonly<Record<string, Theme>>,\n agents: Object.freeze({}) as Readonly<Record<string, AgentProfile>>,\n providers: Object.freeze({}) as Readonly<Record<string, ProviderDescriptor>>,\n subagents: Object.freeze({}) as Readonly<Record<string, SubagentDef>>,\n subagentOverrides: Object.freeze({}) as Readonly<Record<string, SubagentOverride>>,\n configCategories: Object.freeze({}) as Readonly<Record<string, ExtensionConfigCategory>>,\n configItems: Object.freeze({}) as Readonly<Record<string, Readonly<Record<string, StoredExtensionConfigItem>>>>,\n tuiContributions: Object.freeze({\n keybindings: Object.freeze([]) as readonly ExtensionKeybinding[],\n footerHints: Object.freeze([]) as readonly ExtensionFooterHint[],\n modals: Object.freeze([]) as readonly ExtensionModal[],\n renderers: Object.freeze([]) as readonly ExtensionCustomRendererEntry[],\n toolRenderers: Object.freeze([]) as readonly ExtensionToolRendererEntry[],\n }),\n guiContributions: Object.freeze({\n panels: Object.freeze([]) as readonly ExtensionGuiPanel[],\n }),\n dispose: async () => {},\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 { toolOutputByteLength } from '../types'\nimport { CompactInvalidInputError } from './errors'\nimport { utf8ByteLength } from './utils'\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 * Shrink `turns` for a `prompt_too_long` retry and return a fresh array.\n *\n * Drops whole rounds from the head until the remaining slice is at most\n * HALF the byte size of the input (always dropping at least one round).\n * Halving is the load-bearing property: the retry budget is small (3 by\n * default), so shrinkage must be geometric, not linear. Dropping a single\n * round per retry — the old behavior — never converges on a very long\n * session where each round is a rounding error against the overflow,\n * and compaction fails with `prompt_too_long after 3 retries` even\n * though a smaller prompt would have succeeded.\n *\n * A round is a contiguous `[user, assistant?, tool_results?]` group.\n *\n * Adjacency-safe: round boundaries are computed the same way as before,\n * so the result still respects every provider's `tool_use ↔ tool_result`\n * 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 const targetBytes = Math.floor(turnsByteLength(turns) / 2)\n let remaining = dropOldestRound(turns)\n if (remaining.length === turns.length)\n return remaining // nothing droppable — caller stops retrying\n\n while (remaining.length > 1 && turnsByteLength(remaining) > targetBytes) {\n const next = dropOldestRound(remaining)\n if (next.length === remaining.length)\n break\n remaining = next\n }\n return remaining\n}\n\n/**\n * Drop the oldest round from `turns`. Returns a copy of `turns` unchanged\n * when no clean round boundary exists to cut at.\n */\nfunction dropOldestRound(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\n/**\n * Rough byte size of a turn slice — text, thinking, tool outputs, and\n * tool-call inputs. Image blocks are deliberately excluded: the runner\n * strips them (`stripImagesFromTurns`) before any byte math runs.\n * Single source of truth for both the PTL halving target and the\n * runner's `onAttempt` reporting.\n */\nexport function turnsByteLength(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' || block.type === 'thinking')\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 // Prior markers reach the summarizer's wire as inlined text\n // (`turnsToMessages` renders \"[Previous compaction summary]\\n…\"),\n // so they must count toward the PTL halving target too.\n else if (block.type === 'compact-summary')\n total += utf8ByteLength(block.summary)\n }\n }\n return total\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 { ensureToolResultPairing } from '../session/messages'\nimport { linkAbortSignal, resolveTimeoutMs, withInactivityTimeout } from '../timeout'\nimport { isWireTurn } from '../types'\nimport { CompactPromptTooLongError } from './errors'\nimport {\n anchorPreviewFor,\n sliceForCompaction,\n stripImagesFromTurns,\n truncateHeadForPtlRetry,\n turnsByteLength,\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 * INACTIVITY watchdog for the provider summary call in milliseconds —\n * the clock resets on every streamed delta (text or thinking), so it\n * bounds silence, not total duration. A hung stream fails after this\n * much dead air; a healthy-but-slow stream (huge prompt → minutes of\n * generation) is never killed mid-summary. Note the window also covers\n * time-to-first-token: prompt ingestion on very large contexts emits\n * nothing, which is why the default is generous. Set `0` to disable.\n * Default: 300000 (5 minutes of silence).\n */\n providerStreamTimeoutMs?: number\n /**\n * Retries with head-truncation when the provider reports\n * `prompt_too_long`. Each retry halves the working set (dropping whole\n * rounds from the head) before re-issuing the call, so shrinkage is\n * geometric and converges even on very long sessions.\n * 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 * Streaming-progress hook — fires on every streamed delta (text AND\n * thinking) with a cumulative rough output-token estimate for the\n * CURRENT provider attempt (the counter resets on retries). Lets hosts\n * paint live \"Compacting… ~N tokens\" feedback instead of a silent\n * multi-minute spinner. Deltas arrive at provider cadence — throttle\n * on the consumer side. Synchronous and exception-safe by design.\n */\n onProgress?: (progress: CompactStreamProgress) => 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\n/** Payload for {@link CompactOptions.onProgress}. */\nexport interface CompactStreamProgress {\n /** Cumulative characters streamed so far (text + thinking) this attempt. */\n streamedChars: number\n /** Rough token estimate — `streamedChars / 4`, the classic English/code heuristic. */\n estOutputTokens: number\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 of every summary call\n * (see {@link withTrailingSummaryDirective} for the placement contract).\n * Deliberately self-identifying and TAG-FREE: unlike the system prompt's\n * TRAILER it must not contain the literal `<analysis>`/`<summary>`\n * markers, because summarizers sometimes quote the trailing user message\n * verbatim (e.g. under \"All User Messages\") and the tags would leak into\n * the summary body — caught by the compaction inference test.\n *\n * Compaction-specific: paired with the compaction system prompt, which\n * carries the actual format instructions this directive points at.\n */\nconst SUMMARY_USER_DIRECTIVE\n = '[Conversation ends here. This is an automated compaction request from the harness, NOT a message from the user — do not quote or attribute it as one. Summarize the conversation above now, following the format in your system instructions.]'\n\n/**\n * Guarantee the summary directive is the LAST content the model sees.\n *\n * The old approach (`ensureEndsWithUserMessage`) only appended the\n * directive when the conversation ended with an assistant turn. But in\n * tool-heavy sessions the tail is almost always user-role already — a\n * `tool_result`-only turn — so the directive never fired and the only\n * summarization cue was the system prompt, hundreds of thousands of\n * tokens away from the tail. On very large prompts models drift toward\n * answering the most recent message instead, producing a short\n * conversational reply that path 4 of `stripAnalysisBlock` then accepts\n * verbatim as \"the summary\" (observed in the wild: 1800 turns\n * \"summarized\" into ~200 output tokens).\n *\n * The directive is ALWAYS its own synthetic user message — never merged\n * into the trailing user message as an extra text block. An earlier\n * version merged it, and the real-model inference suite caught the\n * consequence: the summarizer attributed the directive to the USER\n * (\"All User Messages: … 'Provide your <analysis> and <summary> now.'\"),\n * contaminating the summary with harness scaffolding. Consecutive\n * user-role messages are wire-safe: Anthropic folds adjacent same-role\n * messages server-side and the other providers normalize them (same\n * contract `applyCompactSummaryCutoff` relies on). The directive text is\n * additionally self-identifying and avoids the literal envelope tags, so\n * even a model that quotes it verbatim can't leak `<analysis>`/`<summary>`\n * markup into the summary body.\n */\nfunction withTrailingSummaryDirective(messages: SessionMessage[], provider: Provider): SessionMessage[] {\n if (messages.length === 0)\n return messages\n const last = messages[messages.length - 1]\n const lastBlock = last.content[last.content.length - 1]\n if (last.role === 'user' && lastBlock?.type === 'text' && lastBlock.text === SUMMARY_USER_DIRECTIVE)\n return messages // already terminated by the directive — idempotent\n return [...messages, provider.userMessage(SUMMARY_USER_DIRECTIVE)]\n}\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\n/**\n * Default inactivity window for the summary stream. Generous on purpose:\n * it must absorb time-to-first-token on near-window prompts (~1M tokens\n * of ingestion produces no deltas for minutes). Mid-stream, any healthy\n * provider emits deltas far more often than this. The old value — a flat\n * 2-minute cap on the WHOLE call — guaranteed failure on the very\n * sessions compaction exists for (observed: 1.5M-token session, provider\n * still ingesting when the watchdog fired).\n */\nconst DEFAULT_COMPACT_STALL_TIMEOUT_MS = 5 * 60 * 1000\n\n/** Rough chars-per-token divisor for the {@link CompactOptions.onProgress} estimate. */\nconst CHARS_PER_TOKEN_ESTIMATE = 4\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 = withTrailingSummaryDirective(paired, opts.provider)\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 ...(opts.onProgress !== undefined ? { onProgress: opts.onProgress } : {}),\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: turnsByteLength(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 onProgress?: (progress: CompactStreamProgress) => void\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_STALL_TIMEOUT_MS)\n let abandoned = false\n // Live progress — counts BOTH channels (text + thinking) since\n // reasoning summarizers stream thinking long before any text.\n let progressChars = 0\n const emitProgress = () => {\n if (!opts.onProgress)\n return\n fireCompactLifecycle(() => opts.onProgress?.({\n streamedChars: progressChars,\n estOutputTokens: Math.round(progressChars / CHARS_PER_TOKEN_ESTIMATE),\n }))\n }\n // Liveness reporter wired up after the watchdog exists — deltas landing\n // before assignment just don't extend the deadline, which is harmless.\n let touchRef: () => void = () => {}\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 touchRef()\n progressChars += delta.length\n emitProgress()\n if (!abandoned)\n streamed += delta\n },\n // Reasoning models stream through the thinking channel long before\n // (or instead of) emitting text — that's liveness AND progress too.\n onThinking: (delta) => {\n touchRef()\n progressChars += delta.length\n emitProgress()\n },\n },\n )\n }\n catch (err) {\n unlink()\n throw err\n }\n streamPromise.catch(() => {})\n\n let result\n try {\n const guarded = withInactivityTimeout(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 stalled: no stream activity for ${timeoutMs}ms.`,\n })\n touchRef = guarded.touch\n result = await guarded.promise\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 // Only wire turns are summarized — `system` is out-of-band and `command`\n // is display-only, neither reaches the model. Shared, fail-safe guard.\n if (!isWireTurn(turn))\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\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 * 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, ModelTarget, ResolveContentRefBlock, RetryConfig, SessionContentBlock, SessionMessage, SessionTurn, ThinkingLevel, ToolBatchExecutionContext, ToolBatchExecutor, ToolHookContext, ToolOutcome, ToolResultContent, ToolResultMetadata, TurnUsage } from './types'\nimport { Buffer } from 'node:buffer'\nimport { rewriteContentToCanonical, rewriteMessagesToWire, toCanonicalName, toWireName } from './aliasing'\nimport { mediaAgeoutMarker, mediaPayloadBytes } from './attachment-handle'\nimport { formatMaxTurnsWarning } from './behaviors/max-turns-warning'\nimport { applyThinkingDecay } from './behaviors/thinking-decay'\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 { looksBinary } from './tools/binary-detect'\nimport { markReadStateElided } from './tools/read-state'\nimport { validateToolArgs } from './tools/validation'\nimport { documentBlockMarker, isWireTurn, sanitizeToolResultMetadata, toolOutputBudgetByteLength, toolOutputByteLength } from './types'\n\nexport { applyThinkingDecay } from './behaviors/thinking-decay'\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 /**\n * Ordered provider+model fallback chain resolved from `AgentRunOptions.model`\n * (`targets[0]` matches the initial `provider`/`model` fields). When a\n * stream fails terminally on the active target with nothing streamed yet,\n * the loop advances to the next entry — reassigning `provider`, `model`,\n * `providerName`, and the wire tool list — and re-issues. Run-sticky:\n * the index never moves backwards within a run. Absent or single-entry\n * means no fallback (today's behavior).\n */\n targets?: readonly ModelTarget[]\n /**\n * Mutable index of the active entry in {@link targets}. Owned by the loop;\n * starts at 0 (or unset) and only ever advances.\n */\n activeTargetIndex?: number\n /**\n * Failure trail of exhausted fallback targets — one `{ provider, model,\n * err }` entry per advancement, in order. Owned by the loop. When the\n * whole chain exhausts, the terminal error appends this trail so earlier\n * failures (often the actionable one — e.g. the primary's auth error)\n * aren't hidden behind the last target's error.\n */\n targetFailures?: Array<{ provider: string, model: string, err: unknown }>\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 for a provider from the current state\n * of the agent's `unlocked` set. Two consumers: every iteration the loop\n * calls this so tools newly surfaced via `tool_search` advertise on the\n * next provider request, and a model fallback calls it with the NEW\n * provider so the wire shapes match the target actually serving the run.\n * Cached per provider+tail by the host, so per-turn calls are cheap.\n * When `undefined`, the loop uses {@link LoopContext.formattedTools}\n * verbatim (and a fallback keeps the previous wire list).\n */\n rebuildFormattedTools?: (provider: Provider) => 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' nudges,\n * thinking-overrun nudges) for injection at the next turn boundary. Kept\n * separate from `steeringQueue` on purpose: the batch schedulers treat a\n * non-empty steering queue as \"superseded by user message\" and skip the\n * remaining sibling tool calls mid-batch. Nudges never interrupt a batch and\n * are dropped if the run ends before the next turn.\n */\n nonInterruptingNudgeQueue?: 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 /** Optional custom warning text for the max-turn proximity nudge. */\n maxTurnsWarningMessage?: string | ((ctx: { remaining: number, maxTurns: number }) => string)\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 /** Whole-task thinking budget (Anthropic adaptive-only). See `AgentBehavior.taskBudget`. */\n taskBudget?: 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 `0` (unlimited).\n */\n maxConsecutivePauseTurns?: number\n /**\n * When set, a turn that hits the output cap (`finishReason: 'length'`)\n * without completing a tool call injects a synthetic \"Please continue.\"\n * and re-enters the loop instead of ending the run mid-output. `runLoop`\n * bounds consecutive resumes. Off by default — see\n * `AgentBehavior.resumeOnMaxTokens`.\n */\n resumeOnMaxTokens?: boolean\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 * Convert turns to the SessionMessage[] format providers expect. Non-wire\n * turns (`system`, display-only `command`) are dropped via the shared\n * {@link isWireTurn} guard — the single, fail-safe source of truth for what\n * reaches the model.\n */\nfunction turnsToMessages(turns: SessionTurn[]): SessionMessage[] {\n return turns\n .filter(isWireTurn)\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 * Elision is MONOTONE across every marker in the session: a turn whose\n * id appears in ANY marker's `replacesTurnIds` never reaches the wire\n * again — the summary that absorbed it stands in for it permanently.\n * (Honoring only the newest marker — the old behavior — resurrected the\n * older marker's replaced turns whenever a re-compaction failed to\n * subsume it, e.g. after a PTL head-drop; observed in the wild as a\n * session re-compacting an unchanged 866k-token wire on every turn.)\n *\n * Per marker:\n *\n * 1. A marker turn never passes through verbatim (providers don't\n * know the `compact-summary` block type).\n * 2. An ACTIVE marker — one not itself replaced by a newer marker —\n * renders as a synthesized plain user-text turn carrying its\n * summary, positioned at the slot of its **first replaced turn**\n * so the summary appears CHRONOLOGICALLY where the content it\n * summarizes used to live:\n * - older unreplaced turns (PTL head-drops) come first\n * - then the synthesized summary\n * - then later unreplaced turns (preserved tail + new prompts)\n * A marker with no surviving replaced slot (empty or dangling\n * `replacesTurnIds`) renders at its own chronological position.\n * 3. A SUBSUMED marker (its id is in a newer marker's replaced set —\n * i.e. its summary text fed the newer summary) contributes only\n * elision: its replaced turns stay gone, its text does not render.\n * 4. Every other turn passes through by reference, including turns\n * no marker replaced (PTL head-drops stay visible verbatim until\n * a later compaction absorbs them — the runner compacts the WIRE\n * view, so they are always at the head of the next scope).\n *\n * If no marker exists, the input array is returned unchanged.\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 markers are the only new\n * `SessionTurn`s allocated.\n */\n/**\n * Recover the auto-compaction hysteresis baseline from a resumed session.\n *\n * Scans backward for the newest `compact-summary` marker and returns the\n * summary call's OUTPUT tokens — the same quantity {@link maybeAutoCompact}\n * records as `lastCompactedInputTokens` after a live compaction (there the\n * value is `summary.output + restorationTokens`). The restoration portion\n * isn't persisted on the marker, so this seed UNDER-counts the true\n * post-compact size; that only weakens the cooldown slightly (it requires a\n * touch more growth before re-firing) and is strictly safer than the prior\n * `undefined`, which disabled the cooldown entirely on every resume.\n *\n * Returns `undefined` when no marker exists (a never-compacted session) so\n * the first compaction still fires off the absolute threshold.\n */\nfunction seedCompactBaseline(turns: readonly SessionTurn[]): number | undefined {\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 const output = block.usage.output\n return typeof output === 'number' && Number.isFinite(output) && output >= 0\n ? output\n : undefined\n }\n }\n return undefined\n}\n\nexport function applyCompactSummaryCutoff(turns: SessionTurn[]): SessionTurn[] {\n if (turns.length === 0)\n return turns\n\n interface MarkerInfo {\n idx: number\n turn: SessionTurn\n summary: string\n replaces: readonly string[]\n }\n const markers: MarkerInfo[] = []\n for (let i = 0; i < turns.length; i++) {\n const block = turns[i].content.find(b => b.type === 'compact-summary')\n if (block && block.type === 'compact-summary')\n markers.push({ idx: i, turn: turns[i], summary: block.summary, replaces: block.replacesTurnIds })\n }\n if (markers.length === 0)\n return turns\n\n // Monotone elision: the union of EVERY marker's replaced set. Once a\n // turn has been absorbed into any summary it never reaches the wire\n // again — even when the marker that absorbed it was itself replaced\n // by a newer one (the newer summary integrated the older's text).\n const replacedAll = new Set<string>()\n for (const m of markers) {\n for (const id of m.replaces)\n replacedAll.add(id)\n }\n\n // Anchor each ACTIVE marker's synthetic at its first replaced turn's\n // slot (chronological position of the content it stands in for);\n // fall back to the marker's own slot when nothing anchors it.\n const markerIdxSet = new Set(markers.map(m => m.idx))\n const syntheticAt = new Map<number, SessionTurn>()\n for (const m of markers) {\n if (replacedAll.has(m.turn.id))\n continue // subsumed — its text fed a newer summary; elision only\n const replaces = new Set(m.replaces)\n let anchor = m.idx\n for (let i = 0; i < turns.length; i++) {\n if (i !== m.idx && replaces.has(turns[i].id) && !syntheticAt.has(i)) {\n anchor = i\n break\n }\n }\n syntheticAt.set(anchor, {\n id: m.turn.id,\n role: 'user',\n content: [{ type: 'text', text: m.summary }],\n createdAt: m.turn.createdAt,\n ...(m.turn.runId ? { runId: m.turn.runId } : {}),\n })\n }\n\n const out: SessionTurn[] = []\n for (let i = 0; i < turns.length; i++) {\n const synthetic = syntheticAt.get(i)\n if (synthetic)\n out.push(synthetic)\n if (markerIdxSet.has(i))\n continue // marker turns never pass through verbatim\n if (replacedAll.has(turns[i].id))\n continue\n out.push(turns[i])\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 */\n/**\n * TEXTUAL document payloads above this many bytes never ride inline on the\n * wire — they are replaced with the recoverable `attachment://` marker\n * immediately, even inside the `mediaKeepTurns` window, because the model\n * can page them back losslessly via read_file's line-sliced attachment path.\n * Matches read_file's default text byte cap.\n *\n * Binary media (images, PDFs, opaque blobs) is deliberately NOT size-gated\n * inside the keep window: the model can only consume those inline, so\n * stubbing a fresh one would make it permanently unviewable and set up a\n * read → stub → re-read ping-pong. Binary keeps pure age-out semantics.\n */\nexport const MEDIA_INLINE_MAX_BYTES = 262_144\n\n/**\n * Base64 chars to decode when sniffing whether a base64 document is textual.\n * Multiple of 4 (decodes cleanly), yielding ~8 KiB — the same head-sample\n * size `looksBinary` uses, so the gate and read_file's `attachmentText`\n * reach the same verdict for the same payload.\n */\nconst TEXT_SNIFF_BASE64_CHARS = 10924\n\n/**\n * True when a document block's bytes are recoverable as paged text through\n * read_file's attachment path. Only such blocks are eligible for the\n * keep-window size gate. Kept deliberately in lockstep with `attachmentText`\n * in tools/read-file.ts: text encoding → yes; PDFs → no; base64 → decode a\n * head sample and sniff.\n */\nfunction isPagedTextRecoverable(block: { type: 'image' | 'document', mediaType: string, data: string, encoding?: 'base64' | 'text' }): boolean {\n if (block.type !== 'document')\n return false\n if (block.encoding === 'text')\n return true\n if (block.mediaType === 'application/pdf')\n return false\n // Decode only a head sample — this runs per media block per request, and\n // the payload can be megabytes. A trailing clipped multi-byte char decodes\n // to a single U+FFFD, safely under looksBinary's ≥5 replacement threshold.\n const sample = Buffer.from(block.data.slice(0, TEXT_SNIFF_BASE64_CHARS), 'base64').toString('utf-8')\n return !looksBinary(sample)\n}\n\nexport function applyMediaAgeout(\n messages: SessionMessage[],\n keep: number,\n options?: { chunkTurns?: number, readPathByCallId?: ReadonlyMap<string, string>, readFileToolName?: string | null, maxInlineBytes?: number },\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 const chunk = Math.max(1, options?.chunkTurns ?? 1)\n const cutoff = rawCutoff <= 0 ? 0 : Math.floor(rawCutoff / chunk) * chunk\n // Size gate: textual documents whose payload exceeds this never ride\n // inline, even inside the keep window — a multi-MB attachment on turn 1\n // would otherwise blow the context (and the process) before age-out could\n // ever help. The marker is content-hash-stable, so gating a block on its\n // first appearance keeps the prefix byte-identical on every request.\n // Binary media is exempt while fresh — see MEDIA_INLINE_MAX_BYTES.\n const maxInline = typeof options?.maxInlineBytes === 'number' && options.maxInlineBytes > 0\n ? options.maxInlineBytes\n : Infinity\n if (cutoff <= 0 && maxInline === Infinity)\n return { messages, elidedReadPaths }\n\n const isInlineMedia = (b: { type: string, data?: unknown }): b is { type: 'image' | 'document', mediaType: string, data: string, name?: string, encoding?: 'base64' | 'text' } =>\n (b.type === 'image' || b.type === 'document') && typeof b.data === 'string'\n\n const readPathByCallId = options?.readPathByCallId\n let changed = false\n const out = messages.slice()\n for (let i = 0; i < out.length; i++) {\n const aged = i < cutoff\n if (!aged && maxInline === Infinity)\n continue\n const elides = (b: { type: 'image' | 'document', mediaType: string, data: string, encoding?: 'base64' | 'text' }): boolean =>\n aged || (mediaPayloadBytes(b.data, b.encoding) > maxInline && isPagedTextRecoverable(b))\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 (isInlineMedia(block) && elides(block)) {\n msgChanged = true\n changed = true\n return { type: 'text', text: mediaAgeoutMarker(block, 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 (isInlineMedia(part) && elides(part)) {\n outputChanged = true\n return { type: 'text' as const, text: mediaAgeoutMarker(part, 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\n/**\n * Distinct provider instances across the run's fallback targets, in chain\n * order — exposed to tools as `ToolContext.providers` so child-spawning\n * tools can route a requested model to the provider that serves it.\n * `undefined` when the run carries no target list (hand-built contexts);\n * single-provider runs return the one-element list so consumers don't\n * need a separate code path.\n */\nfunction runProvidersFor(ctx: LoopContext): readonly Provider[] | undefined {\n if (!ctx.targets || ctx.targets.length === 0)\n return undefined\n const list: Provider[] = []\n for (const t of ctx.targets) {\n if (!list.includes(t.provider))\n list.push(t.provider)\n }\n return list\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`, the loop ends the run with\n // the last turn's `finishReason: 'pause'` instead of injecting another\n // \"Please continue.\" Defaults to 0 (UNLIMITED): empty pauses are vanishingly\n // rare in practice and silently ending a live trajectory is worse than the\n // tail risk of a spin. Set a positive value to re-arm the cap; the budget\n // breakers (`maxCostUsd`/`maxTotalTokens`) are the precise runaway backstop.\n const pauseCap = ctx.maxConsecutivePauseTurns ?? 0\n let consecutiveEmptyPauseTurns = 0\n\n // Consecutive output-cap (`max_tokens`) resume counter + bound. Armed only\n // when the caller opted into `behavior.resumeOnMaxTokens`. Bounds how many\n // times in a row the loop injects a continue after a `finishReason: 'length'`\n // turn so a model that can't make progress can't loop forever. 8 × the\n // model's max output is an enormous single response — generous enough to\n // assemble any real answer, finite enough to stop a pathological spin.\n const maxConsecutiveLengthResumes = 8\n let consecutiveLengthResumes = 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`). Circuit\n // breaker resets every fresh run, but the hysteresis baseline is SEEDED\n // from the latest persisted `compact-summary` marker so the post-compact\n // cooldown survives a resume. Without the seed, a resumed run starts with\n // `lastCompactedInputTokens === undefined` → cooldown disabled → a session\n // that is still over threshold immediately re-compacts over the prior\n // summary (summary-of-a-summary degradation). See `seedCompactBaseline`.\n const autoCompactState: AutoCompactRunState = {\n lastCompactedInputTokens: seedCompactBaseline(ctx.turns),\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 // Output-cap (`max_tokens`) resume bound — mirror of the pause cap for\n // the opt-in continue-on-length path. `executeTurn` only returns\n // `lengthTruncated` when `resumeOnMaxTokens` is set, so this is inert\n // otherwise. Breaks cleanly (no `agent:abort`) leaving the last turn's\n // `finishReason: 'length'` visible in `turnUsages`.\n if (result.lengthTruncated) {\n consecutiveLengthResumes += 1\n if (consecutiveLengthResumes >= maxConsecutiveLengthResumes)\n break\n }\n else {\n consecutiveLengthResumes = 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 non-interrupting middleware nudges at the turn\n // boundary. These ride their own queue — see the\n // `nonInterruptingNudgeQueue` 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.nonInterruptingNudgeQueue && ctx.nonInterruptingNudgeQueue.length > 0) {\n if (result.ended && ctx.followUpQueue.length === 0) {\n ctx.nonInterruptingNudgeQueue.length = 0\n }\n else {\n while (ctx.nonInterruptingNudgeQueue.length > 0) {\n const nudge = ctx.nonInterruptingNudgeQueue.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 = formatMaxTurnsWarning(ctx.maxTurnsWarningMessage, { remaining, maxTurns })\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,\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 * Set when `resumeOnMaxTokens` is enabled and the turn hit the output cap\n * (`finishReason: 'length'`) with no completed tool call, triggering a\n * \"Please continue.\" resume. `runLoop` counts consecutive resumes and ends\n * the run once the internal bound is reached, leaving the last turn's\n * `finishReason: 'length'` visible in `stats`.\n */\n lengthTruncated?: 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 const wrapped = classification\n ? toTypedError(classification, ctx.provider.name, err)\n : new AgentProviderError(errorMessage(err), { provider: ctx.provider.name, cause: err })\n\n // Chain-exhaustion visibility: the surfaced error is the LAST target's,\n // but the actionable failure is often an earlier one (the primary's auth\n // or billing error, say). Append a compact per-target trail so the host\n // sees every failure without wiring a `model:fallback` handler.\n if (ctx.targetFailures && ctx.targetFailures.length > 0) {\n const trail = ctx.targetFailures\n .map(f => `${f.provider}/${f.model}: ${errorMessage(f.err).replace(/\\s+/g, ' ').slice(0, 160)}`)\n .join('; ')\n wrapped.message += ` [fallback chain — earlier targets also failed: ${trail}]`\n }\n return wrapped\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/**\n * Minimum gap between `compact:progress` hook dispatches. Provider deltas\n * arrive far faster than any UI needs — 4/s keeps the counter feeling live\n * without turning the hook into a hot path.\n */\nconst COMPACT_PROGRESS_THROTTLE_MS = 250\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 /**\n * `true` when {@link contextWindow} came from the `provider.meta.contextWindow`\n * fallback rather than explicit host config. A model fallback re-resolves\n * the window from the newly active provider in that case — the trigger\n * should measure the window of the target actually serving the run —\n * while an explicit host-set window is never overridden.\n */\n contextWindowFromProviderMeta: boolean\n keepTurns: number\n minGrowthFraction: number\n restore: boolean\n maxFailures: number\n /** Summarizer provider override, or `null` — use the run's main provider. */\n provider: Provider | null\n /** Summarizer model override, or `null` — use the run's main model. */\n model: string | null\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 contextWindowFromProviderMeta: explicitWindow === null && fallbackWindow !== null,\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 provider: cfg.provider ?? null,\n model: typeof cfg.model === 'string' && cfg.model.length > 0 ? cfg.model : null,\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 // Live progress → `compact:progress` hook, throttled so provider-cadence\n // deltas don't turn into a hook storm (hooks can be async / expensive).\n // Fire-and-forget: progress is observational, a slow listener must never\n // stall the summary stream. `ended` gates the tail — the last throttled\n // dispatch must not land after the paired `compact:end`.\n let lastProgressAt = 0\n let ended = false\n const onProgress = (p: { estOutputTokens: number }) => {\n const now = Date.now()\n if (ended || now - lastProgressAt < COMPACT_PROGRESS_THROTTLE_MS)\n return\n lastProgressAt = now\n void Promise.resolve(ctx.hooks.callHook('compact:progress', {\n ...base,\n estOutputTokens: p.estOutputTokens,\n })).catch(() => {})\n }\n\n try {\n // Summarizer overrides: config may point compaction at a different\n // provider/model than the one driving the conversation (cheaper or\n // larger-window summarizers). A bare `model` override resolves\n // against the run's main provider.\n //\n // CRITICAL: compact the WIRE view, not the raw session turns. Raw\n // turns still contain everything prior markers already replaced, so\n // a re-compaction would re-summarize the same ~full history on every\n // trigger (observed: identical 866k-token summary calls back-to-back)\n // while the wire never shrinks. The cutoff view is exactly what the\n // model sees — prior summaries inlined, replaced turns gone — so a\n // re-compaction absorbs the PTL-dropped head + prior summary + tail\n // and the wire size converges.\n const result = await compactConversation({\n provider: cfg.provider ?? ctx.provider,\n model: cfg.model ?? ctx.model,\n turns: applyCompactSummaryCutoff(ctx.turns),\n scope: 'tail',\n keepTurns: cfg.keepTurns,\n signal: ctx.signal,\n onProgress,\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 finally {\n ended = true\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 */\n/**\n * Server-declared `retry-after` above this is treated as \"not a transient\n * blip\" by the fallback machinery: burst rate limits announce seconds,\n * quota/usage exhaustion announces minutes-to-hours. A run holding more\n * fallback targets fails over immediately instead of burning its retry\n * budget against a wall that won't move within the wait window; a\n * single-target run still waits (capped at `maxDelayMs`) because waiting\n * is its only option.\n */\nconst LONG_RETRY_AFTER_FALLBACK_MS = 15_000\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 maxInlineBytes: MEDIA_INLINE_MAX_BYTES,\n })\n sanitizedMessages = aged.messages\n markReadStateForElidedPaths(ctx, ctx.handle.cwd, aged.elidedReadPaths)\n }\n\n // Decay shrinks the per-turn thinking budget. It is skipped only when the\n // active provider treats `adaptive` + `thinkingBudget` as a whole-response\n // envelope cap (Anthropic — `adaptiveBudgetIsEnvelope`): decaying that would\n // strangle text and tool-call output, not just thinking. Providers where\n // adaptive + `thinkingBudget` is a genuine per-turn thinking budget (e.g.\n // openai-compat `reasoning.max_tokens`) still decay. `taskBudget` is a\n // whole-task cap and is never routed through decay.\n // `turn` is the loop's 0-indexed counter; decay's `runTurn` contract is\n // 1-indexed (`afterTurn: 5` = turns 1-5 keep the base budget).\n const adaptiveEnvelopeCap = ctx.thinking === 'adaptive'\n && ctx.provider.meta.capabilities?.adaptiveBudgetIsEnvelope === true\n const effectiveThinkingBudget = adaptiveEnvelopeCap\n ? ctx.thinkingBudget\n : applyThinkingDecay(ctx.thinkingBudget, ctx.thinkingDecay, turn + 1)\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(ctx.provider)\n : ctx.formattedTools\n\n const streamOptions: StreamOptions = {\n model: ctx.model,\n system: ctx.system,\n tools: formattedTools,\n messages: sanitizedMessages,\n // Pass through unset: providers resolve a model-driven default (Anthropic\n // → model max output; OpenAI-style → omit so the model runs to a natural\n // stop). A hard cap only applies when the caller set `behavior.maxTokens`.\n maxTokens: ctx.maxTokens,\n thinking: ctx.thinking,\n thinkingBudget: effectiveThinkingBudget,\n ...(ctx.taskBudget !== undefined ? { taskBudget: ctx.taskBudget } : {}),\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 // Long `retry-after` short-circuit. On a retryable failure the server\n // may declare how long it will keep rejecting (429/503 `retry-after` /\n // `retry-after-ms`). Burst rate limits announce seconds; quota / usage\n // exhaustion (a subscription plan out of usage) announces minutes to\n // hours — which the backoff would cap at `maxDelayMs` and then burn the\n // whole retry budget against a wall that won't move. When the declared\n // wait exceeds the threshold AND the run still has a fallback target,\n // skip the remaining budget and let the fallback branch below advance\n // (`canRetry` implies `cleanSlate`, so the branch is guaranteed to\n // take it). Without a next target the wait IS the only option —\n // behavior unchanged.\n const retryAfterMs = canRetry ? extractRetryAfterMs(caught) : undefined\n const hasNextTarget = ctx.targets !== undefined\n && (ctx.activeTargetIndex ?? 0) + 1 < ctx.targets.length\n const longRetryAfter = retryAfterMs !== undefined\n && retryAfterMs > LONG_RETRY_AFTER_FALLBACK_MS\n && hasNextTarget\n\n if (canRetry && !longRetryAfter) {\n const meta = extractStreamErrorMeta(caught)\n const delayMs = computeRetryDelayMs(attempt, retryCfg, retryAfterMs)\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 maxInlineBytes: MEDIA_INLINE_MAX_BYTES,\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 // Provider/model fallback. The active target failed terminally (the\n // retry budget above is exhausted or the error was never retryable)\n // and nothing has streamed yet — advance to the next entry of the\n // run's fallback chain and re-issue instead of surfacing the error.\n //\n // Excluded kinds:\n // - `context_exceeded` — owned by the overflow recovery above; the\n // transcript is the problem, not the provider.\n // - `tool_pairing_corruption` — transcript-level corruption fails\n // identically on every provider.\n // Everything else (rate limit, overload, 5xx, auth/billing, even\n // unclassified network failures) is provider-side: a sibling target\n // may well succeed, and a genuinely broken request just exhausts the\n // chain and surfaces the last error as usual.\n //\n // Run-sticky: `activeTargetIndex` only advances, so later turns of\n // this run stay on the fallback target. The next `run()` resolves a\n // fresh target list starting from the primary.\n if (!wasAborted && cleanSlate && ctx.targets\n && classified?.kind !== 'context_exceeded'\n && classified?.kind !== 'tool_pairing_corruption') {\n const fromIndex = ctx.activeTargetIndex ?? 0\n const nextIndex = fromIndex + 1\n if (nextIndex < ctx.targets.length) {\n const from = ctx.targets[fromIndex]\n const to = ctx.targets[nextIndex]\n ctx.activeTargetIndex = nextIndex\n ctx.provider = to.provider\n ctx.model = to.model\n ctx.providerName = to.provider.name\n if (ctx.rebuildFormattedTools)\n ctx.formattedTools = ctx.rebuildFormattedTools(to.provider)\n // Record the exhausted target's failure — if the whole chain ends\n // up exhausting, the terminal error appends this trail so earlier\n // failures stay diagnosable (see `wrapProviderError`).\n ;(ctx.targetFailures ??= []).push({ provider: from.provider.name, model: from.model, err: caught })\n // The auto-compact trigger measures the ACTIVE target's window.\n // Preference order for the new window:\n // 1. The target's own `contextWindow` (host catalog value carried\n // on the chain entry) — trusted even over an explicit host-set\n // window, because it IS host-set, for exactly this model.\n // 2. Provider meta — only when the current value was meta-sourced\n // (never silently override an explicit host-set window).\n // When neither yields a usable number, the previous value stays —\n // a stale estimate still beats an inert trigger.\n if (ctx.autoCompact) {\n const nextWindow = to.contextWindow\n ?? (ctx.autoCompact.contextWindowFromProviderMeta ? to.provider.meta.contextWindow : undefined)\n if (typeof nextWindow === 'number' && Number.isFinite(nextWindow) && nextWindow > 0)\n ctx.autoCompact.contextWindow = nextWindow\n }\n // Rebind the prepared request to the new target: model id, wire\n // tool shapes, and a re-sanitize pass so capability skew (media\n // the new provider can't accept) degrades to markers. Messages\n // stay as-is — the alias rewrite upstream is keyed on the\n // agent-level aliasMaps, not the provider, so they're valid for\n // any target (sanitize is the only provider-dependent pass, and\n // it only degrades one way: media flattened for an earlier\n // non-vision target is not resurrected for a vision-capable one).\n //\n // Deliberately NOT rebound: `maxTokens` and the thinking budget.\n // They were sized for the primary model and there's no portable\n // provider-meta field to clamp against. A target whose ceiling\n // is below the carried budget rejects the request and the chain\n // simply advances again — self-correcting, at the cost of one\n // wasted attempt. Hosts that mix models with very different\n // output ceilings should size budgets for the smallest target.\n streamOptions.model = to.model\n streamOptions.tools = ctx.formattedTools\n streamOptions.messages = sanitizeStoredToolResults(to.provider, streamOptions.messages)\n // Fresh retry budget for the new target.\n attempt = 0\n await settledHook(ctx, 'hook model:fallback', ctx.hooks.callHook('model:fallback', {\n turnId,\n from: { provider: from.provider.name, model: from.model },\n to: { provider: to.provider.name, model: to.model },\n targetIndex: nextIndex,\n err: caught,\n }))\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 // Continue-on-length recovery (opt-in via `behavior.resumeOnMaxTokens`): the\n // model hit the output cap (`max_tokens`) without completing a tool call, so\n // ending here would truncate the trajectory mid-output. Push a synthetic\n // continue — symmetric with the pause recovery below — so the model finishes\n // instead of the run silently stopping between turns. `runLoop` bounds\n // consecutive resumes so a model that can't make progress can't loop forever.\n // Context-window exhaustion ALSO surfaces as `finishReason: 'length'`; we\n // don't special-case it because the resumed turn's pre-stream auto-compaction\n // shrinks the prompt before the next call. This MUST precede the `result.done`\n // short-circuit below: the provider marks a no-tool length turn `done`, and\n // we're pre-empting exactly that early return.\n if (\n ctx.resumeOnMaxTokens\n && result.usage.finishReason === 'length'\n && canonicalToolCalls.length === 0\n && !ctx.signal.aborted\n ) {\n const continueMsg = ctx.provider.userMessage('Please continue.')\n ctx.turns.push({\n id: await ctx.generateTurnId(),\n runId: ctx.runId,\n role: continueMsg.role,\n content: continueMsg.content,\n createdAt: await ctx.clock.now(),\n })\n return { ended: false, turnId, usage: result.usage, lengthTruncated: true as const }\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 ...(emitted.metadata ? { metadata: emitted.metadata } : {}),\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 const runProviders = runProvidersFor(ctx)\n try {\n const toolCtx: ToolContext = {\n provider: ctx.provider,\n model: ctx.model,\n ...(runProviders ? { providers: runProviders } : {}),\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 ...(emitted.metadata ? { metadata: emitted.metadata } : {}),\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, metadata?: ToolResultMetadata }> {\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 metadata: {} as ToolResultMetadata,\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 const sanitizedMetadata = sanitizeToolResultMetadata(transformCtx.metadata)\n const metadata = sanitizedMetadata.metadata\n if (sanitizedMetadata.warnings.length > 0 && process.env.ZIDANE_DEBUG) {\n process.stderr.write(\n `[zidane/loop] sanitized tool metadata for ${name}/${callId}: ${sanitizedMetadata.warnings.join('; ')}\\n`,\n )\n }\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, ...(metadata ? { metadata } : {}) }\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 ...(emitted.metadata ? { metadata: emitted.metadata } : {}),\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 runProviders = runProvidersFor(ctx)\n const toolCtx: ToolContext = {\n provider: ctx.provider,\n model: ctx.model,\n ...(runProviders ? { providers: runProviders } : {}),\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 ...(emitted.metadata ? { metadata: emitted.metadata } : {}),\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 ...(emitted.metadata ? { metadata: emitted.metadata } : {}),\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","import type { Provider } from './providers'\nimport type { ModelTarget, ModelTargetSpec } from './types'\n\n/**\n * Normalize `AgentOptions.provider` into a non-empty provider list.\n *\n * In multi-provider setups every provider needs a non-blank, unique name —\n * model entries reference providers by `Provider.name`, so a blank or\n * duplicate name would make the reference ambiguous (and surface as a\n * baffling `duplicate provider name \"\"` otherwise). A single provider\n * keeps today's behavior: its name plays no role in target resolution,\n * so no constraint applies. Throws at agent construction rather than at\n * run time.\n */\nexport function normalizeProviders(provider: Provider | Provider[]): Provider[] {\n // Copy — the agent holds this list for its lifetime, so a caller mutating\n // the array they passed must not silently rewire the fallback chain.\n const providers = Array.isArray(provider) ? [...provider] : [provider]\n if (providers.length === 0)\n throw new Error('AgentOptions.provider: provider array must not be empty')\n if (providers.length === 1)\n return providers\n const seen = new Set<string>()\n for (const p of providers) {\n if (!p.name.trim()) {\n throw new Error(\n 'AgentOptions.provider: multi-provider setups require a non-empty name on every provider — model entries reference providers by name',\n )\n }\n if (seen.has(p.name)) {\n throw new Error(\n `AgentOptions.provider: duplicate provider name \"${p.name}\" — names must be unique so model entries can reference providers unambiguously`,\n )\n }\n seen.add(p.name)\n }\n return providers\n}\n\n/**\n * Resolve a run's model selection into an ordered, de-duplicated fallback\n * chain of provider+model targets. `targets[0]` is the primary.\n *\n * Expansion rules:\n * - `undefined` → each provider's `meta.defaultModel`, in provider order.\n * - `string` → that model id on every provider, in provider order.\n * - array → entries in order; a `{ provider, model }` entry resolves to that\n * one provider (throws on unknown names), while bare strings and\n * provider-less objects fan out across all providers.\n */\nexport function resolveModelTargets(\n providers: readonly Provider[],\n model?: string | ReadonlyArray<string | ModelTargetSpec>,\n): ModelTarget[] {\n // Guard the exported boundary: `createAgent` always passes the\n // `normalizeProviders` output, but direct callers could hand an empty\n // list — which would silently resolve to zero targets and crash later.\n if (providers.length === 0)\n throw new Error('resolveModelTargets: providers list must not be empty')\n const targets: ModelTarget[] = []\n const push = (provider: Provider, model: string, contextWindow?: number): void => {\n if (!targets.some(t => t.provider === provider && t.model === model))\n targets.push({ provider, model, ...(contextWindow !== undefined ? { contextWindow } : {}) })\n }\n const fanOut = (model: string, contextWindow?: number): void => {\n for (const p of providers) push(p, model, contextWindow)\n }\n\n if (model === undefined) {\n for (const p of providers) push(p, p.meta.defaultModel)\n }\n else if (typeof model === 'string') {\n fanOut(model)\n }\n else {\n if (model.length === 0)\n throw new Error('model: fallback array must not be empty')\n for (const entry of model) {\n if (typeof entry === 'string') {\n fanOut(entry)\n }\n else if (entry.provider === undefined) {\n fanOut(entry.model, entry.contextWindow)\n }\n else {\n const p = providers.find(x => x.name === entry.provider)\n if (!p) {\n throw new Error(\n `model: unknown provider \"${entry.provider}\" — configured providers: ${providers.map(x => x.name).join(', ')}`,\n )\n }\n push(p, entry.model, entry.contextWindow)\n }\n }\n }\n return targets\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 * Agent creation and state management.\n */\n\nimport type { Hookable } from 'hookable'\n\nimport type { AliasMaps } from './aliasing'\nimport type { ContextBreakdown, ContextBreakdownContributor, ContextBreakdownOptions, ContextExactCounts, ContextExtensionRow, ContextExtensionSection, ContextMcpGroup } from './chat/context-breakdown'\nimport type { ExecutionContext, ExecutionHandle, TaskExitInfo, TaskStallInfo } from './contexts'\nimport type { ExtensionBehaviorTransform, ExtensionMcpConfigTransform, ExtensionOrderedContribution, ExtensionRegistry, ExtensionRuntimeEnv, ExtensionRunTransform, ExtensionSkillPolicy, ExtensionToolCatalogTransform, ExtensionToolResultMetadataRegistration } from './extensions'\nimport type { RunUsageMirror } from './loop'\nimport type { McpConnection } from './mcp'\nimport type { Preset } from './presets'\nimport type { Provider, StreamOptions, ToolResult, ToolSpec } from './providers'\nimport type { Session } from './session'\nimport type { PairingRepair } from './session/messages'\nimport type { ActivationVia, ActiveSkill, DeactivationReason, SkillActivationState } 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, CompactProgressHookContext, CompactStartHookContext, McpServerConfig, McpToolHookContext, McpToolSchema, OAuthRefreshHookContext, ResolveContentRefBlock, SessionContentBlock, SessionEndStatus, SessionHookContext, SessionMessage, SessionTurn, SpawnHookContext, StreamHookContext, ToolHookContext, ToolOutcome, ToolResultContent, ToolResultMetadata, TurnUsage } from './types'\nimport { resolve as pathResolve } from 'node:path'\nimport { createHooks } from 'hookable'\nimport { augmentMcpDoubleUnderscoreAliases, buildAliasMaps, buildSanitizedWireAliases, toWireName } from './aliasing'\nimport { installDedupToolsGate, installProgressNudges, installRepeatGuard, installTextToolCallRescue, installThinkingOverrunPolicy, installToolBudgetsGate, installUnknownToolRecovery, installWriteFileExistingPolicy } from './behaviors'\nimport { buildContextBreakdown } from './chat/context-breakdown'\nimport { formatTaskSummary } from './chat/format'\nimport { OUTPUT_RESERVE_TOKENS } from './compact/policy'\nimport { createProcessContext, resolveDetachedTasksCapability } from './contexts'\nimport { AgentBudgetExceededError, errorMessage } from './errors'\nimport { composeExtensionPreset } from './extensions'\nimport { DEFAULT_HOOK_TIMEOUT_MS, resolveAutoCompact, runLoop } from './loop'\nimport { connectMcpServers, wrapDiscoveredMcpTools } from './mcp'\nimport { normalizeProviders, resolveModelTargets } from './model-targets'\nimport { buildPromptMessage, canonicalizePrompt } from './prompt'\nimport { withExtensionSession } from './session'\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 { createToolCancelRegistry } from './tool-cancel'\nimport { getReadState, hydrateReadStateFromSessionAsync, readStateKey } from './tools/read-state'\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 a stream fails terminally on the active provider+model target\n * and the loop advances to the next entry of the run's fallback chain\n * (`AgentRunOptions.model` array / multi-provider fan-out) instead of\n * surfacing the error. Fires once per advancement; the terminal failure of\n * the LAST target still lands on `stream:error` as usual.\n *\n * Fallback is run-sticky: after this fires, the rest of the run stays on\n * `to` (no ping-pong back to the primary); the next `run()` starts from the\n * primary target again.\n *\n * - `from` / `to` — provider name + model id of the exhausted and newly\n * active targets.\n * - `targetIndex` — 0-based index of `to` in the resolved target list.\n * - `err` — the raw error that exhausted `from`.\n *\n * Observational — handlers cannot cancel the fallback. To disable fallback,\n * configure a single provider+model target.\n *\n * Attribution note: the session run record's `providerName` is stamped at\n * `run:start` from the PRIMARY target and is not rewritten when a fallback\n * advances — this hook (and per-turn `TurnUsage.modelId`) is the source of\n * truth for which target actually served a given stretch of the run.\n */\n 'model:fallback': (ctx: StreamHookContext & {\n from: { provider: string, model: string }\n to: { provider: string, model: string }\n targetIndex: number\n err: unknown\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 (throttled) while the compaction summary streams — carries a\n * rough cumulative output-token estimate (`estOutputTokens`, chars ÷ 4)\n * so hosts can paint live progress next to the \"Compacting…\" state.\n * Zero or more per `compact:start`; best-effort and observational.\n */\n 'compact:progress': (ctx: CompactProgressHookContext) => 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 * framework reads the final state once all handlers (including async ones,\n * which are awaited) have run. Built-in behavior gates install during run\n * setup and self-defer when another gate already set `block` / `result`.\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, metadata: ToolResultMetadata, 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 metadata: ToolResultMetadata\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 'model:fallback',\n 'context:overflow',\n 'compact:start',\n 'compact:progress',\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 /**\n * LLM provider(s) driving the agent.\n *\n * A single `Provider` is the common case. Passing an array enables\n * run-level fallback chains: `AgentRunOptions.model` entries reference\n * providers by `Provider.name`, and bare-string model ids fan out across\n * every provider in array order. Names must be unique within the array.\n * The FIRST provider is the primary — agent-level surfaces (`agent.meta`,\n * `getContextBreakdown`, the default model when `model` is omitted) read it.\n */\n provider: 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 * Pre-built {@link ExtensionRegistry} to fold into this agent, synchronously.\n *\n * Build it first (async) via\n * `buildExtensionRegistry(defs.map(inlineLoadedExtension))`, then pass the\n * RESULT here — `createAgent` is sync, so it takes the merged registry, not\n * raw definitions. Folded at the top of `createAgent` (see\n * {@link foldExtensionRegistry}), with explicit `AgentOptions` winning on\n * collision:\n * - `tools` — merged by name; `mcpServers` — deduped by name (explicit\n * wins on collision).\n * - `skills` — last-wins: an explicit `skills` REPLACES the extension's\n * wholesale (composePresets doesn't per-skill merge).\n * - `hooks` — bucketed per event; handlers from BOTH the registry and\n * `hooks` fire (registry first).\n * - system fragments — appended AFTER `system` (blank-line separated).\n * - subagents — applied to whichever `spawn` tool survives, via\n * `spawn.withSubagents`.\n *\n * Config is NOT folded: `ctx.config.get` reads at runtime through the\n * registry's own captured `readSettings` getter (a registry built without\n * one returns declared defaults). `themes` / `agents` / `providers` are\n * host/picker concerns and intentionally stay out of the agent.\n */\n extensions?: ExtensionRegistry\n /**\n * Extension-owned context panel contributors. Hosts that already spread\n * `buildExtensionPreset(registry)` can pass `registry.contextBreakdownSections`\n * here without re-folding the whole registry and duplicating hooks/tools.\n */\n contextBreakdownSections?: readonly ContextBreakdownContributor[]\n runTransforms?: readonly ExtensionOrderedContribution<ExtensionRunTransform>[]\n behaviorTransforms?: readonly ExtensionOrderedContribution<ExtensionBehaviorTransform>[]\n toolCatalogTransforms?: readonly ExtensionOrderedContribution<ExtensionToolCatalogTransform>[]\n mcpConfigTransforms?: readonly ExtensionOrderedContribution<ExtensionMcpConfigTransform>[]\n skillPolicies?: readonly ExtensionOrderedContribution<ExtensionSkillPolicy>[]\n toolResultMetadata?: readonly ExtensionOrderedContribution<ExtensionToolResultMetadataRegistration>[]\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 /**\n * Ensure the execution handle exists (spawning it if this is called\n * before the first run), firing the one-time `execution:ready` hook\n * exactly as `run()` would. The seam for hosts and extension bridges\n * (`ctx.workspace`) that need the live `(execution, handle)` pair\n * before any run has happened. Shares `run()`'s in-flight spawn\n * promise — concurrent callers observe one spawn — and mirrors\n * `run()`'s destroy semantics: calling after `destroy()` re-spawns\n * (the supported resurrection contract), while a destroy landing\n * DURING the spawn reaps the fresh handle and rejects.\n */\n ensureExecution: () => Promise<ExecutionHandle>\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 spare-space).\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' | 'taskBudget' | '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' | 'resumeOnMaxTokens' | 'persistTurns'\n | 'maxPairingRepairsPerTurn' | 'maxTurnsWarning' | 'maxTurnsWarningMessage'\n | 'backgroundOutputCap' | 'backgroundStallWatchdogMs' | 'extensions'\n | 'toolBatchExecutor' | 'toolBatchTimeoutMs' | 'shouldRethrowToolError'\n | 'thinkingOverrunPolicy' | 'readFileDefaults'\n | 'writeFileExistingPolicy' | 'textToolCallRescue' | 'unknownToolRecovery' | 'progressNudges'\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 maxTurnsWarningMessage: runBehavior?.maxTurnsWarningMessage ?? agentBehavior?.maxTurnsWarningMessage,\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 taskBudget: runBehavior?.taskBudget ?? agentBehavior?.taskBudget,\n thinkingOverrunPolicy: runBehavior?.thinkingOverrunPolicy ?? agentBehavior?.thinkingOverrunPolicy,\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 readFileDefaults: runBehavior?.readFileDefaults ?? agentBehavior?.readFileDefaults,\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 writeFileExistingPolicy: runBehavior?.writeFileExistingPolicy ?? agentBehavior?.writeFileExistingPolicy,\n textToolCallRescue: runBehavior?.textToolCallRescue ?? agentBehavior?.textToolCallRescue,\n unknownToolRecovery: runBehavior?.unknownToolRecovery ?? agentBehavior?.unknownToolRecovery,\n progressNudges: runBehavior?.progressNudges ?? agentBehavior?.progressNudges,\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 resumeOnMaxTokens: runBehavior?.resumeOnMaxTokens ?? agentBehavior?.resumeOnMaxTokens,\n persistTurns: runBehavior?.persistTurns ?? agentBehavior?.persistTurns,\n }\n}\n\nfunction extensionEnv<T>(entry: ExtensionOrderedContribution<T>): ExtensionRuntimeEnv {\n return { extensionName: entry.extensionName, source: entry.source }\n}\n\nfunction applyRunTransforms(\n options: AgentRunOptions,\n transforms: readonly ExtensionOrderedContribution<ExtensionRunTransform>[] | undefined,\n): AgentRunOptions {\n if (!transforms?.length)\n return options\n let next: AgentRunOptions = { ...options }\n for (const entry of transforms) {\n try {\n const out = entry.value(next, extensionEnv(entry))\n if (out)\n next = { ...out }\n }\n catch (err) {\n debugExtensionRuntimeError(entry.extensionName, 'run transform', err)\n }\n }\n return next\n}\n\nfunction applyBehaviorTransforms(\n behavior: ResolvedBehavior,\n transforms: readonly ExtensionOrderedContribution<ExtensionBehaviorTransform>[] | undefined,\n explicitRunBehavior: AgentBehavior | undefined,\n): ResolvedBehavior {\n if (!transforms?.length)\n return behavior\n let next: AgentBehavior = { ...behavior }\n for (const entry of transforms) {\n try {\n const out = entry.value(next, extensionEnv(entry))\n if (out)\n next = { ...out }\n }\n catch (err) {\n debugExtensionRuntimeError(entry.extensionName, 'behavior transform', err)\n }\n }\n if (explicitRunBehavior) {\n const writable = next as Record<string, unknown>\n for (const [key, value] of Object.entries(explicitRunBehavior)) {\n if (value !== undefined)\n writable[key] = value\n }\n }\n return resolveBehavior(next)\n}\n\nfunction applyMcpConfigTransforms(\n servers: readonly McpServerConfig[],\n transforms: readonly ExtensionOrderedContribution<ExtensionMcpConfigTransform>[] | undefined,\n): McpServerConfig[] {\n let next = [...servers]\n if (!transforms?.length)\n return next\n for (const entry of transforms) {\n try {\n const out = entry.value(next, extensionEnv(entry))\n if (out)\n next = [...out]\n }\n catch (err) {\n debugExtensionRuntimeError(entry.extensionName, 'MCP config transform', err)\n }\n }\n return next\n}\n\nfunction applyToolCatalogTransforms(\n tools: Readonly<Record<string, ToolDef>>,\n transforms: readonly ExtensionOrderedContribution<ExtensionToolCatalogTransform>[] | undefined,\n phase: 'run' | 'pre-run',\n mcpToolNames: ReadonlySet<string>,\n): Record<string, ToolDef> {\n let next: Readonly<Record<string, ToolDef>> = tools\n if (transforms?.length) {\n for (const entry of transforms) {\n try {\n const out = entry.value(next, { ...extensionEnv(entry), phase, mcpToolNames })\n if (out)\n next = out\n }\n catch (err) {\n debugExtensionRuntimeError(entry.extensionName, 'tool catalog transform', err)\n }\n }\n }\n const out: Record<string, ToolDef> = {}\n for (const tool of Object.values(next)) {\n if (tool?.spec?.name)\n out[tool.spec.name] = tool\n }\n return out\n}\n\nasync function applySkillPolicies(input: {\n policies: readonly ExtensionOrderedContribution<ExtensionSkillPolicy>[] | undefined\n skills: readonly SkillConfig[]\n options: AgentRunOptions\n activate: (skill: SkillConfig) => Promise<void>\n}): Promise<void> {\n if (!input.policies?.length || input.skills.length === 0)\n return\n const byName = new Map(input.skills.map(skill => [skill.name, skill]))\n for (const entry of input.policies) {\n const policy = entry.value\n if (!policy.autoActivate)\n continue\n const skill = byName.get(policy.name)\n if (!skill)\n continue\n try {\n const ok = policy.when\n ? await policy.when({\n ...extensionEnv(entry),\n skills: input.skills,\n run: {\n ...(input.options.model !== undefined ? { model: input.options.model } : {}),\n ...(input.options.prompt !== undefined ? { prompt: input.options.prompt } : {}),\n ...(input.options.parentRunId !== undefined ? { parentRunId: input.options.parentRunId } : {}),\n ...(input.options.depth !== undefined ? { depth: input.options.depth } : {}),\n },\n })\n : true\n if (ok)\n await input.activate(skill)\n }\n catch (err) {\n debugExtensionRuntimeError(entry.extensionName, 'skill policy', err)\n }\n }\n}\n\nfunction toolResultMetadataSections(\n registrations: readonly ExtensionOrderedContribution<ExtensionToolResultMetadataRegistration>[] | undefined,\n turns: readonly SessionTurn[],\n): ContextExtensionSection[] {\n if (!registrations?.length)\n return []\n const out: ContextExtensionSection[] = []\n for (const entry of registrations) {\n const registration = entry.value\n const entries: Array<{ value: ToolResultMetadata[string], turn: SessionTurn, block: Extract<SessionContentBlock, { type: 'tool_result' }> }> = []\n for (const turn of turns) {\n for (const block of turn.content) {\n if (block.type !== 'tool_result' || !block.metadata)\n continue\n const value = block.metadata[registration.namespace]\n if (value !== undefined)\n entries.push({ value, turn, block })\n }\n }\n if (entries.length === 0)\n continue\n try {\n const projected = registration.contextRows?.({\n namespace: registration.namespace,\n entries,\n turns,\n })\n if (projected) {\n if (Array.isArray(projected)) {\n // An explicitly-empty projection means \"nothing to show\" — skip the\n // namespace entirely rather than emit a header with no rows.\n if (projected.length === 0)\n continue\n if (projected.every(row => row && typeof row === 'object' && 'value' in row))\n out.push({ id: `metadata:${registration.namespace}`, label: registration.namespace, rows: projected as ContextExtensionRow[] })\n else\n out.push(...projected as ContextExtensionSection[])\n }\n else {\n out.push(projected as ContextExtensionSection)\n }\n continue\n }\n }\n catch (err) {\n debugExtensionRuntimeError(entry.extensionName, 'tool-result metadata projection', err)\n }\n out.push({\n id: `metadata:${registration.namespace}`,\n label: registration.namespace,\n rows: [{ label: 'Entries', value: String(entries.length) }],\n })\n }\n return out\n}\n\nfunction debugExtensionRuntimeError(extensionName: string, surface: string, err: unknown): void {\n if (!process.env.ZIDANE_DEBUG)\n return\n process.stderr.write(`[zidane/extensions] ${extensionName} ${surface} failed: ${errorMessage(err)}\\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 * Rebuild skill activation state from a session's `skills_use` tool_calls,\n * honoring the most recent `mode` per skill (`activate` wins unless a later\n * `deactivate` overrides it). Idempotent: `activate()` is a no-op for\n * already-active skills, so re-running is safe and only fires `skills:activate`\n * (`via: 'resume'`) for skills it newly activates. Shared by the run-start\n * resume rehydration and the mid-loop durable-replay gate below.\n */\nasync function rehydrateActiveSkillsFromTurns(\n session: Session,\n resolvedSkills: readonly SkillConfig[],\n state: SkillActivationState,\n hooks: Hookable<AgentHooks>,\n): Promise<void> {\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 lastModeBySkill.set(skillName, input?.mode === 'deactivate' ? 'deactivate' : 'activate')\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 (state.activate(skill, 'resume') === 'ok')\n await hooks.callHook('skills:activate', { skill, via: 'resume' })\n }\n}\n\n/**\n * Skill-activation pre-hydration, as a `tool:gate` hook. Same hazard shape as\n * `installReadBeforeEditHydrationGate`: `skills_read` / `skills_run_script`\n * refuse inside their (possibly journaled) `execute` body when the named skill\n * isn't active, but activation lives in an in-memory state machine populated by\n * `skills_use`'s LIVE body. Under durable replay, a `skills_use` from earlier in\n * the SAME run is replayed from the journal (its `state.activate` never re-runs),\n * and the run-start resume rehydration only saw the pre-run turns — so the later\n * `skills_read` runs live with the skill inactive and journals a permanent\n * \"skill is not active\" refusal.\n *\n * This gate fires in the loop before the body, re-runs on every replay, and\n * rebuilds activation from the in-memory `session.turns` (which replay\n * repopulates with the within-run `skills_use`) when the target skill is\n * inactive. It never blocks — the body keeps the authoritative check.\n */\nfunction installSkillsActivationHydrationGate(\n hooks: Hookable<AgentHooks>,\n deps: {\n session: Session | undefined\n getResolvedSkills: () => readonly SkillConfig[] | null\n state: SkillActivationState\n },\n): () => void {\n const session = deps.session\n if (!session)\n return () => {}\n return hooks.hook('tool:gate', async (ctx) => {\n if (ctx.name !== 'skills_read' && ctx.name !== 'skills_run_script')\n return\n const resolvedSkills = deps.getResolvedSkills()\n if (!resolvedSkills || resolvedSkills.length === 0)\n return\n const skillName = typeof (ctx.input as { name?: unknown })?.name === 'string'\n ? (ctx.input as { name: string }).name\n : undefined\n if (!skillName || deps.state.isActive(skillName))\n return\n try {\n await rehydrateActiveSkillsFromTurns(session, resolvedSkills, deps.state, hooks)\n }\n catch {\n // Best-effort — the body still performs the authoritative `isActive` check.\n }\n })\n}\n\n/**\n * Lazy-tool-disclosure unlock pre-hydration, as a `tool:gate` hook. Same hazard\n * shape again: `tool_search`'s LIVE body adds matched tools to the in-memory\n * `unlocked` set, but under durable replay a `tool_search` from earlier in the\n * SAME run is replayed from the journal (its `unlocked.add` never re-runs) and\n * the run-start resume replay only covered pre-run turns. So `installLazyDisclosureGate`\n * would then see the tool as locked and refuse it — diverging from the original\n * (live) execution that ran it.\n *\n * Registered BEFORE `installLazyDisclosureGate` so its rebuild lands before the\n * block decision. When a locked lazy tool is dispatched, it replays every\n * successfully-resolved `tool_search` in the in-memory `session.turns` back\n * through `applyToolSearchToUnlocked` (idempotent), so the unlock set reflects\n * what the model was already shown.\n */\nfunction installLazyDisclosureHydrationGate(\n hooks: Hookable<AgentHooks>,\n deps: {\n enabled: boolean\n session: Session | undefined\n lazyCanonicalNames: ReadonlySet<string>\n lazyEntries: readonly LazyToolEntry[]\n unlocked: Set<string>\n recordUnlock: (canonical: string) => void\n defaultLimit: number | undefined\n },\n): () => void {\n const { enabled, session, lazyCanonicalNames, lazyEntries, unlocked, recordUnlock, defaultLimit } = deps\n if (!enabled || !session || lazyCanonicalNames.size === 0)\n return () => {}\n return hooks.hook('tool:gate', (ctx) => {\n if (ctx.block)\n return\n if (!lazyCanonicalNames.has(ctx.name) || unlocked.has(ctx.name))\n return\n // Locked lazy tool — rebuild the unlock set from the durable transcript.\n // Only replay `tool_search` calls whose result the model actually consumed\n // (mirrors the run-start resume replay), so a stranded/errored search can't\n // speculatively unlock anything.\n const resolvedCallIds = new Set<string>()\n for (const turn of session.turns) {\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 session.turns) {\n for (const block of turn.content) {\n if (block.type !== 'tool_call' || block.name !== 'tool_search')\n continue\n if (!resolvedCallIds.has(block.id))\n continue\n applyToolSearchToUnlocked(lazyEntries, block.input, unlocked, defaultLimit, recordUnlock)\n }\n }\n })\n}\n\n/**\n * Read-before-edit read-state pre-hydration, as a `tool:gate` hook.\n *\n * The `requireReadBeforeEdit` verdict lives inside `edit` / `multi_edit`'s\n * `execute` body, which compares the model-provided path against the\n * per-session read-state map. That map is in-memory and populated by side\n * effects (`read_file`'s `rememberRead`, a prior edit's `readState.set`) that\n * run only on LIVE execution. Under a durable/replay runtime (Restate &\n * friends) those side effects DON'T re-run on replay — and worse, a host that\n * journals the tool body freezes whatever verdict the body produced. So if the\n * map is empty the first time an edit runs live (e.g. the preceding `read_file`\n * was replayed from the journal on a fresh process, or the host's own wrapper —\n * not zidane's `restateTool` — never hydrated), the edit journals a permanent\n * \"has not been read\" rejection that no later replay can undo.\n *\n * This gate closes that hole independently of how the consumer wraps tools: it\n * fires in zidane's loop BEFORE the tool's (possibly journaled) `execute`, and\n * re-fires on every replay. When the target path is missing from the read-state\n * map, it rebuilds the map from the DURABLE transcript (`session.turns`, which\n * replay repopulates) — purely, outside any journal — so the downstream\n * `execute` check sees a populated map and never journals a false negative.\n *\n * Cheap by construction: it only scans when the path is genuinely absent from\n * the map (a single `Map.has` on the hot path), and never blocks — the actual\n * verdict (including disk-drift detection) stays in `execute`.\n */\nfunction installReadBeforeEditHydrationGate(\n hooks: Hookable<AgentHooks>,\n enabled: boolean,\n deps: {\n session: Session | undefined\n getHandle: () => ExecutionHandle | null | undefined\n execution: ExecutionContext\n aliasMaps: AliasMaps\n defaultLineNumbers: boolean\n },\n): () => void {\n const session = deps.session\n if (!enabled || !session)\n return () => {}\n return hooks.hook('tool:gate', async (ctx) => {\n // Names are canonical at gate time (the loop rewrites wire → canonical\n // before dispatch), so match the canonical edit tools directly.\n if (ctx.name !== 'edit' && ctx.name !== 'multi_edit')\n return\n const handle = deps.getHandle()\n if (!handle)\n return\n const target = typeof (ctx.input as { path?: unknown })?.path === 'string'\n ? (ctx.input as { path: string }).path\n : undefined\n if (!target)\n return\n const map = getReadState(session)\n if (!map)\n return\n const key = readStateKey(handle.cwd, target)\n if (map.has(key))\n return\n try {\n await hydrateReadStateFromSessionAsync(session, handle.cwd, {\n defaultLineNumbers: deps.defaultLineNumbers,\n aliasMaps: deps.aliasMaps,\n readFileForHash: async (path) => {\n try {\n return await deps.execution.readFile(handle, path)\n }\n catch {\n return null\n }\n },\n })\n }\n catch {\n // Best-effort — `execute` still performs the authoritative check, and a\n // recoverable \"re-read first\" tool_result is the worst case.\n }\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\n/**\n * Append extension system-prompt fragments after the agent's own `system`,\n * separated by a blank line. The base prompt always leads; fragments trail in\n * registration order. Returns `system` unchanged when there are no fragments.\n */\nfunction appendSystemFragments(system: string | undefined, fragments: readonly string[]): string | undefined {\n if (fragments.length === 0)\n return system\n return [...(system ? [system] : []), ...fragments].join('\\n\\n')\n}\n\n/**\n * Fold a pre-built {@link ExtensionRegistry} into plain {@link AgentOptions},\n * returning a fresh options object with `extensions` stripped — so the rest of\n * {@link createAgent} runs against a registry-free shape and stays untouched.\n *\n * Precedence mirrors the chat host (`composePresets(extensionPreset,\n * profile.preset)`): the extension is the BASE, explicit `AgentOptions` WIN on\n * collision.\n * - `tools` (merged by name) / `mcpServers` (deduped by name) / `toolAliases`\n * / `behavior` (tool-name-keyed sub-records deep-merge) — composed via\n * {@link composePresets}(extension, explicit); explicit wins on collision.\n * - `skills` — last-wins, so an explicit `skills` REPLACES the extension's\n * wholesale (`composePresets` doesn't per-skill merge).\n * - `hooks` — bucketed per event so handlers from BOTH sides fire (extension\n * first, then explicit); \"winning\" doesn't apply, both run.\n * - `system` — handled OUTSIDE the compose: `buildExtensionPreset` omits\n * fragments, and we keep `system` out of the composed presets because\n * `composePresets` replaces (last-wins) rather than appends. Fragments are\n * appended after the agent's own `system` via {@link appendSystemFragments}.\n * - subagents — whichever `spawn` tool survives the compose (explicit or\n * extension) is rebuilt with `spawn.withSubagents(reg.subagents,\n * reg.subagentOverrides)` so the model can dispatch extension subagents.\n *\n * Config is not folded — `ctx.config.get` reads live through the registry's\n * captured `readSettings`. `themes` / `agents` / `providers` are host/picker\n * concerns and intentionally left out.\n */\nfunction foldExtensionRegistry(options: AgentOptions): AgentOptions {\n const reg = options.extensions\n if (!reg)\n return options\n\n // Explicit options as a preset slice, layered LAST so they win. Only the\n // fields `buildExtensionPreset` can contribute (plus toolAliases/behavior,\n // which extensions never set but we route through for a uniform merge).\n const explicitPreset: Preset = {\n ...(options.tools !== undefined ? { tools: options.tools } : {}),\n ...(options.toolAliases !== undefined ? { toolAliases: options.toolAliases } : {}),\n ...(options.behavior !== undefined ? { behavior: options.behavior } : {}),\n ...(options.mcpServers !== undefined ? { mcpServers: options.mcpServers } : {}),\n ...(options.skills !== undefined ? { skills: options.skills } : {}),\n ...(options.hooks !== undefined ? { hooks: options.hooks } : {}),\n ...(options.runTransforms !== undefined ? { runTransforms: options.runTransforms } : {}),\n ...(options.behaviorTransforms !== undefined ? { behaviorTransforms: options.behaviorTransforms } : {}),\n ...(options.toolCatalogTransforms !== undefined ? { toolCatalogTransforms: options.toolCatalogTransforms } : {}),\n ...(options.mcpConfigTransforms !== undefined ? { mcpConfigTransforms: options.mcpConfigTransforms } : {}),\n ...(options.skillPolicies !== undefined ? { skillPolicies: options.skillPolicies } : {}),\n ...(options.toolResultMetadata !== undefined ? { toolResultMetadata: options.toolResultMetadata } : {}),\n }\n const composed = composeExtensionPreset(reg, explicitPreset)\n const contextBreakdownSections = [\n ...reg.contextBreakdownSections,\n ...reg.contextPanelSections,\n ...(options.contextBreakdownSections ?? []),\n ]\n\n const system = appendSystemFragments(options.system, reg.systemFragments)\n\n return {\n ...options,\n ...(composed.tools !== undefined ? { tools: composed.tools } : {}),\n ...(composed.toolAliases !== undefined ? { toolAliases: composed.toolAliases } : {}),\n ...(composed.behavior !== undefined ? { behavior: composed.behavior } : {}),\n ...(composed.mcpServers !== undefined ? { mcpServers: composed.mcpServers } : {}),\n ...(composed.skills !== undefined ? { skills: composed.skills } : {}),\n ...(composed.hooks !== undefined ? { hooks: composed.hooks } : {}),\n ...(composed.runTransforms !== undefined ? { runTransforms: composed.runTransforms } : {}),\n ...(composed.behaviorTransforms !== undefined ? { behaviorTransforms: composed.behaviorTransforms } : {}),\n ...(composed.toolCatalogTransforms !== undefined ? { toolCatalogTransforms: composed.toolCatalogTransforms } : {}),\n ...(composed.mcpConfigTransforms !== undefined ? { mcpConfigTransforms: composed.mcpConfigTransforms } : {}),\n ...(composed.skillPolicies !== undefined ? { skillPolicies: composed.skillPolicies } : {}),\n ...(composed.toolResultMetadata !== undefined ? { toolResultMetadata: composed.toolResultMetadata } : {}),\n ...(system !== undefined ? { system } : {}),\n ...(contextBreakdownSections.length > 0 ? { contextBreakdownSections } : {}),\n extensions: undefined,\n }\n}\n\nconst warnedContextBreakdownContributors = new WeakSet<ContextBreakdownContributor>()\n\nfunction cloneTurnsForContextBreakdown(turns: readonly SessionTurn[]): SessionTurn[] {\n try {\n return structuredClone(turns) as SessionTurn[]\n }\n catch {\n return turns.map(turn => ({\n ...turn,\n content: turn.content.map(block =>\n block && typeof block === 'object'\n ? { ...block }\n : block,\n ) as SessionTurn['content'],\n }))\n }\n}\n\nfunction isContextExtensionSection(value: unknown): value is ContextExtensionSection {\n if (!value || typeof value !== 'object')\n return false\n const section = value as { id?: unknown, label?: unknown, accentColor?: unknown, rows?: unknown }\n return typeof section.id === 'string'\n && typeof section.label === 'string'\n && (section.accentColor === undefined || typeof section.accentColor === 'string')\n && Array.isArray(section.rows)\n && section.rows.every(row =>\n row\n && typeof row === 'object'\n && typeof (row as { label?: unknown }).label === 'string'\n && typeof (row as { value?: unknown }).value === 'string',\n )\n}\n\nfunction warnContextContributorFailureOnce(contributor: ContextBreakdownContributor, err: unknown): void {\n if (warnedContextBreakdownContributors.has(contributor))\n return\n warnedContextBreakdownContributors.add(contributor)\n if (!process.env.ZIDANE_DEBUG)\n return\n const extension = (contributor as { __zidaneExtensionName?: unknown }).__zidaneExtensionName\n const owner = typeof extension === 'string' ? ` extension=${extension}` : ''\n process.stderr.write(`[zidane/agent] context breakdown contributor failed${owner}: ${errorMessage(err)}\\n`)\n}\n\nexport function createAgent(rawOptions: AgentOptions): Agent {\n const { provider: providerOption, 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, contextBreakdownSections, runTransforms, behaviorTransforms, toolCatalogTransforms, mcpConfigTransforms, skillPolicies, toolResultMetadata } = foldExtensionRegistry(rawOptions)\n // Normalized provider list (validated: non-empty, unique names). `provider`\n // stays the PRIMARY provider — every agent-level surface (frozen `meta`,\n // context breakdown, default model) reads it, preserving single-provider\n // behavior byte-for-byte. Additional providers only come into play through\n // run-level model-target resolution (fallback chains).\n const providers = normalizeProviders(providerOption)\n const provider = providers[0]\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 destroyed = false\n /**\n * Bumped by every `destroy()`. The spawn seam compares epochs instead of\n * the `destroyed` boolean because destroy-then-run RESURRECTION is a\n * supported contract (see \"allows re-spawn after destroy\"): a spawn\n * started AFTER a destroy is legitimate, only a destroy landing DURING\n * a spawn must reap the fresh handle.\n */\n let destroyGeneration = 0\n // Shared in-flight spawn promise — same idiom as `mcpWarmupPromise`:\n // `run()`'s lazy bootstrap and the public `ensureExecution()` (extension\n // `ctx.workspace`, host bridges) must observe ONE spawn + ONE\n // `execution:ready` journal event, never race two containers into\n // existence. A rejected spawn clears the slot so the next caller retries.\n let executionSpawnPromise: Promise<ExecutionHandle> | null = null\n\n async function ensureExecutionHandle(spawnTimeoutMs: number): Promise<ExecutionHandle> {\n // Fast path only when BOTH the handle exists and the ready hook fired —\n // an injected handle still owes the hook its one `spawned: false` call.\n if (executionHandle && executionReadyFired)\n return executionHandle\n executionSpawnPromise ??= (async () => {\n try {\n const generationAtStart = destroyGeneration\n const spawnedThisCall = !executionHandle\n if (!executionHandle) {\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 // Destroy raced THIS spawn (epoch advanced mid-flight): the fresh\n // handle would leak a live container on a dead agent — tear it\n // down and reject. An epoch, not the `destroyed` boolean: spawns\n // STARTED after a destroy are the supported resurrection path.\n if (destroyGeneration !== generationAtStart) {\n const leaked = executionHandle\n executionHandle = null\n // Only reap a handle WE spawned — an injected handle is\n // host-owned and must survive the agent (see `injectedHandle`).\n if (leaked && spawnedThisCall)\n await executionContext.destroy(leaked).catch(() => {})\n throw new Error('agent destroyed while the execution context was spawning')\n }\n // Surface the bound handle ONCE — the journalable seam for durable\n // hosts that want to record the minted id. `spawned` distinguishes\n // 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 return executionHandle\n }\n finally {\n executionSpawnPromise = null\n }\n })()\n return executionSpawnPromise\n }\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 = applyMcpConfigTransforms(mcpServers ?? [], mcpConfigTransforms)\n const steeringQueue: string[] = []\n // Advisory nudges. Deliberately NOT `steeringQueue`: the batch schedulers\n // skip remaining sibling tool calls when the steering queue is non-empty\n // mid-batch. These drain at the turn boundary instead.\n const nonInterruptingNudgeQueue: 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 function run(options: AgentRunOptions): Promise<AgentStats> {\n // Bind the active session for `ctx.session` reads/writes for the whole run\n // (run-scoped, not process-global) so concurrent agents stay isolated.\n return withExtensionSession(session, () => runWithinSession(options))\n }\n\n async function runWithinSession(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 options = applyRunTransforms(options, runTransforms)\n\n // Resolve the run's provider+model fallback chain. `targets[0]` is the\n // primary target this run starts on; the loop advances down the list on\n // terminal stream failures (run-sticky — the next run resolves afresh\n // and starts from the primary again). Throws on unknown provider names\n // before any run bookkeeping happens.\n const targets = resolveModelTargets(providers, options.model)\n const runProvider = targets[0].provider\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 = applyBehaviorTransforms(\n resolveBehavior(agentBehavior, options.behavior),\n behaviorTransforms,\n options.behavior,\n )\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 // Persist the run record at START, not only at finalize. Without\n // this, a killed process (crash, SIGKILL, dev-loop restart) loses\n // the record while the run's TURNS survive via `appendTurns` —\n // and replay then has orphan runIds it must guess about\n // (`eventsFromTurns` synthesis mis-classified top-level runs as\n // `child-N` when the previous run died with unresolved\n // tool_calls). Upsert semantics make the finalize-time\n // `updateRun` refresh this same row. Stale `'running'` records\n // from killed processes are demoted on load by\n // `demoteStaleRunningStatus`.\n //\n // Best-effort on BOTH failure axes: a hung store is bounded by\n // `settleWithinTimeout`, and a REJECTING store is swallowed here\n // — this write is a durability bonus, not a run precondition\n // (finalize's `updateRun` lands the record regardless). Note the\n // write can also silently no-op on stores that refuse unknown\n // session ids (fresh session, header not yet created); the\n // first turn flush re-upserts to cover that case.\n if (sessionRun) {\n await settleWithinTimeout('session.updateRun', session.updateRun(sessionRun), hookTimeoutMs)\n .catch((err) => {\n if (process.env.ZIDANE_DEBUG)\n console.error('[zidane] session.updateRun at run start failed; continuing:', err)\n })\n }\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 ...(runProvider.name ? { providerName: runProvider.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 (shared seam — see `ensureExecutionHandle`).\n // Bounded on the first run: docker / sandbox / e2b / daytona contexts do\n // daemon + network round-trips here with no inherent timeout, so a hung\n // daemon would leave `agent.run()` pending forever (the provider\n // watchdog hasn't started yet). The process context spawns\n // synchronously, so the generous default never bites it.\n await ensureExecutionHandle(resolveTimeoutMs(resolvedBehavior.spawnTimeoutMs, DEFAULT_SPAWN_TIMEOUT_MS, resolvedBehavior.maxWallMs))\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 await applySkillPolicies({\n policies: skillPolicies,\n skills: resolvedSkills ?? [],\n options,\n activate: async (skill) => {\n if (skillActivationState.activate(skill, 'explicit') === 'ok')\n await hooks.callHook('skills:activate', { skill, via: 'explicit' })\n },\n })\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 await rehydrateActiveSkillsFromTurns(session, resolvedSkills, skillActivationState, hooks)\n }\n\n const thinking = options.thinking ?? 'off'\n const model = targets[0].model\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 ((runProvider.meta as { clearsContextServerSide?: boolean } | undefined)?.clearsContextServerSide)\n resolvedBehavior.dedupReads = false\n const { maxConcurrentTools, toolBatchExecutor, toolBatchTimeoutMs, shouldRethrowToolError, maxTurns, maxTurnsWarning, maxTurnsWarningMessage, maxCostUsd, maxTotalTokens, maxWallMs, providerStreamStartTimeoutMs, providerStreamTimeoutMs, maxTokens, retry, thinkingBudget, taskBudget, thinkingOverrunPolicy, modelOptions: behaviorModelOptions, schema, cache, toolOutputBudget, toolOutputBudgetExcludeTools, compactStrategy, compactThreshold, compactKeepTurns, thinkingDecay, writeFileExistingPolicy, textToolCallRescue, unknownToolRecovery, progressNudges, dedupTools, toolBudgets, repeatGuard, elideStaleReads, mediaKeepTurns, autoCompact, toolDisclosure, mcpToolNameSeparator, toolSearch, surfaceMcpInstructions, persistThreshold, persistExcludeTools, persistDir, persistMaxBytes, strictToolPairing, maxPairingRepairsPerTurn, maxConsecutivePauseTurns, resumeOnMaxTokens, 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 const transformedToolCatalog = applyToolCatalogTransforms(\n mergedWithSkills,\n toolCatalogTransforms,\n 'run',\n mcpToolNames,\n )\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(transformedToolCatalog)) {\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 = options.tools === undefined\n && (toolDisclosure === 'lazy' || 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 // Cache keyed per provider: with a fallback chain the loop can reformat\n // the same spec list through a different provider's `formatTools`\n // mid-run, and each provider's wire shape is distinct.\n const formattedToolsCache = new Map<Provider, { tailLen: number, value: unknown[] }>()\n function buildFormattedTools(forProvider: Provider = runProvider): unknown[] {\n const cached = formattedToolsCache.get(forProvider)\n if (cached && cached.tailLen === dynamicUnlockOrder.length)\n return cached.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 ? forProvider.formatTools(specs) : []\n formattedToolsCache.set(forProvider, { tailLen: dynamicUnlockOrder.length, value })\n return value\n }\n const formattedTools = buildFormattedTools()\n\n function advertisedToolNames(): string[] {\n const names: string[] = []\n for (const t of Object.values(tools)) {\n if (initialUnlocked.has(t.spec.name))\n names.push(toWireName(t.spec.name, aliasMaps))\n }\n for (const canonical of dynamicUnlockOrder) {\n if (tools[canonical])\n names.push(toWireName(canonical, aliasMaps))\n }\n return names\n }\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(runProvider, 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 reupsertRunRecordOnce()\n await hooks.callHook('session:turns', { sessionId: session.id, turns: newTurns, count: turns.length })\n }\n\n // One-shot follow-up to the run-start record write. For a FRESH\n // session the start write no-ops on stores that refuse unknown\n // session ids (e.g. sqlite's updateRun won't auto-create a header —\n // deletion-race safety); the first `appendTurns` above creates the\n // header, so one re-upsert here makes the `'running'` record\n // durable for run #1 too. Best-effort: crash durability must never\n // fail the turn that triggered it.\n let runRecordReupserted = false\n async function reupsertRunRecordOnce() {\n if (runRecordReupserted || !session || !sessionRun)\n return\n runRecordReupserted = true\n await settleWithinTimeout('session.updateRun', session.updateRun(sessionRun), hookTimeoutMs)\n .catch((err) => {\n if (process.env.ZIDANE_DEBUG)\n console.error('[zidane] session.updateRun re-upsert failed; continuing:', err)\n })\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, runProvider, 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 reupsertRunRecordOnce()\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 const getExecutionHandle = () => executionHandle ?? undefined\n\n perRunUnregisters.push(installWriteFileExistingPolicy(\n hooks,\n writeFileExistingPolicy,\n {\n execution: executionContext,\n getHandle: getExecutionHandle,\n aliasMaps,\n },\n ))\n perRunUnregisters.push(installUnknownToolRecovery(\n hooks,\n unknownToolRecovery,\n {\n advertisedToolNames,\n enqueueNudge: message => nonInterruptingNudgeQueue.push(message),\n },\n ))\n\n // Per-tool soft call budgets. Installed BEFORE repeat-guard + dedup so\n // a budget block wins over a dedup hit — otherwise a model spamming a\n // dedup-eligible tool would bypass the cap entirely (every dedup hit\n // short-circuits budgets that fire later in the chain). Order:\n // allowed-tools → write policy → budgets → repeat-guard → dedup → lazy\n // disclosure. The middleware owns its own per-tool approval counter so\n // within-batch ordering is correct in parallel mode.\n const uninstallToolBudgets = installToolBudgetsGate(\n hooks,\n () => toolBudgets,\n msg => nonInterruptingNudgeQueue.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 // Lazy-disclosure unlock pre-hydration — MUST register before\n // `installLazyDisclosureGate` so a within-run `tool_search` whose live\n // `unlocked.add` was lost to durable replay is rebuilt from the transcript\n // before the hard gate decides to block (see the function doc).\n const uninstallLazyDisclosureHydration = installLazyDisclosureHydrationGate(\n hooks,\n {\n enabled: shouldInjectToolSearch && disclosure.lazyEntries.length > 0,\n session: session ?? undefined,\n lazyCanonicalNames: disclosure.lazyCanonicalNames,\n lazyEntries: disclosure.lazyEntries,\n unlocked,\n recordUnlock: recordDynamicUnlock,\n defaultLimit: toolSearch?.limit,\n },\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 // Read-before-edit pre-hydration gate — keeps the `requireReadBeforeEdit`\n // verdict correct under durable/replay runtimes regardless of how the\n // consumer wraps tools (see `installReadBeforeEditHydrationGate`).\n const uninstallReadBeforeEditHydration = installReadBeforeEditHydrationGate(\n hooks,\n resolvedBehavior.requireReadBeforeEdit === true,\n {\n session: session ?? undefined,\n getHandle: () => executionHandle,\n execution: executionContext,\n aliasMaps,\n defaultLineNumbers: resolvedBehavior.readLineNumbers ?? true,\n },\n )\n\n // Skill-activation pre-hydration gate — keeps `skills_read` /\n // `skills_run_script` from journaling a spurious \"not active\" refusal when\n // a same-run `skills_use` was lost to durable replay (see the function doc).\n const uninstallSkillsActivationHydration = installSkillsActivationHydrationGate(\n hooks,\n {\n session: session ?? undefined,\n getResolvedSkills: () => resolvedSkills,\n state: skillActivationState,\n },\n )\n\n perRunUnregisters.push(installThinkingOverrunPolicy(hooks, thinkingOverrunPolicy, {\n baseBudget: thinkingBudget,\n enqueueNudge: message => nonInterruptingNudgeQueue.push(message),\n }))\n perRunUnregisters.push(installTextToolCallRescue(hooks, textToolCallRescue, {\n enqueueSteer: message => steeringQueue.push(message),\n }))\n perRunUnregisters.push(installProgressNudges(hooks, progressNudges, {\n enqueueNudge: message => nonInterruptingNudgeQueue.push(message),\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 runProvider.meta.contextWindow === 'number'\n ? runProvider.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: runProvider,\n targets,\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 // Always passed (not just for lazy disclosure): a model fallback\n // needs it to reformat the tool list for the new provider's wire\n // shape. Per-provider+tail caching makes the per-turn call cheap.\n rebuildFormattedTools: buildFormattedTools,\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 nonInterruptingNudgeQueue,\n followUpQueue,\n turns,\n runId,\n generateTurnId: mintTurnId,\n clock,\n maxTurns,\n ...(maxTurnsWarning !== undefined ? { maxTurnsWarning } : {}),\n ...(maxTurnsWarningMessage !== undefined ? { maxTurnsWarningMessage } : {}),\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 ...(taskBudget !== undefined ? { taskBudget } : {}),\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 ...(resumeOnMaxTokens !== undefined ? { resumeOnMaxTokens } : {}),\n providerName: runProvider.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 uninstallLazyDisclosureHydration()\n uninstallLazyDisclosureGate()\n uninstallReadBeforeEditHydration()\n uninstallSkillsActivationHydration()\n\n unregisterSpawnHook()\n unregisterTurnSync?.()\n unregisterToolResultsSync?.()\n for (const unregister of perRunUnregisters)\n unregister()\n steeringQueue.length = 0\n nonInterruptingNudgeQueue.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 nonInterruptingNudgeQueue.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 /**\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 destroyGeneration++\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 async function extensionContextSections(): Promise<ContextExtensionSection[]> {\n if (!contextBreakdownSections?.length)\n return []\n const turns = cloneTurnsForContextBreakdown(conversationTurns)\n const out: ContextExtensionSection[] = []\n for (const contributor of contextBreakdownSections) {\n try {\n const result = await contributor({ turns })\n const sections = Array.isArray(result) ? result : result ? [result] : []\n for (const section of sections) {\n if (isContextExtensionSection(section) && section.rows.length > 0)\n out.push(section)\n }\n }\n catch (err) {\n // Context breakdown is observational; a broken extension section\n // should not make the whole panel unavailable.\n warnContextContributorFailureOnce(contributor, err)\n }\n }\n out.push(...toolResultMetadataSections(toolResultMetadata, turns))\n return out\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 const preRunTools = applyToolCatalogTransforms(baseTools, toolCatalogTransforms, 'pre-run', mcpNames)\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(preRunTools)) {\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 the context breakdown is ready 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 const extensionSections = await extensionContextSections()\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 ...(extensionSections.length ? { extensionSections } : {}),\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 ensureExecution: async () =>\n ensureExecutionHandle(resolveTimeoutMs(agentBehavior?.spawnTimeoutMs, DEFAULT_SPAWN_TIMEOUT_MS, agentBehavior?.maxWallMs)),\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"],"mappings":"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,IAAI,MAAM,IAAI,MAAM,MAAM,OAAO;QAE3C,KAAK,MAAM,SAAS,eAAe,MAAM,SAAS,WAAW,OAAO,MAAM,MAAM,SAAS,YAAY,MAAM,MAAM,KAAK,SAAS,GAAG;IACrI,MAAM,OAAOC,QAAY,MAAM,MAAM,IAAI;IACzC,YAAY,IAAI,MAAM,KAAK,IAAI,YAAY,IAAI,IAAI,KAAK,GAAG,KAAK,SAAS,CAAC;GAC5E;GACA;EACF;EACA,IAAI,MAAM,SAAS,iBAAiB,CAAC,MAAM,SAAS;GAClD,MAAM,eAAe,aAAa,IAAI,MAAM,MAAM;GAClD,IAAI,iBAAiB,KAAA;SACN,OAAO,MAAM,WAAW,WAAW,MAAM,SAAS,iBAAiB,MAAM,MAAM,EAAA,CACnF,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,CAAC,CAAC,KAAK,IAAI;AACb;AAqGA,MAAM,iCAAsC,IAAI,IAAI;CAlGlD;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;CA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"}
|