zidane 5.8.0 → 5.8.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (56) hide show
  1. package/dist/{agent-DYghZGC8.d.ts → agent-acjWZ_4E.d.ts} +12 -2
  2. package/dist/{agent-DYghZGC8.d.ts.map → agent-acjWZ_4E.d.ts.map} +1 -1
  3. package/dist/chat/pure.d.ts +3 -3
  4. package/dist/chat.d.ts +6 -6
  5. package/dist/chat.js +2 -2
  6. package/dist/{headless-DnjYDckH.js → headless-CJFFU6DI.js} +3 -3
  7. package/dist/{headless-DnjYDckH.js.map → headless-CJFFU6DI.js.map} +1 -1
  8. package/dist/headless.d.ts +1 -1
  9. package/dist/headless.js +1 -1
  10. package/dist/{index-CVzpMtdq.d.ts → index-Cgr_mbMJ.d.ts} +2 -2
  11. package/dist/{index-CVzpMtdq.d.ts.map → index-Cgr_mbMJ.d.ts.map} +1 -1
  12. package/dist/{index-B2cMuK6S.d.ts → index-zy4OtFSv.d.ts} +2 -2
  13. package/dist/{index-B2cMuK6S.d.ts.map → index-zy4OtFSv.d.ts.map} +1 -1
  14. package/dist/index.d.ts +3 -3
  15. package/dist/index.js +5 -296
  16. package/dist/index.js.map +1 -1
  17. package/dist/{login-D-3A5CK7.js → login-BjY_sBdn.js} +301 -4
  18. package/dist/login-BjY_sBdn.js.map +1 -0
  19. package/dist/{mcp-Kqzz-Rs_.js → mcp-Br3b1Xm3.js} +50 -8
  20. package/dist/mcp-Br3b1Xm3.js.map +1 -0
  21. package/dist/mcp.d.ts +2 -2
  22. package/dist/mcp.js +2 -2
  23. package/dist/{presets-B1gWui0v.js → presets-BmsTNxjR.js} +2 -2
  24. package/dist/{presets-B1gWui0v.js.map → presets-BmsTNxjR.js.map} +1 -1
  25. package/dist/presets.d.ts +2 -2
  26. package/dist/presets.js +1 -1
  27. package/dist/providers.d.ts +1 -1
  28. package/dist/restate.d.ts +1 -1
  29. package/dist/session/sqlite.d.ts +1 -1
  30. package/dist/session.d.ts +1 -1
  31. package/dist/skills.d.ts +2 -2
  32. package/dist/{tool-formatters-0FEGRd6w.d.ts → tool-formatters-B5UNV_sy.d.ts} +2 -2
  33. package/dist/{tool-formatters-0FEGRd6w.d.ts.map → tool-formatters-B5UNV_sy.d.ts.map} +1 -1
  34. package/dist/tools/fetch-url.d.ts +1 -1
  35. package/dist/tools/web-search.d.ts +1 -1
  36. package/dist/{tools-BmPeBGU1.js → tools-BzQtic6M.js} +16 -3
  37. package/dist/tools-BzQtic6M.js.map +1 -0
  38. package/dist/tools.d.ts +2 -2
  39. package/dist/tools.js +1 -1
  40. package/dist/{transcript-anchors-Bp5bV0Bv.js → transcript-anchors-C571npbs.js} +18 -5
  41. package/dist/transcript-anchors-C571npbs.js.map +1 -0
  42. package/dist/{transcript-anchors-D2erm5iS.d.ts → transcript-anchors-Dtz6U4GW.d.ts} +5 -5
  43. package/dist/{transcript-anchors-D2erm5iS.d.ts.map → transcript-anchors-Dtz6U4GW.d.ts.map} +1 -1
  44. package/dist/tui.d.ts +14 -4
  45. package/dist/tui.d.ts.map +1 -1
  46. package/dist/tui.js +282 -76
  47. package/dist/tui.js.map +1 -1
  48. package/dist/{turn-operations-CeUrtsaM.d.ts → turn-operations-CqJAEzoe.d.ts} +3 -3
  49. package/dist/{turn-operations-CeUrtsaM.d.ts.map → turn-operations-CqJAEzoe.d.ts.map} +1 -1
  50. package/dist/types.d.ts +2 -2
  51. package/docs/ARCHITECTURE.md +2 -1
  52. package/package.json +1 -1
  53. package/dist/login-D-3A5CK7.js.map +0 -1
  54. package/dist/mcp-Kqzz-Rs_.js.map +0 -1
  55. package/dist/tools-BmPeBGU1.js.map +0 -1
  56. package/dist/transcript-anchors-Bp5bV0Bv.js.map +0 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tools-BzQtic6M.js","names":["CLIENT_ID","AUTHORIZE_URL","TOKEN_URL","CALLBACK_PORT","CALLBACK_PATH","PROVIDER_NAME","parseAuthorizationInput","exchangeAuthorizationCode","DEFAULT_LIMIT","builtinShell","pathResolve","nodeGlob","glob","nodeGlob","fsStat","readFile","writeFile"],"sources":["../src/aliasing.ts","../src/chat/oauth-page/server.ts","../src/chat/oauth-page/anthropic.ts","../src/chat/oauth-page/openai-codex.ts","../src/chat/oauth-page/render.ts","../src/chat/oauth-page/index.ts","../src/chat/providers.ts","../src/tools/read-state.ts","../src/dedup-tools.ts","../src/loop-persistence.ts","../src/tools/validation.ts","../src/loop.ts","../src/prompt.ts","../src/repeat-guard.ts","../src/tool-budgets.ts","../src/tools/shell-semantics.ts","../src/tools/shell.ts","../src/tools/binary-detect.ts","../src/tools/skills-read.ts","../src/tools/shell-quote.ts","../src/tools/skills-run-script.ts","../src/tools/skills-use.ts","../src/tools/tool-search.ts","../src/agent.ts","../src/tools/path-suggest.ts","../src/tools/edit.ts","../src/tools/glob.ts","../src/tools/grep.ts","../src/tools/interaction.ts","../src/tools/list-files.ts","../src/tools/multi-edit.ts","../src/tools/binary-read.ts","../src/tools/read-file.ts","../src/tools/shell-kill.ts","../src/tools/spawn.ts","../src/tools/write-file.ts"],"sourcesContent":["/**\n * Tool-name aliasing helpers.\n *\n * Aliasing is applied at the LLM boundary only: the provider sees the aliased\n * name, but hooks and persisted turns use canonical names. See `AgentOptions.toolAliases`.\n */\n\nimport type { SessionContentBlock, SessionMessage } from './types'\n\n/** Forward + reverse lookup between canonical and aliased tool names */\nexport interface AliasMaps {\n /** canonical → alias (only entries where alias !== canonical) */\n aliasByCanonical: Map<string, string>\n /** alias → canonical (only entries where alias !== canonical) */\n canonicalByAlias: Map<string, string>\n}\n\n/**\n * Build alias lookup maps from a `toolAliases` record.\n *\n * Validates:\n * - No two canonical names map to the same alias (collision).\n * - No alias collides with another canonical tool name (would shadow).\n *\n * Silently ignores alias entries whose canonical name isn't in `canonicalNames` —\n * preset/agent authors can declare aliases for tools that may be added later via MCP.\n *\n * @param aliases - The `toolAliases` map from the agent options.\n * @param canonicalNames - All tool canonical names currently in scope (agent + MCP).\n */\nexport function buildAliasMaps(\n aliases: Record<string, string> | undefined,\n canonicalNames: Iterable<string>,\n): AliasMaps {\n const aliasByCanonical = new Map<string, string>()\n const canonicalByAlias = new Map<string, string>()\n\n if (!aliases) {\n return { aliasByCanonical, canonicalByAlias }\n }\n\n const canonicalSet = new Set(canonicalNames)\n\n // A canonical name is \"genuinely remapped\" when it has an alias entry that is\n // both non-empty and different from itself. Identity aliases or missing keys\n // mean the tool still answers to its canonical name on the wire.\n function isRemappedAway(canonical: string): boolean {\n const mapped = aliases![canonical]\n return typeof mapped === 'string' && mapped.length > 0 && mapped !== canonical\n }\n\n for (const [canonical, alias] of Object.entries(aliases)) {\n if (typeof alias !== 'string' || alias.length === 0)\n throw new Error(`Tool alias for \"${canonical}\" must be a non-empty string`)\n\n if (alias === canonical)\n continue\n\n if (!canonicalSet.has(canonical))\n continue\n\n // When the alias matches an existing canonical tool name, only allow it if\n // that other canonical is itself being remapped away (i.e. a name swap).\n // Otherwise the wire name would shadow a real tool.\n if (canonicalSet.has(alias) && !isRemappedAway(alias)) {\n throw new Error(`Tool alias \"${canonical}\" -> \"${alias}\" collides with an existing canonical tool name`)\n }\n\n const existingCanonical = canonicalByAlias.get(alias)\n if (existingCanonical && existingCanonical !== canonical) {\n throw new Error(\n `Tool alias collision: both \"${existingCanonical}\" and \"${canonical}\" map to alias \"${alias}\"`,\n )\n }\n\n aliasByCanonical.set(canonical, alias)\n canonicalByAlias.set(alias, canonical)\n }\n\n return { aliasByCanonical, canonicalByAlias }\n}\n\n/** Return the alias for a canonical name, falling back to the canonical name itself. */\nexport function toWireName(canonical: string, maps: AliasMaps): string {\n return maps.aliasByCanonical.get(canonical) ?? canonical\n}\n\n/** Return the canonical name for a wire name, falling back to the wire name itself. */\nexport function toCanonicalName(wire: string, maps: AliasMaps): string {\n return maps.canonicalByAlias.get(wire) ?? wire\n}\n\n/**\n * Augment {@link AliasMaps} with INBOUND-ONLY aliases for the Claude Code\n * `mcp__server__tool` double-underscore naming convention. For every MCP\n * tool registered with the canonical `mcp_<server>_<tool>` form, add a\n * `canonicalByAlias` entry mapping the double-underscore variant to the\n * same canonical name. Outbound (`aliasByCanonical`) is left untouched —\n * the model still sees the single-underscore canonical on the wire, so\n * the tools-array byte count (and the provider prompt cache) is\n * unaffected.\n *\n * Why: SDK consumers and tool descriptions across the ecosystem\n * inconsistently reference one form or the other (Anthropic's docs use\n * double, zidane uses single). A Claude-Code-trained model emitting\n * `mcp__supabase__apply_migration` against a server zidane registered as\n * `mcp_supabase_apply_migration` would otherwise trip the `tool:unknown`\n * path and waste a turn on the correction.\n *\n * Idempotent: a canonical that already has a double-underscore alias\n * mapped (host's explicit `toolAliases` got there first) is left alone.\n * Tools whose canonical name DOESN'T start with `mcp_` (or that contain\n * no inner separator) are skipped — we don't want to invent aliases for\n * non-MCP tools or malformed names.\n *\n * Mutates `maps.canonicalByAlias` in place and returns the same object\n * for chaining.\n */\nexport function augmentMcpDoubleUnderscoreAliases(\n maps: AliasMaps,\n canonicalNames: Iterable<string>,\n): AliasMaps {\n for (const canonical of canonicalNames) {\n if (!canonical.startsWith('mcp_'))\n continue\n // Strip leading `mcp_`, then require AT LEAST one more `_` so the\n // remainder is `<server>_<tool>` (a degenerate `mcp_foo` with no\n // tool segment has nothing meaningful to double-underscore).\n const tail = canonical.slice(4)\n const sep = tail.indexOf('_')\n if (sep <= 0 || sep >= tail.length - 1)\n continue\n const server = tail.slice(0, sep)\n const tool = tail.slice(sep + 1)\n const doubleForm = `mcp__${server}__${tool}`\n if (doubleForm === canonical)\n continue // shouldn't happen but defensive\n if (maps.canonicalByAlias.has(doubleForm))\n continue // host alias or earlier pass got there first\n maps.canonicalByAlias.set(doubleForm, canonical)\n }\n return maps\n}\n\n/**\n * Rewrite `tool_call` block names in a content array from canonical → wire for outbound\n * messages sent to the provider. Mutation is non-destructive (returns a new array).\n */\nexport function rewriteContentToWire(\n content: SessionContentBlock[],\n maps: AliasMaps,\n): SessionContentBlock[] {\n if (maps.aliasByCanonical.size === 0)\n return content\n return content.map((block) => {\n if (block.type !== 'tool_call')\n return block\n const wire = maps.aliasByCanonical.get(block.name)\n if (!wire || wire === block.name)\n return block\n return { ...block, name: wire }\n })\n}\n\n/**\n * Rewrite `tool_call` block names in a content array from wire → canonical for inbound\n * messages received from the provider. Non-destructive.\n */\nexport function rewriteContentToCanonical(\n content: SessionContentBlock[],\n maps: AliasMaps,\n): SessionContentBlock[] {\n if (maps.canonicalByAlias.size === 0)\n return content\n return content.map((block) => {\n if (block.type !== 'tool_call')\n return block\n const canonical = maps.canonicalByAlias.get(block.name)\n if (!canonical || canonical === block.name)\n return block\n return { ...block, name: canonical }\n })\n}\n\n/**\n * Rewrite every `SessionMessage.content` in an array from canonical → wire.\n * Returns a new array of new message objects — input messages are not mutated.\n * When the alias map is empty, returns the input by reference (no allocation).\n */\nexport function rewriteMessagesToWire(\n messages: SessionMessage[],\n maps: AliasMaps,\n): SessionMessage[] {\n if (maps.aliasByCanonical.size === 0)\n return messages\n return messages.map(msg => ({ ...msg, content: rewriteContentToWire(msg.content, maps) }))\n}\n","/**\n * Local HTTP callback server primitive shared by the Anthropic + Codex\n * login flows. Owns three concerns:\n *\n * 1. Bind a loopback listener (configurable port + host via\n * `PI_OAUTH_CALLBACK_HOST`, matching pi-ai's semantics).\n * 2. Parse the redirect query string into `{ code, state }` or surface\n * a structured error.\n * 3. Render the result page through the caller-supplied renderer.\n *\n * Each provider clones the lifecycle wrapper (`waitForCode` / `cancelWait`\n * / `close`) so the higher-level login code reads the same on either\n * side. The only thing the server itself decides is the HTML.\n */\n\nimport type { IncomingMessage, Server, ServerResponse } from 'node:http'\nimport type { OAuthCallbackPageRenderer } from './render'\nimport { createServer } from 'node:http'\n\nconst CALLBACK_HOST = process.env.PI_OAUTH_CALLBACK_HOST || '127.0.0.1'\n\nexport interface CallbackServerOptions {\n /** Loopback port. Must match the `redirect_uri` registered with the provider. */\n port: number\n /** Path component of the redirect URI (e.g. `/callback`, `/auth/callback`). */\n path: string\n /**\n * Expected `state` value. The server rejects any callback whose `state`\n * doesn't match, exactly. For Anthropic this is the PKCE verifier; for\n * Codex it's a random 16-byte hex string.\n */\n expectedState: string\n /** Display name passed through to {@link OAuthCallbackPageRenderer}. */\n providerName: string\n /** HTML renderer for both success + error pages. */\n renderPage: OAuthCallbackPageRenderer\n /** Success-page body shown after a clean redirect. */\n successMessage: string\n /**\n * How to react when `server.listen` itself fails (typically `EADDRINUSE`\n * when another instance is mid-flow). `\"reject\"` propagates the error to\n * the awaiting `start` caller; `\"resolveWithStub\"` returns a degraded\n * handle whose `waitForCode` immediately resolves to `null`, so the\n * caller can fall back to manual paste. Anthropic uses `\"reject\"`,\n * Codex uses `\"resolveWithStub\"` to match pi-ai's per-provider behavior.\n */\n onListenError?: 'reject' | 'resolveWithStub'\n}\n\nexport interface CallbackServerHandle {\n /** Fully-qualified redirect URI to advertise to the provider. */\n redirectUri: string\n /**\n * Resolves when the redirect lands (with `{ code, state? }`) or when\n * {@link cancelWait} is invoked (with `null`). Never throws.\n */\n waitForCode: () => Promise<{ code: string, state?: string } | null>\n /** Unblock `waitForCode` early (e.g. manual paste won the race). */\n cancelWait: () => void\n /** Close the listener. Idempotent. */\n close: () => void\n}\n\ninterface PendingResolution {\n settle: (value: { code: string, state?: string } | null) => void\n}\n\nfunction buildRequestHandler(opts: CallbackServerOptions, pending: PendingResolution) {\n return (req: IncomingMessage, res: ServerResponse) => {\n try {\n const url = new URL(req.url || '', 'http://localhost')\n\n if (url.pathname !== opts.path) {\n respondError(res, 404, opts, 'Callback route not found.')\n return\n }\n\n const error = url.searchParams.get('error')\n if (error) {\n respondError(res, 400, opts, `${opts.providerName} authentication did not complete.`, `Error: ${error}`)\n return\n }\n\n const code = url.searchParams.get('code')\n const state = url.searchParams.get('state')\n\n if (!code || !state) {\n respondError(res, 400, opts, 'Missing code or state parameter.')\n return\n }\n\n if (state !== opts.expectedState) {\n respondError(res, 400, opts, 'State mismatch.')\n return\n }\n\n res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' })\n res.end(opts.renderPage({\n kind: 'success',\n provider: opts.providerName,\n message: opts.successMessage,\n }))\n pending.settle({ code, state })\n }\n catch {\n // We intentionally swallow the actual error here: surfacing a stack\n // to the user's browser would leak internals, and the login flow\n // is about to fall back to manual paste anyway.\n respondError(res, 500, opts, 'Internal error while processing the callback.')\n }\n }\n}\n\nfunction respondError(\n res: ServerResponse,\n status: number,\n opts: CallbackServerOptions,\n message: string,\n details?: string,\n) {\n res.writeHead(status, { 'Content-Type': 'text/html; charset=utf-8' })\n res.end(opts.renderPage({\n kind: 'error',\n provider: opts.providerName,\n message,\n details,\n }))\n}\n\nfunction buildStubHandle(redirectUri: string, server: Server | null): CallbackServerHandle {\n return {\n redirectUri,\n waitForCode: async () => null,\n cancelWait: () => {},\n close: () => {\n try { server?.close() }\n catch { /* already closed */ }\n },\n }\n}\n\n/**\n * Start the loopback HTTP callback server. The returned handle is the\n * caller's contract — they `await waitForCode()`, race it against manual\n * paste, then `close()` in a `finally`.\n */\nexport async function startCallbackServer(opts: CallbackServerOptions): Promise<CallbackServerHandle> {\n const onListenError = opts.onListenError ?? 'reject'\n const redirectUri = `http://localhost:${opts.port}${opts.path}`\n\n return new Promise<CallbackServerHandle>((resolve, reject) => {\n let settled = false\n const pending: PendingResolution = {\n settle: () => {},\n }\n const waitPromise = new Promise<{ code: string, state?: string } | null>((resolveWait) => {\n pending.settle = (value) => {\n if (settled)\n return\n settled = true\n resolveWait(value)\n }\n })\n\n const server = createServer(buildRequestHandler(opts, pending))\n\n server.on('error', (err) => {\n if (onListenError === 'resolveWithStub') {\n pending.settle(null)\n resolve(buildStubHandle(redirectUri, server))\n return\n }\n reject(err)\n })\n\n server.listen(opts.port, CALLBACK_HOST, () => {\n resolve({\n redirectUri,\n waitForCode: () => waitPromise,\n cancelWait: () => pending.settle(null),\n close: () => {\n try { server.close() }\n catch { /* already closed */ }\n },\n })\n })\n })\n}\n","/**\n * Anthropic OAuth flow with a customisable callback page.\n *\n * Mirrors `loginAnthropic` from `@earendil-works/pi-ai/oauth` 1:1 except:\n *\n * 1. The HTTP callback server's response HTML routes through\n * {@link OAuthCallbackPageRenderer} instead of pi-ai's baked-in page.\n * 2. The lifecycle is delegated to {@link startCallbackServer} so the\n * Codex sibling and any future loopback provider share one HTTP impl.\n *\n * Refresh + `getApiKey` delegate straight to pi-ai's exports — those don't\n * touch HTML and there's no upside to forking them.\n *\n * Endpoints / client id / port / scopes match pi-ai `0.76.0`. Bump in\n * lockstep on the next `@earendil-works/pi-ai` upgrade, or delete this\n * file once pi-ai exposes a `renderCallbackPage` callback upstream.\n */\n\nimport type {\n OAuthCredentials,\n OAuthLoginCallbacks,\n OAuthProviderInterface,\n} from '@earendil-works/pi-ai/oauth'\nimport type { OAuthCallbackPageRenderer } from './render'\nimport { refreshAnthropicToken } from '@earendil-works/pi-ai/oauth'\nimport { generatePkce } from './pkce'\nimport { startCallbackServer } from './server'\n\n// Pulled verbatim from pi-ai 0.76.0 — `dist/utils/oauth/anthropic.js`.\nconst CLIENT_ID = atob('OWQxYzI1MGEtZTYxYi00NGQ5LTg4ZWQtNTk0NGQxOTYyZjVl')\nconst AUTHORIZE_URL = 'https://claude.ai/oauth/authorize'\nconst TOKEN_URL = 'https://platform.claude.com/v1/oauth/token'\nconst CALLBACK_PORT = 53692\nconst CALLBACK_PATH = '/callback'\nconst SCOPES = 'org:create_api_key user:profile user:inference user:sessions:claude_code user:mcp_servers user:file_upload'\nconst PROVIDER_NAME = 'Anthropic'\n\n/**\n * Parse what the user pasted into the manual-code prompt. Accepts:\n * - the bare authorization code\n * - the full `redirect_uri?code=...&state=...` URL\n * - the `code#state` shorthand Anthropic surfaces on the page\n * - a raw query string with `code=...&state=...`\n *\n * Matches pi-ai's parse table so an existing user's muscle memory still works.\n */\nfunction parseAuthorizationInput(input: string): { code?: string, state?: string } {\n const value = input.trim()\n if (!value)\n return {}\n\n try {\n const url = new URL(value)\n return {\n code: url.searchParams.get('code') ?? undefined,\n state: url.searchParams.get('state') ?? undefined,\n }\n }\n catch {\n // fall through to non-URL parsing below\n }\n\n if (value.includes('#')) {\n const [code, state] = value.split('#', 2)\n return { code, state }\n }\n if (value.includes('code=')) {\n const params = new URLSearchParams(value)\n return {\n code: params.get('code') ?? undefined,\n state: params.get('state') ?? undefined,\n }\n }\n return { code: value }\n}\n\nasync function postJson(url: string, body: Record<string, unknown>): Promise<string> {\n const response = await fetch(url, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'Accept': 'application/json',\n },\n body: JSON.stringify(body),\n signal: AbortSignal.timeout(30_000),\n })\n const responseBody = await response.text()\n if (!response.ok)\n throw new Error(`HTTP request failed. status=${response.status}; url=${url}; body=${responseBody}`)\n return responseBody\n}\n\nasync function exchangeAuthorizationCode(\n code: string,\n state: string,\n verifier: string,\n redirectUri: string,\n): Promise<OAuthCredentials> {\n const responseBody = await postJson(TOKEN_URL, {\n grant_type: 'authorization_code',\n client_id: CLIENT_ID,\n code,\n state,\n redirect_uri: redirectUri,\n code_verifier: verifier,\n })\n\n const tokenData = JSON.parse(responseBody) as {\n refresh_token: string\n access_token: string\n expires_in: number\n }\n\n return {\n refresh: tokenData.refresh_token,\n access: tokenData.access_token,\n // -5min so a refresh fires before the upstream expiry; matches pi-ai.\n expires: Date.now() + tokenData.expires_in * 1000 - 5 * 60 * 1000,\n }\n}\n\nexport interface LoginAnthropicWithPageOptions extends Pick<\n OAuthLoginCallbacks,\n 'onAuth' | 'onPrompt' | 'onProgress' | 'onManualCodeInput'\n> {\n renderPage: OAuthCallbackPageRenderer\n}\n\nexport async function loginAnthropicWithCustomPage(\n options: LoginAnthropicWithPageOptions,\n): Promise<OAuthCredentials> {\n const { verifier, challenge } = await generatePkce()\n const server = await startCallbackServer({\n port: CALLBACK_PORT,\n path: CALLBACK_PATH,\n expectedState: verifier,\n providerName: PROVIDER_NAME,\n renderPage: options.renderPage,\n successMessage: 'Anthropic authentication completed. You can close this window.',\n onListenError: 'reject',\n })\n\n let code: string | undefined\n let state: string | undefined\n\n try {\n const authParams = new URLSearchParams({\n code: 'true',\n client_id: CLIENT_ID,\n response_type: 'code',\n redirect_uri: server.redirectUri,\n scope: SCOPES,\n code_challenge: challenge,\n code_challenge_method: 'S256',\n state: verifier,\n })\n\n options.onAuth({\n url: `${AUTHORIZE_URL}?${authParams.toString()}`,\n instructions: 'Complete login in your browser. If the browser is on another machine, paste the final redirect URL here.',\n })\n\n if (options.onManualCodeInput) {\n let manualInput: string | undefined\n let manualError: Error | undefined\n const manualPromise = options\n .onManualCodeInput()\n .then((input) => {\n manualInput = input\n server.cancelWait()\n })\n .catch((err) => {\n manualError = err instanceof Error ? err : new Error(String(err))\n server.cancelWait()\n })\n\n const result = await server.waitForCode()\n if (manualError)\n throw manualError\n\n if (result?.code) {\n code = result.code\n state = result.state\n }\n else if (manualInput) {\n const parsed = parseAuthorizationInput(manualInput)\n if (parsed.state && parsed.state !== verifier)\n throw new Error('OAuth state mismatch')\n code = parsed.code\n state = parsed.state ?? verifier\n }\n\n if (!code) {\n await manualPromise\n if (manualError)\n throw manualError\n if (manualInput) {\n const parsed = parseAuthorizationInput(manualInput)\n if (parsed.state && parsed.state !== verifier)\n throw new Error('OAuth state mismatch')\n code = parsed.code\n state = parsed.state ?? verifier\n }\n }\n }\n else {\n const result = await server.waitForCode()\n if (result?.code) {\n code = result.code\n state = result.state\n }\n }\n\n if (!code) {\n const input = await options.onPrompt({\n message: 'Paste the authorization code or full redirect URL:',\n placeholder: server.redirectUri,\n })\n const parsed = parseAuthorizationInput(input)\n if (parsed.state && parsed.state !== verifier)\n throw new Error('OAuth state mismatch')\n code = parsed.code\n state = parsed.state ?? verifier\n }\n\n if (!code)\n throw new Error('Missing authorization code')\n if (!state)\n throw new Error('Missing OAuth state')\n\n options.onProgress?.('Exchanging authorization code for tokens...')\n return await exchangeAuthorizationCode(code, state, verifier, server.redirectUri)\n }\n finally {\n server.close()\n }\n}\n\n/**\n * Build an `OAuthProviderInterface` that behaves identically to pi-ai's\n * `anthropicOAuthProvider` except for the callback page HTML. Drop this\n * onto `ProviderDescriptor.oauthProvider` to override.\n */\nexport function createAnthropicOAuthProviderWithCustomPage(\n renderPage: OAuthCallbackPageRenderer,\n): OAuthProviderInterface {\n return {\n id: 'anthropic',\n name: 'Anthropic (Claude Pro/Max)',\n usesCallbackServer: true,\n async login(callbacks: OAuthLoginCallbacks) {\n return loginAnthropicWithCustomPage({\n renderPage,\n onAuth: callbacks.onAuth,\n onPrompt: callbacks.onPrompt,\n onProgress: callbacks.onProgress,\n onManualCodeInput: callbacks.onManualCodeInput,\n })\n },\n async refreshToken(credentials: OAuthCredentials) {\n return refreshAnthropicToken(credentials.refresh)\n },\n getApiKey(credentials: OAuthCredentials) {\n return credentials.access\n },\n }\n}\n","/**\n * OpenAI Codex (ChatGPT Plus/Pro) OAuth flow with a customisable callback\n * page. 1:1 mirror of pi-ai `0.76.0`'s `loginOpenAICodex` except the HTTP\n * callback server routes through {@link OAuthCallbackPageRenderer}.\n *\n * Refresh delegates to pi-ai's exported `refreshOpenAICodexToken` —\n * Codex's refresh path involves JWT decoding for `accountId`, and we don't\n * want to duplicate it. The HTML override is purely about the landing page.\n */\n\nimport type {\n OAuthCredentials,\n OAuthLoginCallbacks,\n OAuthProviderInterface,\n} from '@earendil-works/pi-ai/oauth'\nimport type { OAuthCallbackPageRenderer } from './render'\nimport { randomBytes } from 'node:crypto'\nimport { refreshOpenAICodexToken } from '@earendil-works/pi-ai/oauth'\nimport { generatePkce } from './pkce'\nimport { startCallbackServer } from './server'\n\n// Pulled verbatim from pi-ai 0.76.0 — `dist/utils/oauth/openai-codex.js`.\nconst CLIENT_ID = 'app_EMoamEEZ73f0CkXaXp7hrann'\nconst AUTHORIZE_URL = 'https://auth.openai.com/oauth/authorize'\nconst TOKEN_URL = 'https://auth.openai.com/oauth/token'\nconst CALLBACK_PORT = 1455\nconst CALLBACK_PATH = '/auth/callback'\nconst SCOPE = 'openid profile email offline_access'\nconst JWT_CLAIM_PATH = 'https://api.openai.com/auth'\nconst PROVIDER_NAME = 'OpenAI'\n\nfunction createState(): string {\n return randomBytes(16).toString('hex')\n}\n\nfunction parseAuthorizationInput(input: string): { code?: string, state?: string } {\n const value = input.trim()\n if (!value)\n return {}\n\n try {\n const url = new URL(value)\n return {\n code: url.searchParams.get('code') ?? undefined,\n state: url.searchParams.get('state') ?? undefined,\n }\n }\n catch {\n // fall through\n }\n\n if (value.includes('#')) {\n const [code, state] = value.split('#', 2)\n return { code, state }\n }\n if (value.includes('code=')) {\n const params = new URLSearchParams(value)\n return {\n code: params.get('code') ?? undefined,\n state: params.get('state') ?? undefined,\n }\n }\n return { code: value }\n}\n\ninterface JwtPayload {\n [key: string]: unknown\n [JWT_CLAIM_PATH]?: { chatgpt_account_id?: string }\n}\n\nfunction decodeJwt(token: string): JwtPayload | null {\n try {\n const parts = token.split('.')\n if (parts.length !== 3)\n return null\n const payload = parts[1] ?? ''\n const decoded = atob(payload)\n return JSON.parse(decoded) as JwtPayload\n }\n catch {\n return null\n }\n}\n\nfunction getAccountId(accessToken: string): string | null {\n const payload = decodeJwt(accessToken)\n const claim = payload?.[JWT_CLAIM_PATH] as { chatgpt_account_id?: string } | undefined\n const accountId = claim?.chatgpt_account_id\n return typeof accountId === 'string' && accountId.length > 0 ? accountId : null\n}\n\ninterface TokenExchangeSuccess {\n type: 'success'\n access: string\n refresh: string\n expires: number\n}\n\ninterface TokenExchangeFailure {\n type: 'failed'\n message: string\n}\n\nasync function exchangeAuthorizationCode(\n code: string,\n verifier: string,\n redirectUri: string,\n): Promise<TokenExchangeSuccess | TokenExchangeFailure> {\n const response = await fetch(TOKEN_URL, {\n method: 'POST',\n headers: { 'Content-Type': 'application/x-www-form-urlencoded' },\n body: new URLSearchParams({\n grant_type: 'authorization_code',\n client_id: CLIENT_ID,\n code,\n code_verifier: verifier,\n redirect_uri: redirectUri,\n }),\n })\n\n if (!response.ok) {\n const text = await response.text().catch(() => '')\n return {\n type: 'failed',\n message: `OpenAI Codex token exchange failed (${response.status}): ${text || response.statusText}`,\n }\n }\n\n const json = await response.json() as {\n access_token?: string\n refresh_token?: string\n expires_in?: number\n }\n if (!json.access_token || !json.refresh_token || typeof json.expires_in !== 'number') {\n return {\n type: 'failed',\n message: `OpenAI Codex token exchange response missing fields: ${JSON.stringify(json)}`,\n }\n }\n return {\n type: 'success',\n access: json.access_token,\n refresh: json.refresh_token,\n expires: Date.now() + json.expires_in * 1000,\n }\n}\n\nexport interface LoginOpenAICodexWithPageOptions extends Pick<\n OAuthLoginCallbacks,\n 'onAuth' | 'onPrompt' | 'onProgress' | 'onManualCodeInput'\n> {\n renderPage: OAuthCallbackPageRenderer\n /** OAuth `originator` parameter. Defaults to `\"pi\"` to stay observable upstream. */\n originator?: string\n}\n\nexport async function loginOpenAICodexWithCustomPage(\n options: LoginOpenAICodexWithPageOptions,\n): Promise<OAuthCredentials> {\n const { verifier, challenge } = await generatePkce()\n const state = createState()\n const originator = options.originator ?? 'pi'\n\n const server = await startCallbackServer({\n port: CALLBACK_PORT,\n path: CALLBACK_PATH,\n expectedState: state,\n providerName: PROVIDER_NAME,\n renderPage: options.renderPage,\n successMessage: 'OpenAI authentication completed. You can close this window.',\n // Matches pi-ai: Codex falls back to manual paste on port collision.\n onListenError: 'resolveWithStub',\n })\n\n const authUrl = new URL(AUTHORIZE_URL)\n authUrl.searchParams.set('response_type', 'code')\n authUrl.searchParams.set('client_id', CLIENT_ID)\n authUrl.searchParams.set('redirect_uri', server.redirectUri)\n authUrl.searchParams.set('scope', SCOPE)\n authUrl.searchParams.set('code_challenge', challenge)\n authUrl.searchParams.set('code_challenge_method', 'S256')\n authUrl.searchParams.set('state', state)\n authUrl.searchParams.set('id_token_add_organizations', 'true')\n authUrl.searchParams.set('codex_cli_simplified_flow', 'true')\n authUrl.searchParams.set('originator', originator)\n\n options.onAuth({\n url: authUrl.toString(),\n instructions: 'A browser window should open. Complete login to finish.',\n })\n\n let code: string | undefined\n\n try {\n if (options.onManualCodeInput) {\n let manualCode: string | undefined\n let manualError: Error | undefined\n const manualPromise = options\n .onManualCodeInput()\n .then((input) => {\n manualCode = input\n server.cancelWait()\n })\n .catch((err) => {\n manualError = err instanceof Error ? err : new Error(String(err))\n server.cancelWait()\n })\n\n const result = await server.waitForCode()\n if (manualError)\n throw manualError\n\n if (result?.code) {\n code = result.code\n }\n else if (manualCode) {\n const parsed = parseAuthorizationInput(manualCode)\n if (parsed.state && parsed.state !== state)\n throw new Error('State mismatch')\n code = parsed.code\n }\n\n if (!code) {\n await manualPromise\n if (manualError)\n throw manualError\n if (manualCode) {\n const parsed = parseAuthorizationInput(manualCode)\n if (parsed.state && parsed.state !== state)\n throw new Error('State mismatch')\n code = parsed.code\n }\n }\n }\n else {\n const result = await server.waitForCode()\n if (result?.code)\n code = result.code\n }\n\n if (!code) {\n const input = await options.onPrompt({\n message: 'Paste the authorization code (or full redirect URL):',\n })\n const parsed = parseAuthorizationInput(input)\n if (parsed.state && parsed.state !== state)\n throw new Error('State mismatch')\n code = parsed.code\n }\n\n if (!code)\n throw new Error('Missing authorization code')\n\n const tokenResult = await exchangeAuthorizationCode(code, verifier, server.redirectUri)\n if (tokenResult.type !== 'success')\n throw new Error(tokenResult.message)\n\n const accountId = getAccountId(tokenResult.access)\n if (!accountId)\n throw new Error('Failed to extract accountId from token')\n\n return {\n access: tokenResult.access,\n refresh: tokenResult.refresh,\n expires: tokenResult.expires,\n accountId,\n }\n }\n finally {\n server.close()\n }\n}\n\n/**\n * Build an `OAuthProviderInterface` that behaves identically to pi-ai's\n * `openaiCodexOAuthProvider` except for the callback page HTML.\n */\nexport function createOpenAICodexOAuthProviderWithCustomPage(\n renderPage: OAuthCallbackPageRenderer,\n): OAuthProviderInterface {\n return {\n id: 'openai-codex',\n name: 'ChatGPT Plus/Pro (Codex Subscription)',\n usesCallbackServer: true,\n async login(callbacks: OAuthLoginCallbacks) {\n return loginOpenAICodexWithCustomPage({\n renderPage,\n onAuth: callbacks.onAuth,\n onPrompt: callbacks.onPrompt,\n onProgress: callbacks.onProgress,\n onManualCodeInput: callbacks.onManualCodeInput,\n })\n },\n async refreshToken(credentials: OAuthCredentials) {\n return refreshOpenAICodexToken(credentials.refresh)\n },\n getApiKey(credentials: OAuthCredentials) {\n return credentials.access\n },\n }\n}\n","/**\n * HTML rendered by the local OAuth callback server when the browser hits\n * `redirect_uri`. This is the page the user sees the moment authentication\n * finishes — by default pi-ai bakes in its own dark page; this module lets\n * a host swap it for something branded without forking the package.\n *\n * Renderer is a single pure function — `(page) => string` — so a host can\n * inline a literal template, pull from a theme registry, or proxy a static\n * asset built at compile time. Anything that returns a `string`.\n */\n\n/**\n * Outcome rendered on the callback page. `kind` drives status code on the\n * underlying HTTP response (`200` for success, `4xx` for error); the rest\n * is content. `provider` is the human-readable name of the OAuth provider\n * (e.g. `\"Anthropic\"`, `\"OpenAI Codex\"`) so a renderer can swap copy or\n * logos per provider.\n */\nexport interface OAuthCallbackPage {\n kind: 'success' | 'error'\n /** Headline message — usually one sentence. Safe to include user-visible details. */\n message: string\n /** Optional secondary details (e.g. provider error code). Rendered in mono. */\n details?: string\n /** Provider display name passed through from the login function. */\n provider: string\n}\n\nexport type OAuthCallbackPageRenderer = (page: OAuthCallbackPage) => string\n\nfunction escapeHtml(value: string): string {\n return value\n .replaceAll('&', '&amp;')\n .replaceAll('<', '&lt;')\n .replaceAll('>', '&gt;')\n .replaceAll('\"', '&quot;')\n .replaceAll('\\'', '&#39;')\n}\n\n/**\n * Default zidane-themed page. Visually neutral but distinguishable from\n * pi-ai's stock page — dark background, mono headings, no logo (host can\n * pass a custom renderer to add one).\n */\nexport const renderDefaultCallbackPage: OAuthCallbackPageRenderer = (page) => {\n const title = page.kind === 'success'\n ? `Signed in to ${page.provider}`\n : `Could not sign in to ${page.provider}`\n const heading = escapeHtml(title)\n const message = escapeHtml(page.message)\n const details = page.details ? escapeHtml(page.details) : undefined\n\n return `<!doctype html>\n<html lang=\"en\">\n<head>\n <meta charset=\"utf-8\" />\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />\n <title>${heading}</title>\n <style>\n :root {\n --text: #f4f4f5;\n --text-dim: #a1a1aa;\n --accent: ${page.kind === 'success' ? '#22d3ee' : '#f87171'};\n --page-bg: #0a0a0a;\n --panel-bg: #131316;\n --border: #27272a;\n --font-sans: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n --font-mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;\n }\n * { box-sizing: border-box; }\n html { color-scheme: dark; }\n body {\n margin: 0;\n min-height: 100vh;\n display: flex;\n align-items: center;\n justify-content: center;\n padding: 24px;\n background: var(--page-bg);\n color: var(--text);\n font-family: var(--font-sans);\n }\n main {\n width: 100%;\n max-width: 520px;\n padding: 32px;\n background: var(--panel-bg);\n border: 1px solid var(--border);\n border-radius: 12px;\n }\n .label {\n font-family: var(--font-mono);\n font-size: 12px;\n letter-spacing: 0.08em;\n text-transform: uppercase;\n color: var(--accent);\n margin-bottom: 12px;\n }\n h1 {\n margin: 0 0 12px;\n font-size: 22px;\n font-weight: 600;\n letter-spacing: -0.01em;\n color: var(--text);\n }\n p {\n margin: 0;\n line-height: 1.6;\n color: var(--text-dim);\n font-size: 14px;\n }\n .details {\n margin-top: 18px;\n padding: 12px 14px;\n background: var(--page-bg);\n border: 1px solid var(--border);\n border-radius: 8px;\n font-family: var(--font-mono);\n font-size: 12px;\n color: var(--text-dim);\n white-space: pre-wrap;\n word-break: break-word;\n }\n </style>\n</head>\n<body>\n <main>\n <div class=\"label\">zidane · OAuth · ${escapeHtml(page.provider)}</div>\n <h1>${heading}</h1>\n <p>${message}</p>\n ${details ? `<div class=\"details\">${details}</div>` : ''}\n </main>\n</body>\n</html>`\n}\n\n/**\n * Tiny helper kept private to the module so renderer authors can reuse\n * the same escaping rules as the default theme. Re-exported in case a\n * host wants to embed user-supplied messages safely.\n */\nexport const escapeHtmlForCallbackPage: (value: string) => string = escapeHtml\n","/**\n * Custom OAuth callback page — drop-in replacements for pi-ai's built-in\n * Anthropic + OpenAI Codex providers that route the post-redirect HTML\n * through your own renderer.\n *\n * Why this module exists\n * ----------------------\n * `@earendil-works/pi-ai` (as of `0.76.0`) bakes its callback page into\n * `loginAnthropic` / `loginOpenAICodex` with no override. The upstream\n * fix is a tiny `renderCallbackPage` hook on `OAuthLoginCallbacks`; until\n * that lands this module forks the two flows in one self-contained place\n * — everything else (token refresh, `getApiKey`, env handling) delegates\n * back to pi-ai unchanged.\n *\n * How to use\n * ----------\n *\n * ```ts\n * import { createCustomCallbackOAuthProviders, renderDefaultCallbackPage } from './chat/oauth-page'\n * import { BUILTIN_PROVIDERS } from './chat/providers'\n *\n * const { anthropic, openaiCodex } = createCustomCallbackOAuthProviders(renderDefaultCallbackPage)\n *\n * const providers = {\n * ...BUILTIN_PROVIDERS,\n * anthropic: { ...BUILTIN_PROVIDERS.anthropic, oauthProvider: anthropic },\n * openai: { ...BUILTIN_PROVIDERS.openai, oauthProvider: openaiCodex },\n * }\n * ```\n *\n * Or override per provider with `createAnthropicOAuthProviderWithCustomPage`\n * / `createOpenAICodexOAuthProviderWithCustomPage` directly.\n *\n * Deleting this module\n * --------------------\n * When pi-ai exposes `renderCallbackPage` on `OAuthLoginCallbacks`:\n *\n * 1. Delete this folder.\n * 2. Surface the upstream callback through `OAuthFlowOptions` in\n * `src/chat/oauth.ts` (one optional pass-through field).\n * 3. Re-point any descriptors that imported from here to pi-ai's\n * `anthropicOAuthProvider` / `openaiCodexOAuthProvider` directly.\n *\n * Pinned to pi-ai `0.76.0`. Re-verify endpoints + ports on every pi-ai\n * bump (auth URLs, client IDs, callback ports are duplicated verbatim\n * here; see `anthropic.ts` and `openai-codex.ts` for the source-of-truth\n * pointers).\n */\n\nimport type { OAuthProviderInterface } from '@earendil-works/pi-ai/oauth'\nimport type { OAuthCallbackPageRenderer } from './render'\nimport { registerOAuthProvider } from '@earendil-works/pi-ai/oauth'\nimport { createAnthropicOAuthProviderWithCustomPage } from './anthropic'\nimport { createCursorOAuthProvider } from './cursor'\nimport { createOpenAICodexOAuthProviderWithCustomPage } from './openai-codex'\n\nexport type {\n LoginAnthropicWithPageOptions,\n} from './anthropic'\nexport {\n createAnthropicOAuthProviderWithCustomPage,\n loginAnthropicWithCustomPage,\n} from './anthropic'\nexport {\n createCursorOAuthProvider,\n CURSOR_OAUTH_PROVIDER_ID,\n loginCursor,\n refreshCursorToken,\n} from './cursor'\nexport type {\n LoginOpenAICodexWithPageOptions,\n} from './openai-codex'\nexport {\n createOpenAICodexOAuthProviderWithCustomPage,\n loginOpenAICodexWithCustomPage,\n} from './openai-codex'\nexport type {\n OAuthCallbackPage,\n OAuthCallbackPageRenderer,\n} from './render'\nexport {\n escapeHtmlForCallbackPage,\n renderDefaultCallbackPage,\n} from './render'\n\nexport interface CustomCallbackOAuthProviders {\n anthropic: OAuthProviderInterface\n openaiCodex: OAuthProviderInterface\n}\n\n/**\n * Bundle helper — returns both Anthropic + OpenAI Codex providers wired\n * with the same renderer. Hosts that want different renderers per provider\n * should call the individual `create…WithCustomPage` factories instead.\n */\nexport function createCustomCallbackOAuthProviders(\n renderPage: OAuthCallbackPageRenderer,\n): CustomCallbackOAuthProviders {\n return {\n anthropic: createAnthropicOAuthProviderWithCustomPage(renderPage),\n openaiCodex: createOpenAICodexOAuthProviderWithCustomPage(renderPage),\n }\n}\n\nlet cursorRegistered = false\n\n/**\n * Register Cursor with pi-ai's OAuth registry.\n *\n * Unlike Anthropic / Codex, Cursor is **not** built into pi-ai, so\n * `getOAuthApiKey('cursor', …)` (used by `resolveOAuthApiKey` for lazy\n * token refresh) would throw \"Unknown OAuth provider\" until we register it.\n * Call once at startup from both the CLI auth path and the TUI. Idempotent.\n */\nexport function registerCursorOAuthProvider(): OAuthProviderInterface {\n const provider = createCursorOAuthProvider()\n if (!cursorRegistered) {\n registerOAuthProvider(provider)\n cursorRegistered = true\n }\n return provider\n}\n","/**\n * Provider registry — the customization seam for the TUI.\n *\n * A {@link ProviderDescriptor} carries everything the TUI needs to know about\n * a provider in one place: how to instantiate it, what to show in the UI,\n * how to detect credentials, how to OAuth (if applicable), and where to look\n * up its models. Hosts can ship their own descriptors instead of the built-in\n * four — see {@link BUILTIN_PROVIDERS}.\n */\n\nimport type { OAuthProviderInterface } from '@earendil-works/pi-ai/oauth'\nimport type { Provider } from '../providers'\nimport { getModel, getModels } from '@earendil-works/pi-ai'\nimport { anthropic, cerebras, cursor, local, openai, openrouter } from '../providers'\nimport { ANTHROPIC_EXTRA_MODELS, FAST_MODE_OPTIONS } from './anthropic-models'\nimport { createCustomCallbackOAuthProviders, registerCursorOAuthProvider, renderDefaultCallbackPage } from './oauth-page'\n\n/**\n * pi-ai's stock Anthropic + Codex OAuth providers bake their own callback\n * HTML; we route both through `renderDefaultCallbackPage` so the post-redirect\n * page matches zidane's theme. The override lives in `src/chat/oauth-page/`\n * — see that folder's `index.ts` for the deletion path once pi-ai exposes\n * a `renderCallbackPage` hook upstream.\n */\nconst { anthropic: anthropicOAuthProvider, openaiCodex: openaiCodexOAuthProvider }\n = createCustomCallbackOAuthProviders(renderDefaultCallbackPage)\n\n/**\n * Cursor isn't built into pi-ai, so registering it makes lazy token refresh\n * (`getOAuthApiKey('cursor', …)` inside `resolveOAuthApiKey`) resolve to our\n * Cursor flow. The poll-based login has no callback server, so no themed page.\n */\nconst cursorOAuthProvider = registerCursorOAuthProvider()\n\n/**\n * Structural model metadata — compatible with pi-ai's `Model` interface but\n * not coupled to it, so hosts can pass either pi-ai-shaped objects or a\n * custom registry of their own.\n *\n * Deliberately a structural duplicate of pi-ai's `Model` rather than a\n * re-export: keeps the public API stable when pi-ai bumps shape, and lets\n * hosts implement their own registry without depending on pi-ai's types.\n *\n * Lives here (rather than `./config`) because `ProviderDescriptor.models`\n * needs it — pushing it to `config.tsx` created an import cycle.\n */\nexport interface ModelInfo {\n id: string\n name?: string\n contextWindow: number\n maxTokens?: number\n reasoning?: boolean\n input?: readonly ('text' | 'image')[]\n cost?: { input: number, output: number, cacheRead?: number, cacheWrite?: number }\n provider?: string\n /**\n * Model-specific toggleable knobs surfaced in the UI (e.g. Anthropic's\n * \"fast mode\"). Each option is an independent boolean the user flips on the\n * active model; enabled options are collected into `StreamOptions.modelOptions`\n * keyed by {@link ModelOption.id} and applied to the wire by the provider.\n * Omitted / empty → the model has no custom options and the picker is hidden.\n */\n options?: readonly ModelOption[]\n}\n\n/**\n * A single toggleable model option. Declarative metadata only — the provider\n * owns the actual request-body translation (keyed on {@link id}). Kept generic\n * (not \"fast mode\"-specific) so new per-model knobs land without a type change.\n */\nexport interface ModelOption {\n /** Stable identifier; the key under which the toggle persists + reaches the provider. */\n id: string\n /** Short label shown in the picker (e.g. \"Fast mode\"). */\n label: string\n /** One-line description of the trade-off (shown in the picker row). */\n description?: string\n /**\n * Optional cost multipliers applied to the model's base rates while the\n * option is enabled. `{ input: 2, output: 2 }` doubles both. Used by the\n * cost estimator so the footer reflects premium-priced modes (fast mode).\n */\n costMultiplier?: { input?: number, output?: number, cacheRead?: number, cacheWrite?: number }\n}\n\n/**\n * Free-form configuration field that a provider needs the user to supply\n * alongside (or instead of) an API key — e.g. a self-hosted base URL, an\n * Azure deployment name, a default model id.\n *\n * The wizard prompts for each field in order. Submitted values are persisted\n * into the apikey credential (as `customFields[key]`) and mirrored into\n * `process.env[envVar]` so the provider's factory can read them via the same\n * env-var convention as `envKey`.\n *\n * Descriptors without an `envKey` use `customFields` as the sole credential\n * mechanism — the wizard skips its API-key step in that case.\n */\nexport interface CustomField {\n /** Storage key inside the credential's `customFields` record. */\n key: string\n /** Human-readable label shown in the wizard step's title. */\n label: string\n /** Env var name to mirror this field into at TUI launch + on submit. */\n envVar: string\n /** Optional placeholder shown in the wizard input. */\n placeholder?: string\n /** Optional helper copy rendered above the input. */\n hint?: string\n /** When true, an empty submission is rejected by the wizard. Default: false. */\n required?: boolean\n}\n\nexport interface ProviderDescriptor {\n /**\n * Unique identifier. Used as the registry key, persisted in `state.json`\n * as the resumed provider, and (by default) as the credential-file key.\n */\n key: string\n /** Display name shown in the TUI's provider picker and labels. */\n label: string\n /**\n * Factory that builds a fresh `Provider` instance. Called on every session\n * activation. Some factories (e.g. zidane's built-in `anthropic`) eagerly\n * resolve credentials at construction time and throw when none are\n * configured — set {@link defaultModel} on the descriptor to avoid the TUI\n * calling the factory before the user has picked + authed a provider.\n */\n factory: () => Provider\n /**\n * Default model id to seed the picker with. When omitted, the TUI falls\n * back to `descriptor.factory().meta.defaultModel`, which constructs the\n * provider — fine for lazy-credential factories, but breaks for factories\n * that throw on missing credentials before the wizard runs. Setting this\n * eagerly lets the TUI render the auth wizard without ever instantiating\n * the provider.\n */\n defaultModel?: string\n /**\n * Env var checked when detecting whether the user has an API key\n * configured for this provider. Optional — set to `undefined` when the\n * provider only supports OAuth (or only credentials via a custom path).\n */\n envKey?: string\n /**\n * Key under which credentials live in `credentials.json`. Defaults to\n * `key`. The only built-in that overrides this is OpenAI Codex\n * (`openai` → `openai-codex`) to stay compatible with the harness\n * provider's existing lookup.\n */\n credentialFileKey?: string\n /** Placeholder shown in the wizard's API-key input. */\n apiKeyPlaceholder?: string\n /**\n * pi-ai (or compat) OAuth provider. When present, the wizard offers an\n * OAuth option in addition to API key.\n */\n oauthProvider?: OAuthProviderInterface\n /**\n * Optional copy appended to the wizard's \"OAuth\" method description.\n * Use to communicate why a user might pick OAuth (e.g. subscription tier,\n * org-wide SSO). Shown after `browser-based sign-in`. Skip the leading\n * space — the wizard adds it. Example: `'Claude Pro/Max subscription'`.\n */\n oauthHint?: string\n /**\n * pi-ai provider id used to look up models in pi-ai's built-in registry.\n * Defaults to `key`. Only Codex differs (`openai` → `openai-codex`).\n */\n piProviderId?: string\n /**\n * Override the model list returned for this provider's picker. When set,\n * skips pi-ai's registry entirely. Useful for hosts maintaining their\n * own model catalogue or for custom providers pi-ai doesn't know about.\n */\n models?: readonly ModelInfo[]\n /**\n * Additional free-form fields the wizard should collect from the user\n * (e.g. base URL for a self-hosted endpoint, default model slug for a\n * provider with no fixed catalogue). Each field is persisted in the\n * apikey credential's `customFields` map and mirrored into the matching\n * `envVar` at TUI launch.\n *\n * When `envKey` is also set, the wizard collects custom fields **before**\n * the API key. When `envKey` is omitted, the wizard skips the API-key\n * step entirely — `customFields` becomes the sole credential mechanism\n * (used by the `local` provider, which authenticates via baseURL only).\n */\n customFields?: readonly CustomField[]\n /**\n * Extra models merged AHEAD of pi-ai's registry list (de-duped by id, pi-ai\n * wins on collision). Unlike {@link models} this augments rather than\n * replaces — used to surface freshly-shipped models pi-ai hasn't bundled\n * yet without losing the rest of the registry. Ignored when {@link models}\n * is set.\n */\n extraModels?: readonly ModelInfo[]\n /**\n * Resolve the custom {@link ModelOption}s a given model id supports. Used to\n * attach options (e.g. fast mode) to models that come from pi-ai's registry —\n * which {@link extraModels} can't reach because pi-ai wins the id collision.\n * Returns `undefined` / `[]` for models with no options. Models that already\n * carry `options` (e.g. via {@link extraModels}) keep theirs.\n */\n optionsFor?: (modelId: string) => readonly ModelOption[] | undefined\n /**\n * Env var holding a user-supplied context window (in tokens) for providers\n * that have no model registry to advertise one — chiefly the `local`\n * provider, whose runtime (Ollama, vLLM, …) doesn't expose its loaded\n * model's window over the OpenAI-compat API. When set, {@link getContextWindow}\n * falls back to resolving this env var (via {@link resolveContextWindow})\n * after the registry lookup comes up empty. The value is either a single\n * window (`\"32768\"`, applied to every model) or a per-model map\n * (`\"llama3.1:8b=32768, qwen=128k, 64k\"`, with an optional bare fallback).\n * Pair it with a {@link CustomField} that mirrors the user's value into the\n * same `envVar` so the footer's context indicator and auto-compaction work\n * for local models.\n */\n contextWindowEnvVar?: string\n}\n\n/** Convenience accessor — returns `credentialFileKey ?? key`. */\nexport function credKeyOf(desc: ProviderDescriptor): string {\n return desc.credentialFileKey ?? desc.key\n}\n\n/** Convenience accessor — returns `piProviderId ?? key`. */\nexport function piIdOf(desc: ProviderDescriptor): string {\n return desc.piProviderId ?? desc.key\n}\n\n// ---------------------------------------------------------------------------\n// Built-in descriptors\n// ---------------------------------------------------------------------------\n\nexport const anthropicDescriptor: ProviderDescriptor = {\n key: 'anthropic',\n label: 'Anthropic',\n factory: anthropic,\n defaultModel: 'claude-opus-4-8',\n envKey: 'ANTHROPIC_API_KEY',\n apiKeyPlaceholder: 'sk-ant-…',\n oauthProvider: anthropicOAuthProvider,\n oauthHint: 'Claude Pro/Max subscription',\n extraModels: ANTHROPIC_EXTRA_MODELS,\n optionsFor: id => FAST_MODE_OPTIONS[id] ? [FAST_MODE_OPTIONS[id]] : undefined,\n}\n\nexport const openaiDescriptor: ProviderDescriptor = {\n key: 'openai',\n label: 'OpenAI',\n factory: openai,\n defaultModel: 'gpt-5.5',\n envKey: 'OPENAI_CODEX_API_KEY',\n credentialFileKey: 'openai-codex',\n piProviderId: 'openai-codex',\n apiKeyPlaceholder: 'sk-… or eyJ… (Codex)',\n oauthProvider: openaiCodexOAuthProvider,\n}\n\nexport const openrouterDescriptor: ProviderDescriptor = {\n key: 'openrouter',\n label: 'OpenRouter',\n factory: openrouter,\n defaultModel: 'anthropic/claude-sonnet-4-6',\n envKey: 'OPENROUTER_API_KEY',\n apiKeyPlaceholder: 'sk-or-…',\n}\n\nexport const cerebrasDescriptor: ProviderDescriptor = {\n key: 'cerebras',\n label: 'Cerebras',\n factory: cerebras,\n defaultModel: 'zai-glm-4.7',\n envKey: 'CEREBRAS_API_KEY',\n apiKeyPlaceholder: 'csk-…',\n}\n\n/**\n * Conservative context-window assumption for a user-configured local model.\n * Local runtimes expose no reliable per-model metadata over the OpenAI-compat\n * `/v1/models` endpoint (it returns ids, not context sizes), so we pick a\n * floor that fits the smallest mainstream OSS models (Llama 3.x 8B, Qwen2.5)\n * without over-promising. The footer's context indicator and auto-compaction\n * lean on this — under-estimating is the safe direction (compact early rather\n * than overflow the server's real window).\n *\n * Users running larger-context models (e.g. a 128K Qwen) can raise it via the\n * `LOCAL_LLM_CONTEXT_WINDOW` env var / customField, which supports a single\n * value or a per-model map (resolved by {@link resolveContextWindow}).\n */\nconst LOCAL_DEFAULT_CONTEXT_WINDOW = 8_192\n\n/**\n * Local OpenAI-compatible LLM (Ollama, vLLM, LM Studio, Lemonade, llama.cpp).\n *\n * No fixed base URL or model catalogue — both come from the user via\n * `customFields`. No `envKey`, so the wizard skips the API-key prompt and\n * treats the customFields-only credential as the auth signal (see\n * `applyApiKeyEnv` + `detectAuth`).\n *\n * Vision / cache / reasoning are off by default in the factory — flip them\n * by passing a custom descriptor that calls `local({ capabilities })`.\n *\n * `models` is a getter, not a static list: there's no fixed catalogue to ship,\n * but once the user has configured a default model (mirrored into\n * `LOCAL_LLM_DEFAULT_MODEL` by `applyApiKeyEnv`), we surface it as a\n * single-entry list so the model picker + footer aren't empty. Resolved at\n * access time because the env var is populated at TUI launch, after this\n * module loads. Empty list when unconfigured — the picker hides, the user\n * types a slug at the prompt as before.\n */\nexport const localDescriptor: ProviderDescriptor = {\n key: 'local',\n label: 'Local LLM',\n factory: local,\n get models(): readonly ModelInfo[] {\n const configured = process.env.LOCAL_LLM_DEFAULT_MODEL?.trim()\n if (!configured)\n return []\n // Resolve the window from the user's `LOCAL_LLM_CONTEXT_WINDOW` (single\n // value or per-model map), falling back to the conservative default. Uses\n // the same parser as `getContextWindow`'s `contextWindowEnvVar` path so\n // both surfaces agree on the number.\n const resolved = resolveContextWindow(process.env.LOCAL_LLM_CONTEXT_WINDOW, configured)\n return [{\n id: configured,\n name: configured,\n contextWindow: resolved ?? LOCAL_DEFAULT_CONTEXT_WINDOW,\n input: ['text'],\n }]\n },\n customFields: [\n {\n key: 'baseURL',\n label: 'Base URL',\n envVar: 'LOCAL_LLM_BASE_URL',\n placeholder: 'http://localhost:11434/v1',\n hint: 'Where your local LLM server is reachable. Examples: Ollama → http://localhost:11434/v1, vLLM → http://localhost:8000/v1, LM Studio → http://localhost:1234/v1.',\n required: true,\n },\n {\n key: 'apiKey',\n label: 'API key',\n envVar: 'LOCAL_LLM_API_KEY',\n placeholder: 'leave blank if your server is unauthenticated',\n },\n {\n key: 'model',\n label: 'Default model',\n envVar: 'LOCAL_LLM_DEFAULT_MODEL',\n placeholder: 'e.g. llama3.1:8b, qwen2.5-coder, mistral-small',\n hint: 'Model id your server exposes. Leave blank to pick later from the model picker.',\n },\n {\n key: 'contextWindow',\n label: 'Context window (tokens)',\n envVar: 'LOCAL_LLM_CONTEXT_WINDOW',\n placeholder: 'e.g. 32768 · or per-model: llama3.1:8b=32768, qwen2.5-coder=128k, 64k',\n hint: 'Context length(s) for your local model(s) — local runtimes don\\'t advertise this, so set it here to enable the context indicator and auto-compaction. Use a single value (32768, 128k) for all models, or a comma-separated map of `model=window` with an optional bare value as the fallback. Press',\n },\n ],\n contextWindowEnvVar: 'LOCAL_LLM_CONTEXT_WINDOW',\n}\n\n/**\n * Cursor models. pi-ai has no Cursor registry, so we ship a small curated\n * list (Cursor proxies these). Context windows are nominal — Cursor inference\n * isn't wired yet (the provider's `stream()` throws), so these only drive the\n * picker once the protobuf transport lands.\n */\nconst CURSOR_MODELS: readonly ModelInfo[] = [\n { id: 'claude-4.6-sonnet', name: 'Claude Sonnet 4.6', contextWindow: 200_000, reasoning: true, input: ['text', 'image'] },\n { id: 'claude-4.8-opus', name: 'Claude Opus 4.8', contextWindow: 200_000, reasoning: true, input: ['text', 'image'] },\n { id: 'gpt-5.5', name: 'GPT-5.5', contextWindow: 272_000, reasoning: true, input: ['text', 'image'] },\n { id: 'composer-2', name: 'Composer 2', contextWindow: 200_000, input: ['text'] },\n { id: 'composer-2.5', name: 'Composer 2.5', contextWindow: 200_000, input: ['text'] },\n]\n\nexport const cursorDescriptor: ProviderDescriptor = {\n key: 'cursor',\n label: 'Cursor',\n factory: cursor,\n defaultModel: 'claude-4.6-sonnet',\n // OAuth-only: no API-key env var.\n envKey: undefined,\n oauthProvider: cursorOAuthProvider,\n oauthHint: 'Cursor subscription',\n models: CURSOR_MODELS,\n}\n\n/**\n * Default provider registry. Passed verbatim when `runTui` is invoked without\n * an explicit `providers` option. Hosts that want to override per-provider\n * metadata can spread this and replace specific entries:\n *\n * ```ts\n * runTui({ providers: { ...BUILTIN_PROVIDERS, anthropic: myOwnAnthropicDescriptor } })\n * ```\n *\n * `cursor` is intentionally NOT registered here: OAuth works, but inference\n * isn't wired (`cursor.stream()` throws — Cursor speaks a protobuf/HTTP-2\n * agent protocol, not OpenAI-compat). Shipping it in the picker only lets users\n * select a model that errors every turn. The descriptor stays exported so a\n * host can opt in (`{ ...BUILTIN_PROVIDERS, cursor: cursorDescriptor }`) once\n * the transport lands — re-add it here at that point.\n */\nexport const BUILTIN_PROVIDERS: Readonly<Record<string, ProviderDescriptor>> = {\n anthropic: anthropicDescriptor,\n openai: openaiDescriptor,\n openrouter: openrouterDescriptor,\n cerebras: cerebrasDescriptor,\n local: localDescriptor,\n}\n\n// ---------------------------------------------------------------------------\n// Model registry helper\n// ---------------------------------------------------------------------------\n\n/**\n * Resolve the model list for a given provider. Honors `descriptor.models`\n * when set; otherwise queries pi-ai via `descriptor.piProviderId`. Returns\n * `[]` for descriptors with no known mapping (custom providers without a\n * model list) — callers should hide the model picker in that case.\n */\nexport function modelsForDescriptor(descriptor: ProviderDescriptor): readonly ModelInfo[] {\n if (descriptor.models)\n return descriptor.models\n let bundled: readonly ModelInfo[]\n try {\n bundled = getModels(piIdOf(descriptor) as never) as readonly ModelInfo[]\n }\n catch {\n bundled = []\n }\n if (descriptor.extraModels?.length) {\n // Merge extras ahead of the bundled list, de-duped by id (bundled wins, so a\n // model that lands in pi-ai later transparently supersedes the local entry).\n const bundledIds = new Set(bundled.map(m => m.id))\n const extras = descriptor.extraModels.filter(m => !bundledIds.has(m.id))\n bundled = [...extras, ...bundled]\n }\n return descriptor.optionsFor ? bundled.map(m => decorateOptions(m, descriptor)) : bundled\n}\n\n/**\n * Attach descriptor-resolved {@link ModelOption}s to a model that doesn't\n * already declare its own. Pure / non-mutating — returns the input untouched\n * when the model has options already or the descriptor resolves none.\n */\nfunction decorateOptions(model: ModelInfo, descriptor: ProviderDescriptor): ModelInfo {\n if (model.options?.length || !descriptor.optionsFor)\n return model\n const options = descriptor.optionsFor(model.id)\n return options?.length ? { ...model, options } : model\n}\n\n/**\n * Resolve a single model's metadata via the descriptor's model source.\n * Mirrors {@link modelsForDescriptor} routing: descriptor's own list wins,\n * pi-ai's registry is the fallback. Returns `null` when the model isn't\n * known.\n */\nexport function getModelInfo(descriptor: ProviderDescriptor, modelId: string): ModelInfo | null {\n if (descriptor.models)\n return descriptor.models.find(m => m.id === modelId) ?? null\n try {\n const bundled = getModel(piIdOf(descriptor) as never, modelId as never) as ModelInfo | undefined\n if (bundled)\n return decorateOptions(bundled, descriptor)\n }\n catch {\n // fall through to extras\n }\n const extra = descriptor.extraModels?.find(m => m.id === modelId)\n return extra ? decorateOptions(extra, descriptor) : null\n}\n\n/**\n * Parse a user-supplied context-window string into a token count.\n *\n * Accepts a plain integer (`\"32768\"`), or a number with a `k`/`m` suffix\n * (×1_000 / ×1_000_000, case-insensitive, optional space) — so `\"128k\"`\n * yields `128_000`, matching how the footer renders windows (`fmtTokens`).\n * Fractional values are floored (`\"1.5k\"` → `1500`). Returns `null` for\n * empty, malformed, zero, or negative input so callers fall through to\n * \"window unknown\" rather than a bogus ceiling.\n */\nexport function parseContextWindow(raw: string | undefined | null): number | null {\n if (raw == null)\n return null\n const trimmed = raw.trim()\n if (trimmed.length === 0)\n return null\n const match = /^(\\d+(?:\\.\\d+)?)\\s*([km])?$/i.exec(trimmed)\n if (!match)\n return null\n const suffix = match[2]?.toLowerCase()\n const mult = suffix === 'k' ? 1_000 : suffix === 'm' ? 1_000_000 : 1\n const value = Math.floor(Number.parseFloat(match[1]) * mult)\n return value >= 1 ? value : null\n}\n\n/**\n * Resolve a per-model context window from a user-supplied spec string.\n *\n * A spec is a comma-separated list of entries. An entry is either:\n * - `model=window` — a per-model override (split on the **first** `=`, so\n * model ids containing `:` or `/` survive), or\n * - a bare `window` — the fallback applied to any model not named above.\n *\n * Windows are parsed by {@link parseContextWindow} (plain int or k/m suffix).\n * Whitespace around entries and the `=` is ignored; malformed entries are\n * skipped rather than poisoning the whole spec; a later duplicate key wins.\n *\n * Lookup order for `modelId`: exact map entry → bare fallback → `null`. A\n * lone bare value (`\"32768\"`) therefore behaves as a single global window,\n * keeping the simple single-model config working.\n *\n * @example resolveContextWindow('llama3.1:8b=32768, qwen=128k, 64k', 'qwen') // 128000\n */\nexport function resolveContextWindow(spec: string | undefined | null, modelId: string): number | null {\n if (spec == null)\n return null\n const overrides = new Map<string, number>()\n let fallback: number | null = null\n for (const entry of spec.split(',')) {\n const eq = entry.indexOf('=')\n if (eq === -1) {\n const bare = parseContextWindow(entry)\n if (bare != null)\n fallback = bare\n continue\n }\n const key = entry.slice(0, eq).trim()\n const window = parseContextWindow(entry.slice(eq + 1))\n if (key.length > 0 && window != null)\n overrides.set(key, window)\n }\n return overrides.get(modelId) ?? fallback\n}\n\n/**\n * Look up the model's max context window via the descriptor's model source.\n * Falls back to {@link ProviderDescriptor.contextWindowEnvVar} (parsed via\n * {@link resolveContextWindow}, which supports a per-model map) when the\n * registry has nothing — this is how local LLMs, whose runtimes don't\n * advertise a window, get one. Returns `null` when neither source resolves a\n * value (custom slugs, providers without a registry or a configured window);\n * callers should hide the context indicator and skip auto-compaction then.\n */\nexport function getContextWindow(descriptor: ProviderDescriptor, modelId: string): number | null {\n const fromRegistry = getModelInfo(descriptor, modelId)?.contextWindow\n if (fromRegistry != null)\n return fromRegistry\n if (descriptor.contextWindowEnvVar)\n return resolveContextWindow(process.env[descriptor.contextWindowEnvVar], modelId)\n return null\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`\n * (`utils/context.ts:12`) — covers p99.99 of summary + response output.\n * A turn that consumed N input tokens leaves `effectiveWindow - N`\n * headroom for the next prompt's response; once headroom shrinks\n * 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 * Used by both the footer's context indicator and the auto-compact\n * trigger so the two surfaces agree on \"how full are we, really\".\n * Pass `null` through unchanged so callers can pipe `getContextWindow`\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 * Whether the given model exposes a reasoning / extended-thinking knob.\n * Drives the TUI's effort picker visibility — the `ctrl+n` shortcut and\n * the bottom-bar `effortName` segment only surface when this is `true`.\n * Returns `false` for unknown models (no registry entry → no advertised\n * capability).\n */\nexport function modelSupportsReasoning(descriptor: ProviderDescriptor, modelId: string): boolean {\n return getModelInfo(descriptor, modelId)?.reasoning === true\n}\n\n/**\n * Custom {@link ModelOption}s the given model supports (e.g. fast mode), or `[]`\n * when none. Drives the TUI/GUI options picker visibility — surfaces only when\n * non-empty.\n */\nexport function modelOptionsFor(descriptor: ProviderDescriptor, modelId: string): readonly ModelOption[] {\n return getModelInfo(descriptor, modelId)?.options ?? []\n}\n\n/**\n * Normalize a model-options blob to an enabled-only `{ id: true }` map.\n *\n * Single source of truth shared by every surface that reads or persists model\n * options — the TUI run path, the GUI engine reader, and the GUI IPC handlers.\n * Tolerant of arbitrary input (persisted JSON metadata may be anything): a\n * non-object → `{}`, and only entries strictly equal to `true` survive. This\n * keeps the persisted shape minimal (no `{ fast: false }` noise) and means\n * callers never forward a disabled option to `agent.run`.\n */\nexport function enabledModelOptions(raw: unknown): Record<string, boolean> {\n if (!raw || typeof raw !== 'object')\n return {}\n const out: Record<string, boolean> = {}\n for (const [id, on] of Object.entries(raw as Record<string, unknown>)) {\n if (on === true)\n out[id] = true\n }\n return out\n}\n\n/**\n * Resolve a model's remembered options for a (re)pick: keep only options the\n * model still declares AND that are enabled, dropping any that no longer apply\n * (e.g. switching away from a model that supported `fast`). Returns `undefined`\n * when nothing applies so callers can omit the field entirely.\n *\n * Shared by the TUI pick path and the launch-time resume path so \"which options\n * survive a model switch / relaunch\" is decided in exactly one place.\n */\nexport function restoreModelOptions(\n descriptor: ProviderDescriptor,\n modelId: string,\n remembered: Record<string, Record<string, boolean>> | undefined,\n): Record<string, boolean> | undefined {\n const validIds = new Set(modelOptionsFor(descriptor, modelId).map(o => o.id))\n if (validIds.size === 0)\n return undefined\n const enabled = enabledModelOptions(remembered?.[modelId])\n const filtered: Record<string, boolean> = {}\n for (const id of Object.keys(enabled)) {\n if (validIds.has(id))\n filtered[id] = true\n }\n return Object.keys(filtered).length > 0 ? filtered : undefined\n}\n","/**\n * Per-session file read tracking + tool-dedup state.\n *\n * Two concerns, one module so they can share the `WeakMap<Session, …>`\n * pattern (state lives as long as the session does, GC'd alongside it —\n * no public schema changes, no persistence). Each section below stands\n * on its own; the two maps don't share keys or behavior.\n *\n * ## Read-state\n *\n * Shared between `read_file` (dedup re-reads) and `edit` / `multi_edit`\n * (`requireReadBeforeEdit` guard). The state map records, per canonical\n * absolute file path:\n *\n * - `contentHash` — FNV-1a hash of the bytes the model last saw. Identical\n * re-reads return a stub; edits compare against this hash to detect\n * stale content.\n * - `(offset, limit, maxBytes, lineNumbers)` — slice / shape of the last\n * read, so a wider re-read invalidates the prior stub and a different\n * `lineNumbers` mode doesn't dedup against the previous shape.\n * - `mtimeMs` — wall-clock at last read, diagnostic only.\n *\n * Resolution is split between two helpers so consumers don't have to\n * juggle the `Session` vs. explicit-map cases themselves:\n *\n * - {@link getReadState} returns the map keyed by `Session` instance.\n * - {@link resolveReadStateMap} prefers an explicit `ctx.readState`\n * (the path `spawn`'s `shareReadState` uses to forward the parent's\n * map into a sessionless child) and falls back to the session-keyed\n * lookup. Tools should call this helper, not `getReadState` directly.\n *\n * ## Tool-dedup\n *\n * Backs `behavior.dedupTools`. Records the most recent `(hash, result)`\n * per canonical tool name within a session. Same WeakMap pattern; no\n * cross-talk with the read-state map.\n */\n\nimport type { Session } from '../session'\nimport type { ToolResultContent } from '../types'\nimport { resolve } from 'node:path'\n\n// ---------------------------------------------------------------------------\n// Read-state\n// ---------------------------------------------------------------------------\n\nexport interface ReadStateEntry {\n contentHash: string\n /** Slice parameters for the last read. */\n offset: number\n limit: number\n maxBytes: number\n /**\n * Whether the prior read emitted line-number prefixes. Tracked so a\n * read with `lineNumbers: true` doesn't dedup against one with\n * `lineNumbers: false` (or vice versa) — the dedup stub would mislead\n * the model about the prior output's shape.\n */\n lineNumbers?: boolean\n /** Wall-clock at last read — diagnostic only, not used for invalidation. */\n mtimeMs: number\n}\n\nexport type ReadStateMap = Map<string, ReadStateEntry>\n\nconst STATE = new WeakMap<Session, ReadStateMap>()\n\n/**\n * Get or lazily create the per-session read-state map. Returns `undefined`\n * when no session is provided.\n *\n * Most tool callers should prefer {@link resolveReadStateMap}, which\n * additionally honors an explicit `ctx.readState` (the path\n * `spawn`'s `shareReadState: true` uses to forward the parent's map\n * into a sessionless child). Use this helper directly only when the\n * Session-keyed map is exactly what you want and you don't need to\n * accept an override.\n */\nexport function getReadState(session: Session | undefined): ReadStateMap | undefined {\n if (!session)\n return undefined\n let map = STATE.get(session)\n if (!map) {\n map = new Map()\n STATE.set(session, map)\n }\n return map\n}\n\n/**\n * Resolve the active read-state map from a tool context. An explicit\n * `ctx.readState` wins (used by `spawn`'s `shareReadState` opt-in to\n * forward the parent's map into a sessionless child); otherwise the\n * usual `Session`-keyed lookup applies.\n *\n * Tools should call this helper instead of `getReadState(ctx.session)`\n * directly so the `shareReadState` plumbing is honored uniformly.\n */\nexport function resolveReadStateMap(ctx: {\n session?: Session\n readState?: ReadStateMap\n}): ReadStateMap | undefined {\n return ctx.readState ?? getReadState(ctx.session)\n}\n\n/**\n * Canonical read-state key for a `(cwd, path)` pair.\n *\n * `ctx.execution.readFile(handle, path)` resolves the model-provided\n * path against `handle.cwd` before touching disk — so `src/App.tsx`,\n * `./src/App.tsx`, and `/abs/cwd/src/App.tsx` all read the **same**\n * bytes. The read-state map MUST mirror that resolution: without it,\n * a model that reads `src/App.tsx` and then edits `./src/App.tsx`\n * trips the `requireReadBeforeEdit` gate for a file it demonstrably\n * already saw (the gate's \"has not been read in this session\" message\n * fires on a stale key).\n *\n * `node:path`'s `resolve(cwd, path)` short-circuits when `path` is\n * already absolute, so absolute and relative shapes converge on the\n * same canonical form. We don't `realpath()` symlinks — the file IS\n * the path the model addressed, not the realpath behind it; the host's\n * execution context decides how to dereference.\n */\nexport function readStateKey(cwd: string, path: string): string {\n return resolve(cwd, path)\n}\n\n/**\n * FNV-1a 32-bit hash, hex-encoded. Fast, non-cryptographic — we only need\n * collision resistance against accidental matches between different file\n * contents within one session (~1 in 4 billion). Cheaper than allocating\n * a Buffer and pulling in `crypto`.\n */\nexport function hashContent(text: string): string {\n let h = 0x811C9DC5\n for (let i = 0; i < text.length; i++) {\n h ^= text.charCodeAt(i)\n h = (h + ((h << 1) + (h << 4) + (h << 7) + (h << 8) + (h << 24))) >>> 0\n }\n return h.toString(16).padStart(8, '0')\n}\n\n// ---------------------------------------------------------------------------\n// Tool-dedup\n// ---------------------------------------------------------------------------\n\nexport interface ToolDedupEntry {\n hash: string\n result: string | ToolResultContent[]\n /**\n * Number of replay-hits accumulated against this `(name, hash)` since\n * the most recent fresh dispatch. `0` immediately after a fresh\n * dispatch; incremented every time a `tool:gate` handler returns the\n * cached result for an identical input. Used by `dedup-tools` to\n * implement `mode: 'block-after'` — when the count crosses the\n * configured threshold, the gate stops replaying and refuses the call\n * with a `Blocked:` tool_result so the model breaks out of the loop.\n *\n * Resets to `0` on a hash change (a different input under the same\n * tool name is treated as a fresh sequence). Optional for back-compat\n * with consumers that built the entry directly; absent is treated as\n * `0`.\n */\n repeats?: number\n}\n\nexport type ToolDedupMap = Map<string, ToolDedupEntry>\n\nconst TOOL_DEDUP_STATE = new WeakMap<Session, ToolDedupMap>()\n\n/**\n * Get or lazily create the per-session tool-dedup map. Returns `undefined`\n * when no session is provided — middleware should treat that as \"no dedup\".\n */\nexport function getToolDedupState(session: Session | undefined): ToolDedupMap | undefined {\n if (!session)\n return undefined\n let map = TOOL_DEDUP_STATE.get(session)\n if (!map) {\n map = new Map()\n TOOL_DEDUP_STATE.set(session, map)\n }\n return map\n}\n","/**\n * `dedup-tools` middleware — generic per-tool argument deduplication on top\n * of the `tool:gate` writable-`result` / writable-`block` slots.\n *\n * Mirrors the WeakMap-keyed-on-Session pattern that backs `read_file` dedup\n * (`src/tools/read-state.ts`), generalized to arbitrary tools whose hasher\n * is supplied by the consumer via `behavior.dedupTools`.\n *\n * Two modes (see `DedupToolConfig`):\n * - `'replay'` (default) — identical re-calls replay the prior result.\n * Cheap, but doesn't break the loop — the model keeps seeing the same\n * tool_result and keeps trying.\n * - `'block-after'` — replay for the first `threshold` identical calls,\n * then refuse with `Blocked: <reason>` via `tool:gate`. Sticky: every\n * subsequent identical call also blocks until a different input resets\n * the counter for that tool.\n *\n * Wiring:\n * 1. `tool:gate` — compute hash, compare to prior `(name → entry)`.\n * - Hash miss: record the hash on the per-call `pending` map so\n * `tool:after` writes the entry with `repeats: 0`.\n * - Hash hit, replay mode: set `ctx.result`, bump `pending.repeats`.\n * The Z20 substitute path fires `tool:after`; the handler reads\n * `pending.repeats` and persists it. The model sees the cached\n * payload again.\n * - Hash hit, block-after mode AND `count >= threshold`: set\n * `ctx.block` + `ctx.reason`, persist the bumped counter on\n * `state` directly (block path skips `tool:after`).\n * - Hash hit, block-after mode AND `count < threshold`: same as\n * replay (the cache is still warm, we just haven't crossed the\n * ceiling yet).\n * 2. `tool:after` — record `(hash, result, repeats)` for the dispatched\n * OR replayed call. Recorded post-`tool:transform`, i.e. the payload\n * the model actually saw — replaying a pre-transform value would\n * give the model different content on the dedup hit than on the\n * original call.\n *\n * No-session runs are silent no-ops: there's nowhere to record state.\n *\n * State scoping (`DedupToolConfig.scope`):\n * - `'session'` (default) — `(session, toolName)`. Right for pure tools.\n * - `'run'` — `(session, runId, toolName)`. Keeps a parent and its\n * subagents from sharing a dedup entry, so an identical re-call only\n * counts/replays within the run that issued it. `todowrite` uses this\n * so its `block-after` loop-breaker is run-local.\n */\n\nimport type { Hookable } from 'hookable'\nimport type { AgentHooks } from './agent'\nimport type { Session } from './session'\nimport type { AgentBehavior, ToolHookContext, ToolResultContent } from './types'\nimport { getToolDedupState } from './tools/read-state'\n\n/**\n * Install the per-tool argument-dedup middleware on a hook bus.\n * `getDedupTools` returns the resolved per-tool hashers (run override merged\n * with agent defaults). `getSession` does the same for the session-bound\n * state. Both are called lazily so handlers attached after install (e.g. via\n * MCP bootstrap completion) can take effect.\n *\n * Returns an `uninstall` fn — the agent calls this in `finally` so handlers\n * never leak across runs.\n */\nexport function installDedupToolsGate(\n hooks: Hookable<AgentHooks>,\n getDedupTools: () => AgentBehavior['dedupTools'] | undefined,\n getSession: () => Session | undefined,\n): () => void {\n // Track the hash *we* selected per `(callId, name)` so the corresponding\n // `tool:after` records the right entry. Necessary because `ctx.input` on\n // `tool:after` could in principle differ from the gate snapshot if a\n // `tool:gate` mutation rewrote `input` — and re-hashing there would emit a\n // dedup entry against a different fingerprint than the model's input.\n //\n // Cleared in the same `tool:after` handler after recording. Entries leak\n // when `tool:after` never fires for a call this gate saw — three known\n // paths: a downstream `tool:gate` listener sets `block: true`, validation\n // rejects the (post-gate) input, or a `tool:before` handler throws. All\n // bounded by run lifetime: the closure-local map dies with the run when\n // `uninstall()` clears it (called from the agent's run-end `finally`).\n interface PendingEntry {\n hash: string\n /**\n * Number of replay-hits this call has accumulated against the prior\n * `state` entry. `0` means the gate took the fresh-dispatch path\n * (hash miss). Persisted into `state.repeats` on `tool:after`.\n */\n repeats: number\n /**\n * Resolved state-map key (`name` or `runId::name`) the gate selected.\n * Carried so `tool:after` records against the exact same slot the\n * gate read — re-deriving there would risk drift if the scope inputs\n * ever differ between the two hooks.\n */\n key: string\n }\n const pending = new Map<string, PendingEntry>()\n\n function pendingKey(callId: string, name: string): string {\n return `${callId}::${name}`\n }\n\n /**\n * Resolve the legacy hasher-only form vs the new config-object form to\n * the same internal shape. Centralised so the gate/after handlers stay\n * single-branch. Returns `null` when the tool has no dedup config.\n */\n function resolveConfig(name: string): {\n hasher: (input: Record<string, unknown>) => string | undefined\n mode: 'replay' | 'block-after'\n threshold: number\n reason: string | ((input: Record<string, unknown>, count: number) => string) | undefined\n scope: 'session' | 'run'\n } | null {\n const dedupTools = getDedupTools()\n const raw = dedupTools?.[name]\n if (!raw)\n return null\n if (typeof raw === 'function')\n return { hasher: raw, mode: 'replay', threshold: Infinity, reason: undefined, scope: 'session' }\n // Threshold floor: `Math.max(2, Math.floor(raw.threshold))`. Values\n // `<= 1` would block the very first call (or first replay), which is\n // indistinguishable from never registering the hasher at all. We\n // clamp to 2 (block on the 2nd identical call) instead of silently\n // failing closed. Non-numbers / NaN fall through to the\n // mode-conditional default. Floats are floored AFTER the clamp so\n // `threshold: 1.5` produces 2, not 1.\n const rawThreshold = typeof raw.threshold === 'number' && Number.isFinite(raw.threshold)\n ? Math.max(2, Math.floor(raw.threshold))\n : undefined\n const threshold = rawThreshold\n ?? (raw.mode === 'block-after' ? 4 : Infinity)\n return {\n hasher: raw.hasher,\n mode: raw.mode ?? 'replay',\n threshold,\n reason: raw.reason,\n scope: raw.scope === 'run' ? 'run' : 'session',\n }\n }\n\n /**\n * State-map key for a tool's dedup entry. Session-scoped tools key on\n * the bare canonical name (the historical behavior). Run-scoped tools\n * prefix the owning `runId` so a parent and a subagent never share an\n * entry — falling back to the bare name when no `runId` is present\n * (hand-built contexts), which keeps the session-scope semantics for\n * those callers.\n */\n function stateKey(name: string, scope: 'session' | 'run', runId: string | undefined): string {\n if (scope === 'run' && runId)\n return `${runId}::${name}`\n return name\n }\n\n function formatReason(\n reason: string | ((input: Record<string, unknown>, count: number) => string) | undefined,\n toolName: string,\n input: Record<string, unknown>,\n count: number,\n ): string {\n if (typeof reason === 'string')\n return reason\n if (typeof reason === 'function') {\n try {\n const out = reason(input, count)\n if (typeof out === 'string' && out.length > 0)\n return out\n }\n catch {\n // Fall through to default\n }\n }\n return `Identical \\`${toolName}\\` call repeated ${count} times — break the loop by changing your approach or moving on to a different step.`\n }\n\n function gateHandler(ctx: ToolHookContext & {\n block: boolean\n reason: string\n result?: string | ToolResultContent[]\n runToolCounts: Readonly<Record<string, number>>\n }) {\n // Don't override prior gate handlers (block, or another middleware that\n // already substituted a result).\n if (ctx.block || ctx.result !== undefined)\n return\n\n const config = resolveConfig(ctx.name)\n if (!config)\n return\n\n const session = getSession()\n const state = getToolDedupState(session)\n if (!state)\n return\n\n let hash: string | undefined\n try {\n hash = config.hasher(ctx.input)\n }\n catch {\n // A throwing hasher disables dedup for this call only — never\n // surface it as a tool failure. The model still gets the real\n // execution, telemetry consumers see no surprise.\n return\n }\n if (typeof hash !== 'string' || hash.length === 0)\n return\n\n const key = stateKey(ctx.name, config.scope, ctx.runId)\n const prior = state.get(key)\n if (prior && prior.hash === hash) {\n // Replay-hit. `repeats` in state is the count of replays BEFORE\n // this call (0 right after a fresh dispatch). The total identical\n // calls counting this one = (prior.repeats ?? 0) + 1 + 1 = the\n // first dispatch plus all replays inclusive. We compute that as\n // `count` for both threshold checks and reason-formatting.\n const priorRepeats = prior.repeats ?? 0\n const count = priorRepeats + 2 // dispatch + earlier replays + this one\n if (config.mode === 'block-after' && count >= config.threshold) {\n // Block. The block path skips `tool:after`, so we MUST persist\n // the bumped counter here to keep subsequent identical calls\n // blocking. We do NOT overwrite `prior.result` — keeping the\n // last-seen successful payload around lets the host inspect it\n // post-hoc, and a hash change later resets the counter anyway.\n ctx.block = true\n ctx.reason = formatReason(config.reason, ctx.name, ctx.input, count)\n state.set(key, { hash, result: prior.result, repeats: priorRepeats + 1 })\n return\n }\n // Replay — feed the cached payload back through the Z20 substitute\n // path. `tool:after` fires and records the bumped counter via\n // `pending`.\n ctx.result = prior.result\n pending.set(pendingKey(ctx.callId, ctx.name), { hash, repeats: priorRepeats + 1, key })\n return\n }\n\n // Miss — remember the hash + zero repeats so the matching\n // `tool:after` records a fresh state entry.\n pending.set(pendingKey(ctx.callId, ctx.name), { hash, repeats: 0, key })\n }\n\n function afterHandler(ctx: ToolHookContext & {\n result: string | ToolResultContent[]\n outputBytes: number\n }) {\n const key = pendingKey(ctx.callId, ctx.name)\n const entry = pending.get(key)\n if (entry === undefined)\n return\n pending.delete(key)\n\n const session = getSession()\n const state = getToolDedupState(session)\n if (!state)\n return\n\n state.set(entry.key, { hash: entry.hash, result: ctx.result, repeats: entry.repeats })\n }\n\n const unregisterGate = hooks.hook('tool:gate', gateHandler)\n const unregisterAfter = hooks.hook('tool:after', afterHandler)\n\n return function uninstall() {\n unregisterGate()\n unregisterAfter()\n pending.clear()\n }\n}\n","/**\n * Tool-result disk persistence — replace oversize `tool_result` outputs with\n * a `<persisted-output>` stub pointing at the full payload on disk.\n *\n * Why: a single 200 KB shell output sits in the prompt prefix for the rest\n * of the session and gets re-sent every turn. Persisting it shrinks the\n * inline content to a fixed-size stub (preview + path), preserves the\n * original bytes on disk so the model can `read_file` them deliberately,\n * and keeps the prompt-cache prefix byte-stable across turns.\n *\n * Wire contract:\n *\n * - Trigger: `behavior.persistThreshold` bytes (off / undefined disables).\n * - Excluded tools: `behavior.persistExcludeTools` — bypass regardless of\n * size. Built-in chat profiles list `read_file`, `tool_search`,\n * `skills_use`, `skills_read`, `present_plan`, `ask_user`, `spawn`.\n * - Target: `behavior.persistDir`, one file per call id (`<callId>.txt`).\n * Atomic via write-to-`.tmp` then rename.\n * - Stub: byte-stable XML wrapper carrying tool name, byte count, path,\n * and a 2 KiB head preview. The stub itself becomes the `tool_result`\n * output that lands in `session.turns`, so re-emission on subsequent\n * turns reads back the same bytes and rides the prompt cache.\n *\n * No state. The substitution is one-shot at emit time; nothing in this\n * module persists across turns. The disk file is the source of truth for\n * \"what the model originally saw\"; the in-message stub is what gets\n * re-emitted.\n *\n * Execution-context note: persistence writes to the **host** filesystem\n * via Node's `fs/promises`. For agents running in a docker / sandbox\n * context, the model's `read_file` (which routes through\n * `ctx.execution.readFile`) cannot reach the host blob unless the\n * persistence dir is mounted into the container. Process-context agents\n * (the TUI default) have no such constraint.\n */\n\nimport type { ToolResultContent } from './types'\nimport { mkdir, readdir, rename, rm, stat, unlink, writeFile } from 'node:fs/promises'\nimport { dirname, isAbsolute, join } from 'node:path'\nimport { utf8ByteLength } from './compact/utils'\nimport { errorMessage } from './errors'\nimport { toolOutputByteLength } from './types'\nimport { escapeXml } from './xml'\n\n/**\n * Bytes of head content included in the inline preview block. 2 KiB matches\n * Claude Code's `PREVIEW_SIZE_BYTES` — enough for the model to identify the\n * content class (error output / structured data / log shape) and decide\n * whether to call `read_file` on the persisted path for the full payload.\n *\n * Tail-priority preview (matching `shell`'s truncation strategy) was\n * considered but rejected: most \"what is this?\" decisions get made from\n * the head, and the path is in the stub for the rare case where the tail\n * matters.\n */\nexport const PERSISTENCE_PREVIEW_BYTES = 2 * 1024\n\n/**\n * Byte-stable prefix every {@link buildPersistedStub} output starts with.\n * Exported so wire-level passes (tail compaction, future stale-output\n * elision) can recognize a persisted stub and preserve its path attribute\n * rather than replacing the stub with their own — losing the pointer to\n * the on-disk blob.\n *\n * Bound to the literal opening of the XML tag; changing the stub format\n * requires updating this constant in lockstep (and shipping a migration\n * for in-flight sessions).\n */\nexport const PERSISTED_STUB_PREFIX = '<persisted-output tool=\"'\n\n/**\n * Characters that are safe to use verbatim as a filesystem segment under\n * `persistDir`. Alphanumeric + `_` + `-` + `.` covers every real-world\n * `tool_use.id` we've seen (Anthropic `toolu_…`, OpenAI `call_…`, Bun\n * `crypto.randomUUID()` style, our own short ids).\n *\n * Anything else — slashes, `..`, control chars, spaces, unicode — is\n * rejected by {@link maybePersistToolResult} so a malformed or hostile\n * `callId` can't escape `persistDir` via path traversal.\n */\nconst SAFE_CALL_ID = /^[\\w.-]+$/\n\n/**\n * Resolve the per-session persistence directory under `<userDir>/tool-results/<sessionId>/`.\n *\n * The chat layer calls this at session activation and forwards the result\n * via `behavior.persistDir`. Exposed as a public helper so SDK consumers\n * pick the same layout — single source of truth for \"where do blobs live\".\n */\nexport function resolvePersistDir(opts: { userDir: string, sessionId: string }): string {\n if (!isAbsolute(opts.userDir))\n throw new Error(`resolvePersistDir: userDir must be absolute, got \"${opts.userDir}\"`)\n if (!opts.sessionId)\n throw new Error('resolvePersistDir: sessionId must be a non-empty string')\n return join(opts.userDir, 'tool-results', opts.sessionId)\n}\n\n/**\n * Resolve the per-session background-tasks directory under\n * `<userDir>/<sessionId>/tasks/`.\n *\n * The chat layer calls this at session activation and forwards the result\n * via `behavior.tasksDir`. Same shape as {@link resolvePersistDir}: hosts\n * get a single source of truth for \"where do task log files live\".\n * Created on first write; cleanup is the session-delete path's job.\n */\nexport function resolveTasksDir(opts: { userDir: string, sessionId: string }): string {\n if (!isAbsolute(opts.userDir))\n throw new Error(`resolveTasksDir: userDir must be absolute, got \"${opts.userDir}\"`)\n if (!opts.sessionId)\n throw new Error('resolveTasksDir: sessionId must be a non-empty string')\n return join(opts.userDir, opts.sessionId, 'tasks')\n}\n\n/**\n * 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. Eviction\n * failures (permissions, races) are surfaced through `ZIDANE_DEBUG`\n * but never block the calling tool result; an over-cap dir is a\n * housekeeping concern, not a correctness one.\n */\n maxBytes?: number\n}\n\nexport type PersistOutcome\n = | { kind: 'skip', reason: 'disabled' | 'excluded' | 'under-threshold' | 'unsupported-shape' | 'unsafe-call-id' | 'invalid-persist-dir' }\n | { kind: 'persisted', output: string, originalBytes: number, persistedPath: string, evicted?: { files: number, bytes: number } }\n | { kind: 'error', reason: 'write-failed', error: Error }\n\n/**\n * Decide-and-persist for a single tool result. Pure decision + filesystem\n * side-effect; returns the new wire-level `output` string when substitution\n * happened, otherwise tells the caller to leave the result alone.\n *\n * Atomicity: writes go through `<path>.tmp` + `rename` so a concurrent\n * read (or a crash mid-write) never sees a half-written blob.\n *\n * `ToolResultContent[]` results (images, structured blocks) currently bypass\n * persistence — the inline image bytes are the point of the call, and a\n * mixed text/image array isn't representable as a single `.txt` file. We\n * may revisit if a tool starts returning very large text-only arrays.\n */\nexport async function maybePersistToolResult(input: PersistInput): Promise<PersistOutcome> {\n if (!input.threshold || input.threshold <= 0)\n return { kind: 'skip', reason: 'disabled' }\n\n if (input.excludeTools?.includes(input.toolName))\n return { kind: 'skip', reason: 'excluded' }\n\n // Structured content (images, multi-block) — out of scope for v1.\n // Persistence on these would require either flattening to text or\n // multi-file blobs; neither buys us much for the common case (a tool\n // returning a large image is rare, and the image already has its own\n // size cap in the read_file image path).\n if (typeof input.output !== 'string')\n return { kind: 'skip', reason: 'unsupported-shape' }\n\n // Defense in depth — the chat layer always passes the absolute path\n // produced by `resolvePersistDir`, but a direct SDK consumer could pass\n // a relative path that would resolve against cwd in unexpected ways. We\n // refuse rather than create surprise files outside the intended dir.\n if (!isAbsolute(input.persistDir))\n return { kind: 'skip', reason: 'invalid-persist-dir' }\n\n // Path-traversal guard. The `callId` becomes a filename under\n // `persistDir`; an id containing `/`, `\\`, `..`, or control chars could\n // escape the dir (`../../etc/passwd.txt`) or land in subdirectories the\n // operator didn't anticipate. Real providers (Anthropic `toolu_…`,\n // OpenAI `call_…`, UUIDs) all pass the strict regex; rejecting the rest\n // is purely defensive and never bites legitimate traffic.\n if (!SAFE_CALL_ID.test(input.callId) || input.callId.includes('..'))\n return { kind: 'skip', reason: 'unsafe-call-id' }\n\n const originalBytes = toolOutputByteLength(input.output)\n if (originalBytes <= input.threshold)\n return { kind: 'skip', reason: 'under-threshold' }\n\n const persistedPath = join(input.persistDir, `${input.callId}.txt`)\n try {\n await writeAtomic(persistedPath, input.output)\n }\n catch (err) {\n return { kind: 'error', reason: 'write-failed', error: err instanceof Error ? err : new Error(String(err)) }\n }\n\n const stub = buildPersistedStub({\n toolName: input.toolName,\n originalBytes,\n persistedPath,\n output: input.output,\n })\n\n // LRU enforcement runs AFTER the write so the new blob participates in\n // the sweep (and, being newest by mtime, is never evicted itself unless\n // the cap is so tight a single blob can't fit). Best-effort: a failed\n // unlink is logged under `ZIDANE_DEBUG` but never propagates — the\n // caller already has its tool result; an over-cap dir is housekeeping.\n let evicted: { files: number, bytes: number } | undefined\n if (typeof input.maxBytes === 'number' && Number.isFinite(input.maxBytes) && input.maxBytes > 0) {\n evicted = await enforcePersistDirCap(input.persistDir, input.maxBytes)\n }\n\n return { kind: 'persisted', output: stub, originalBytes, persistedPath, ...(evicted && evicted.files > 0 ? { evicted } : {}) }\n}\n\n/**\n * Enforce a byte-cap on a persist directory by removing oldest `*.txt`\n * blobs (by mtime) until the remaining total is at or below `maxBytes`.\n *\n * Exported for tests + for hosts that want to run a periodic sweep\n * decoupled from the per-call hook. {@link maybePersistToolResult} calls\n * this automatically when `maxBytes` is set on its input, so production\n * callers usually don't invoke it directly.\n *\n * Failure modes:\n *\n * - `persistDir` missing → no-op (nothing to evict).\n * - `readdir` / `stat` errors → swallowed (best-effort); returns\n * `{ files: 0, bytes: 0 }`.\n * - Individual `unlink` failures → counted in the result only if they\n * succeeded; the loop continues to the next candidate so a single\n * permission blip doesn't abort the whole sweep.\n *\n * The mtime-asc sort is stable enough for our purposes: two blobs\n * written in the same millisecond are evicted in `readdir` order, which\n * is filesystem-dependent but consistent within a run. We never need\n * strict total ordering — the LRU is a soft cap, not a transactional\n * guarantee.\n */\nexport async function enforcePersistDirCap(\n persistDir: string,\n maxBytes: number,\n): Promise<{ files: number, bytes: number }> {\n if (!isAbsolute(persistDir) || !Number.isFinite(maxBytes) || maxBytes <= 0)\n return { files: 0, bytes: 0 }\n\n let entries: string[]\n try {\n entries = await readdir(persistDir)\n }\n catch {\n // Directory doesn't exist (no blobs persisted yet) or unreadable.\n // Either way: nothing to do.\n return { files: 0, bytes: 0 }\n }\n\n // `.txt` extension is the canonical shape produced by `maybePersistToolResult`.\n // Filter on it so a stray `.tmp` (crash residue) or a manually-dropped\n // sentinel file doesn't get counted toward the budget or accidentally\n // evicted.\n const txtFiles = entries.filter(name => name.endsWith('.txt'))\n\n interface FileMeta { path: string, size: number, mtime: number }\n const metas: FileMeta[] = []\n for (const name of txtFiles) {\n const path = join(persistDir, name)\n try {\n const s = await stat(path)\n if (!s.isFile())\n continue\n metas.push({ path, size: s.size, mtime: s.mtimeMs })\n }\n catch {\n // Race: file vanished between readdir and stat. Skip silently.\n }\n }\n\n const totalBytes = metas.reduce((sum, m) => sum + m.size, 0)\n if (totalBytes <= maxBytes)\n return { files: 0, bytes: 0 }\n\n // Oldest first. Keep evicting until we're under cap OR we're about to\n // touch the newest blob — that one represents the just-written result\n // the caller needs to be able to point the model at, so we preserve it\n // even if its size alone exceeds the cap. That edge case surfaces as a\n // cap exceedance in the logs; the operator's right next move is\n // raising `persistMaxBytes`.\n //\n // The \"preserve newest\" guard runs off the loop INDEX rather than the\n // successful-eviction counter: an unlink failure earlier in the sweep\n // (file vanished mid-readdir, permissions blip) must NOT shift the\n // safety boundary forward. Were we to use `evictedFiles === metas.length - 1`\n // and the very first unlink failed, we'd walk all the way to the\n // newest blob and try to evict it.\n metas.sort((a, b) => a.mtime - b.mtime)\n let running = totalBytes\n let evictedFiles = 0\n let evictedBytes = 0\n for (let i = 0; i < metas.length; i++) {\n if (running <= maxBytes)\n break\n if (i === metas.length - 1)\n break\n const meta = metas[i]!\n try {\n await unlink(meta.path)\n running -= meta.size\n evictedFiles += 1\n evictedBytes += meta.size\n }\n catch (err) {\n if (process.env.ZIDANE_DEBUG)\n process.stderr.write(`[zidane/persistence] evict \"${meta.path}\" failed: ${errorMessage(err)}\\n`)\n }\n }\n\n return { files: evictedFiles, bytes: evictedBytes }\n}\n\ninterface BuildStubInput {\n toolName: string\n originalBytes: number\n persistedPath: string\n output: string\n}\n\n/**\n * Render the byte-stable `<persisted-output>` stub the model sees in place\n * of the original `tool_result`.\n *\n * Format choices:\n * - XML wrapper because models reliably parse it as structural.\n * - Byte count + path in attributes so the model can decide whether to\n * `read_file` the persisted blob without scanning the preview.\n * - Preview always shows the head — `shell`'s tail-priority truncation is\n * irrelevant here because the model has the full path if it needs the\n * tail.\n * - No timestamps, no random UUIDs inside the stub: every byte must be\n * reproducible from the inputs, otherwise re-emission on subsequent\n * turns would bust the prompt cache.\n *\n * Exported for tests (asserting the byte-stable contract) and for SDK\n * consumers wiring their own persistence middleware against the same\n * surface.\n */\nexport function buildPersistedStub(input: BuildStubInput): string {\n const { slice: previewSlice, bytes: previewBytes } = sliceFirstBytes(input.output, PERSISTENCE_PREVIEW_BYTES)\n const truncated = previewSlice.length < input.output.length\n const previewMarker = truncated\n ? `\\n…(${input.originalBytes - previewBytes} more bytes in persisted file)`\n : ''\n // Open tag built from PERSISTED_STUB_PREFIX so the stub-detection constant\n // and the stub emitter share one source of truth — changing the format\n // requires updating one place and downstream recognizers can't drift.\n return [\n `${PERSISTED_STUB_PREFIX}${escapeXml(input.toolName)}\" bytes=\"${input.originalBytes}\" path=\"${escapeXml(input.persistedPath)}\">`,\n `${previewSlice}${previewMarker}`,\n '</persisted-output>',\n ].join('\\n')\n}\n\n/**\n * Remove every persisted blob belonging to a session. Called by the chat\n * layer from its session-delete path so closing a session frees the disk\n * footprint alongside the SQLite row.\n *\n * Idempotent — missing directory (session never persisted anything) is a\n * no-op, not an error. Wraps the `rm -rf` so a permissions blip on one\n * blob doesn't propagate to the caller; the chat layer can't usefully\n * recover from \"couldn't unlink a result file\" mid-delete.\n */\nexport async function cleanupPersistedSession(persistRoot: string): Promise<void> {\n if (!isAbsolute(persistRoot))\n throw new Error(`cleanupPersistedSession: persistRoot must be absolute, got \"${persistRoot}\"`)\n try {\n await rm(persistRoot, { recursive: true, force: true })\n }\n catch (err) {\n // Surface under ZIDANE_DEBUG; never throw. A leaked blob dir is\n // harmless (it can be `rm -rf`'d manually) — propagating the error\n // would block the SessionStore.delete() path the user is awaiting.\n if (process.env.ZIDANE_DEBUG)\n process.stderr.write(`[zidane/persistence] cleanup \"${persistRoot}\" failed: ${errorMessage(err)}\\n`)\n }\n}\n\n// ---------------------------------------------------------------------------\n// Internal helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Write-then-rename for atomicity. A concurrent reader (or a crash mid-\n * write) never observes a partial file: the rename is atomic on every\n * POSIX FS, and the destination is either the fully-written file or\n * absent. Creates the parent directory on demand so the caller doesn't\n * have to track which sessions have already initialized their dir.\n *\n * On write failure (disk full, permissions, signal), removes the `.tmp`\n * file so it doesn't accumulate as debris alongside successful blobs.\n * The unlink itself is best-effort — if we can't write to the dir we\n * likely can't unlink from it either, and the original error is what\n * the caller needs to see.\n *\n * Same idiom we use elsewhere (state-store snapshots, session export).\n */\nasync function writeAtomic(path: string, content: string): Promise<void> {\n await mkdir(dirname(path), { recursive: true })\n const tmp = `${path}.tmp`\n try {\n await writeFile(tmp, content, 'utf8')\n await rename(tmp, path)\n }\n catch (err) {\n await rm(tmp, { force: true }).catch(() => {})\n throw err\n }\n}\n\n/**\n * 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","/**\n * Tool argument validation against JSON Schema-style inputSchema.\n *\n * Two passes:\n * 1. Required-field presence. Missing or null/undefined required fields fail.\n * 2. Per-property type checks with **best-effort coercion**. Small/OSS models\n * routinely send `\"true\"` for a `boolean` field or `\"42\"` for a `number`,\n * and rejecting outright forces a confusing retry. Instead, we auto-heal\n * coerce when the conversion is unambiguous, fail only when the value\n * cannot be reasonably normalized to any of the declared types.\n *\n * Recursion: when a property declares `type: 'array'` with an `items` schema,\n * each item is validated against `items`. Object items are walked one level\n * deep (their declared `properties` get the same coercion + enum checks the\n * top level does). Items that can't be coerced are dropped rather than\n * rejecting the whole call — the model rarely benefits from an\n * all-or-nothing failure on a 20-item list because one entry was malformed.\n * Dropped items are reported back via `droppedItems` so the tool's `execute`\n * can surface a hint to the model if it wants to.\n */\n\nexport interface ValidationResult {\n valid: boolean\n /** Human-readable reason. Present on failure only. */\n error?: string\n /**\n * Possibly-coerced input. Present iff `valid: true`. Tools should call\n * `execute(coercedInput, ctx)` so auto-healed values reach the tool body.\n * When no coercion was applied, this is reference-equal to the input.\n */\n coercedInput?: Record<string, unknown>\n /**\n * Names of fields whose values were coerced. Empty when nothing changed.\n * Useful for telemetry (`validation:reject` on failure already carries the\n * reason; this is the success-path equivalent).\n */\n coercions?: readonly string[]\n /**\n * Indexes of array items dropped during recursive validation, keyed by\n * the property name. Empty / absent when nothing was dropped. Tools that\n * care about the discrepancy (e.g. `todowrite` wanting to surface\n * \"ignored 2 malformed items\") can inspect this.\n */\n droppedItems?: Readonly<Record<string, readonly number[]>>\n}\n\ninterface PropertySchema {\n type?: string | string[]\n enum?: unknown[]\n /** For `type: 'object'` — nested property schemas (used by array-item recursion). */\n properties?: Record<string, PropertySchema>\n /** For `type: 'object'` — required field names (used by array-item recursion). */\n required?: string[]\n /** For `type: 'array'` — schema applied to each item. */\n items?: PropertySchema\n /** For `type: 'array'` — maximum item count. Excess items are truncated. */\n maxItems?: number\n /** For `type: 'array'` — minimum item count. Falls below → validation error. */\n minItems?: number\n}\n\nconst TRUE_STRINGS = new Set(['true', 'True', 'TRUE', '1', 'yes', 'Yes', 'YES'])\nconst FALSE_STRINGS = new Set(['false', 'False', 'FALSE', '0', 'no', 'No', 'NO'])\n\nexport function validateToolArgs(\n input: Record<string, unknown>,\n schema: Record<string, unknown>,\n): ValidationResult {\n const required = (schema.required ?? []) as string[]\n const properties = (schema.properties ?? {}) as Record<string, PropertySchema>\n\n // Pass 1: required-field presence.\n for (const field of required) {\n if (!(field in input) || input[field] === undefined || input[field] === null) {\n return { valid: false, error: `Missing required field: ${field}` }\n }\n }\n\n // Pass 2: per-property type check with auto-coercion. Walk the input keys\n // (rather than schema properties) so unknown keys flow through unchanged.\n let coerced: Record<string, unknown> | undefined\n const coercions: string[] = []\n let droppedItems: Record<string, readonly number[]> | undefined\n\n for (const [key, value] of Object.entries(input)) {\n const propSchema = properties[key]\n if (!propSchema?.type)\n continue\n if (value === undefined || value === null)\n continue\n\n const outcome = coerceValue(value, propSchema)\n if (outcome.error) {\n return { valid: false, error: `Field \"${key}\": ${outcome.error}` }\n }\n if (outcome.changed) {\n if (!coerced)\n coerced = { ...input }\n coerced[key] = outcome.value\n coercions.push(key)\n }\n\n // Array recursion: validate each item against `propSchema.items` when\n // declared. Item-level failures drop the item rather than failing the\n // whole call — see file-level comment for rationale.\n const arrayValue = outcome.changed ? outcome.value : value\n if (propSchema.type === 'array' && propSchema.items && Array.isArray(arrayValue)) {\n const itemOutcome = validateArrayItems(arrayValue, propSchema)\n if (itemOutcome.error) {\n return { valid: false, error: `Field \"${key}\": ${itemOutcome.error}` }\n }\n if (itemOutcome.changed || itemOutcome.dropped.length > 0 || itemOutcome.truncated) {\n if (!coerced)\n coerced = { ...input }\n coerced[key] = itemOutcome.items\n // Coercion flag covers both per-item coercions and structural changes\n // (drops, truncation) so consumers wiring `validation:coerce` see a\n // single signal: \"the framework rewrote this field.\"\n if (!coercions.includes(key))\n coercions.push(key)\n }\n if (itemOutcome.dropped.length > 0) {\n if (!droppedItems)\n droppedItems = {}\n droppedItems[key] = itemOutcome.dropped\n }\n }\n }\n\n return {\n valid: true,\n coercedInput: coerced ?? input,\n coercions,\n ...(droppedItems ? { droppedItems } : {}),\n }\n}\n\ninterface ArrayItemsOutcome {\n items: unknown[]\n changed: boolean\n truncated: boolean\n dropped: number[]\n error?: string\n}\n\nfunction validateArrayItems(items: unknown[], schema: PropertySchema): ArrayItemsOutcome {\n if (schema.minItems !== undefined && items.length < schema.minItems) {\n return {\n items,\n changed: false,\n truncated: false,\n dropped: [],\n error: `expected at least ${schema.minItems} item${schema.minItems === 1 ? '' : 's'}, got ${items.length}`,\n }\n }\n\n const itemSchema = schema.items\n if (!itemSchema) {\n return { items, changed: false, truncated: false, dropped: [] }\n }\n\n const out: unknown[] = []\n // Parallel array — original input index of each entry in `out`. Used to\n // resolve truncation drops back to original indexes without re-walking.\n const outOriginalIdx: number[] = []\n const dropped: number[] = []\n let changed = false\n\n for (let i = 0; i < items.length; i++) {\n const item = items[i]\n const v = validateOneItem(item, itemSchema)\n if (v.dropped) {\n dropped.push(i)\n changed = true\n continue\n }\n if (v.changed)\n changed = true\n out.push(v.value)\n outOriginalIdx.push(i)\n }\n\n let truncated = false\n if (schema.maxItems !== undefined && out.length > schema.maxItems) {\n for (let i = schema.maxItems; i < out.length; i++)\n dropped.push(outOriginalIdx[i])\n out.length = schema.maxItems\n truncated = true\n changed = true\n }\n\n // Keep dropped sorted ascending — both branches preserve insertion order,\n // but truncation appends original indexes that may be < some validation\n // drops (no, actually drops happen first then truncation indexes are\n // strictly larger). Still cheap to sort defensively.\n dropped.sort((a, b) => a - b)\n\n return { items: out, changed, truncated, dropped }\n}\n\ninterface OneItemOutcome {\n value: unknown\n changed: boolean\n /** True when the item should be omitted from the output array. */\n dropped: boolean\n}\n\nfunction validateOneItem(item: unknown, schema: PropertySchema): OneItemOutcome {\n // Object items: one level of property coercion + required-field check.\n // We do NOT recurse infinitely — two levels of nesting is enough to cover\n // the structured tool inputs that exist today, and stopping here keeps the\n // validator predictable.\n if (schema.type === 'object') {\n if (!item || typeof item !== 'object' || Array.isArray(item))\n return { value: item, changed: false, dropped: true }\n\n const obj = item as Record<string, unknown>\n const required = schema.required ?? []\n for (const field of required) {\n const v = obj[field]\n if (v === undefined || v === null)\n return { value: item, changed: false, dropped: true }\n }\n\n const properties = schema.properties ?? {}\n let coercedItem: Record<string, unknown> | undefined\n for (const [key, value] of Object.entries(obj)) {\n const subSchema = properties[key]\n if (!subSchema?.type)\n continue\n if (value === undefined || value === null)\n continue\n const outcome = coerceValue(value, subSchema)\n if (outcome.error) {\n // Sub-property couldn't be coerced — drop the whole item.\n return { value: item, changed: false, dropped: true }\n }\n if (outcome.changed) {\n if (!coercedItem)\n coercedItem = { ...obj }\n coercedItem[key] = outcome.value\n }\n }\n return coercedItem\n ? { value: coercedItem, changed: true, dropped: false }\n : { value: item, changed: false, dropped: false }\n }\n\n // Scalar items: reuse the top-level coercer; drop on hard failure.\n if (schema.type) {\n const outcome = coerceValue(item, schema)\n if (outcome.error)\n return { value: item, changed: false, dropped: true }\n return { value: outcome.value, changed: outcome.changed, dropped: false }\n }\n\n return { value: item, changed: false, dropped: false }\n}\n\ninterface CoerceOutcome {\n /** Coerced value; equal to input when `changed: false`. */\n value: unknown\n changed: boolean\n error?: string\n}\n\nfunction coerceValue(value: unknown, schema: PropertySchema): CoerceOutcome {\n const declaredTypes = Array.isArray(schema.type)\n ? schema.type as string[]\n : [schema.type as string]\n\n // Already matches one of the declared types? No coercion, just enum-check.\n for (const t of declaredTypes) {\n if (matchesType(value, t)) {\n if (schema.enum && !schema.enum.includes(value)) {\n return {\n value,\n changed: false,\n error: `must be one of ${JSON.stringify(schema.enum)}, got ${formatValue(value)}`,\n }\n }\n return { value, changed: false }\n }\n }\n\n // Try coercion to each declared type in order; first success wins.\n for (const t of declaredTypes) {\n const coerced = tryCoerce(value, t)\n if (coerced.ok) {\n if (schema.enum && !schema.enum.includes(coerced.value)) {\n return {\n value,\n changed: false,\n error: `must be one of ${JSON.stringify(schema.enum)}, got ${formatValue(coerced.value)}`,\n }\n }\n return { value: coerced.value, changed: true }\n }\n }\n\n const expected = declaredTypes.join(' | ')\n return {\n value,\n changed: false,\n error: `expected ${expected}, got ${jsonType(value)} ${formatValue(value)}`,\n }\n}\n\nfunction matchesType(value: unknown, type: string): boolean {\n switch (type) {\n case 'string': return typeof value === 'string'\n case 'number': return typeof value === 'number' && Number.isFinite(value)\n case 'integer': return typeof value === 'number' && Number.isInteger(value)\n case 'boolean': return typeof value === 'boolean'\n case 'array': return Array.isArray(value)\n case 'object': return value !== null && typeof value === 'object' && !Array.isArray(value)\n case 'null': return value === null\n default: return true\n }\n}\n\nfunction tryCoerce(value: unknown, type: string): { ok: true, value: unknown } | { ok: false } {\n // string → other\n if (typeof value === 'string') {\n if (type === 'boolean') {\n const trimmed = value.trim()\n if (TRUE_STRINGS.has(trimmed))\n return { ok: true, value: true }\n if (FALSE_STRINGS.has(trimmed))\n return { ok: true, value: false }\n return { ok: false }\n }\n if (type === 'number') {\n const n = Number(value.trim())\n return Number.isFinite(n) ? { ok: true, value: n } : { ok: false }\n }\n if (type === 'integer') {\n const n = Number(value.trim())\n return Number.isInteger(n) ? { ok: true, value: n } : { ok: false }\n }\n if (type === 'array' || type === 'object') {\n try {\n const parsed = JSON.parse(value)\n if (type === 'array' && Array.isArray(parsed))\n return { ok: true, value: parsed }\n if (type === 'object' && parsed !== null && typeof parsed === 'object' && !Array.isArray(parsed))\n return { ok: true, value: parsed }\n return { ok: false }\n }\n catch {\n return { ok: false }\n }\n }\n if (type === 'null') {\n return value === '' || value === 'null' ? { ok: true, value: null } : { ok: false }\n }\n }\n\n // number → string / integer\n if (typeof value === 'number' && Number.isFinite(value)) {\n if (type === 'string')\n return { ok: true, value: String(value) }\n if (type === 'integer' && Number.isInteger(value))\n return { ok: true, value }\n }\n\n // boolean → string\n if (typeof value === 'boolean' && type === 'string')\n return { ok: true, value: String(value) }\n\n return { ok: false }\n}\n\nfunction jsonType(value: unknown): string {\n if (value === null)\n return 'null'\n if (Array.isArray(value))\n return 'array'\n return typeof value\n}\n\nfunction formatValue(value: unknown): string {\n let s: string\n try {\n s = JSON.stringify(value)\n }\n catch {\n s = String(value)\n }\n if (s === undefined)\n s = String(value)\n return s.length > 80 ? `${s.slice(0, 77)}...` : s\n}\n","/**\n * Agent turn execution loop.\n *\n * Handles streaming, tool execution (per-tool concurrency-safe\n * scheduling via `executeToolBatch`), steering injection, follow-up\n * messages, and abort.\n */\n\nimport type { Hookable } from 'hookable'\nimport type { AgentHooks } from './agent'\nimport type { AliasMaps } from './aliasing'\nimport type { ExecutionContext, ExecutionHandle } from './contexts'\nimport type { Provider, StreamOptions, ToolCall, ToolResult, ToolSpec } from './providers'\nimport type { Session } from './session'\nimport type { PairingRepair } from './session/messages'\nimport type { SkillsConfig } from './skills/types'\nimport type { ReadStateMap } from './tools/read-state'\nimport type { ToolContext, ToolDef } from './tools/types'\nimport type { AgentBehavior, AgentClock, AgentStats, McpServerConfig, RetryConfig, SessionMessage, SessionTurn, ThinkingLevel, ToolHookContext, ToolResultContent, TurnUsage } from './types'\nimport { rewriteContentToCanonical, rewriteMessagesToWire, toCanonicalName, toWireName } from './aliasing'\nimport { AgentAbortedError, AgentBudgetExceededError, AgentProviderError, AgentToolPairingError, errorMessage, toTypedError } from './errors'\nimport { maybePersistToolResult, PERSISTED_STUB_PREFIX } from './loop-persistence'\nimport { ensureEndsWithUserMessage, ensureToolResultPairing } from './session/messages'\nimport { readStateKey, resolveReadStateMap } from './tools/read-state'\nimport { validateToolArgs } from './tools/validation'\nimport { toolOutputByteLength } from './types'\n\nexport interface LoopContext {\n provider: Provider\n hooks: Hookable<AgentHooks>\n /** Agent display name — forwarded into `ToolContext.name`. */\n agentName?: string\n /** Agent default system prompt — forwarded into `ToolContext.system`. */\n agentSystem?: string\n /** Source agent tools (pre-MCP-merge) — forwarded into `ToolContext.tools`. */\n agentTools: Record<string, ToolDef>\n /** Agent tool aliases — forwarded into `ToolContext.toolAliases`. */\n agentToolAliases?: Record<string, string>\n /** Agent MCP servers — forwarded into `ToolContext.mcpServers`. */\n agentMcpServers?: McpServerConfig[]\n /** Agent skills config — forwarded into `ToolContext.skills`. */\n agentSkills?: SkillsConfig\n /** Agent behavior defaults — forwarded into `ToolContext.behavior`. */\n agentBehavior?: AgentBehavior\n tools: Record<string, ToolDef>\n formattedTools: unknown[]\n /**\n * Recompute the wire-level tool list from the current state of the agent's\n * `unlocked` set. Set when `behavior.toolDisclosure === 'lazy'` and at least\n * one MCP tool is lazy — every iteration the loop calls this so newly-\n * surfaced tools (via `tool_search`) advertise on the next provider request.\n * When `undefined`, the loop uses {@link LoopContext.formattedTools} verbatim.\n */\n rebuildFormattedTools?: () => unknown[]\n aliasMaps: AliasMaps\n model: string\n system: string\n thinking: ThinkingLevel\n /**\n * Hard cap on the number of tools in flight concurrently within a single\n * assistant turn. The scheduler dispatches concurrency-safe tools\n * (`ToolDef.isConcurrencySafe`) in parallel up to this cap; unsafe tools\n * act as barriers. `undefined` resolves to the framework default (10);\n * `<= 0` is clamped to `1` (effectively force-sequential).\n */\n maxConcurrentTools?: number\n signal: AbortSignal\n execution: ExecutionContext\n handle: ExecutionHandle\n steeringQueue: string[]\n followUpQueue: string[]\n turns: SessionTurn[]\n runId: string\n generateTurnId: () => string | Promise<string>\n /**\n * Time + UUID source for journaled metadata (turn `createdAt`, synthetic\n * ids). Resolved in `agent.run()` from per-run > agent-level > default\n * precedence. Live-only measurements (TTFT deltas, elapsed) keep\n * `Date.now()` directly.\n */\n clock: AgentClock\n /** Max loop iterations (default: 50) */\n maxTurns?: number\n /** 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 /** Max tokens per LLM turn (default: 16384) */\n maxTokens?: number\n /** Exact thinking token budget (overrides level-based default) */\n thinkingBudget?: number\n /** Enabled model-specific options (e.g. `{ fast: true }`). See `StreamOptions.modelOptions`. */\n modelOptions?: Record<string, boolean>\n /** JSON Schema for structured output enforcement */\n schema?: Record<string, unknown>\n /** Enable provider prompt caching (default: true). See `AgentBehavior.cache`. */\n cache?: boolean\n /** Soft cap on cumulative 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 /** 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 * 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 exceeds the cap, the loop ends the run with\n * `finishReason: 'pause'` instead of injecting another continue.\n *\n * Counter resets every turn that EMITS visible output (text, thinking,\n * or tool_calls) — only consecutive empty pauses count. Default in\n * `AgentBehavior.maxConsecutivePauseTurns` is `5`.\n */\n maxConsecutivePauseTurns?: number\n /** Provider name forwarded into `AgentToolPairingError` for strict-mode throws. */\n providerName?: string\n /**\n * Per-callId {@link AbortController} registry shared with the agent so an\n * external `agent.cancelTool(callId)` can flip a single in-flight tool's\n * signal without aborting the whole run. The loop registers a controller\n * for each call right before gate/execute and removes it in a `finally`\n * — so the map only ever holds *currently dispatching* calls.\n *\n * When undefined, per-call cancellation is unavailable; the loop still\n * honors `ctx.signal` (run / sibling abort) the same way.\n */\n pendingToolCancels?: Map<string, AbortController>\n}\n\nconst IMAGE_OMITTED_MARKER = '[image omitted — model does not support vision]'\n\n/**\n * Canonical tool_result text emitted when a tool call is interrupted by the\n * user mid-flight (Esc / Ctrl-C / external `AbortSignal`). Mirrors Claude\n * Code's `INTERRUPT_MESSAGE_FOR_TOOL_USE` so downstream consumers can pattern\n * match a single string across both harnesses. Always paired with\n * `isError: true` on the wire — the model treats it as a failed call rather\n * than a successful tool response.\n */\nexport const INTERRUPT_MESSAGE_FOR_TOOL_USE = '[Request interrupted by user for tool use]'\n\n/**\n * Canonical tool_result text emitted when a tool call is skipped because a\n * steering message arrived between dispatches inside\n * {@link executeToolBatch}. Distinguished from\n * {@link INTERRUPT_MESSAGE_FOR_TOOL_USE} so consumers can split \"user\n * cancelled\" from \"framework superseded\".\n */\nexport const TOOL_USE_SKIPPED_MESSAGE = '[Tool use skipped — superseded by user message]'\n\n/**\n * Canonical tool_result text emitted when a single tool call is cancelled\n * mid-flight via `agent.cancelTool(callId)` (typically the TUI's\n * \"cancel this tool\" affordance). Distinguished from\n * {@link INTERRUPT_MESSAGE_FOR_TOOL_USE} (run-wide user abort) and\n * {@link TOOL_USE_SKIPPED_MESSAGE} (steered) so the model — and downstream\n * consumers — can tell the three apart by string match.\n *\n * Always paired with `isError: true` on the wire so the model treats the\n * call as failed rather than as a successful response. The remaining tool\n * calls in the batch continue running, in contrast with a full-run abort.\n */\nexport const TOOL_USE_CANCELLED_MESSAGE = '[Tool call cancelled by user]'\n\n/**\n * Sentinel message rejected from the per-call cancellation promise inside\n * {@link executeSingleTool}'s race. Plain-string and module-private — only\n * the surrounding code reads it (via the `perCallAbort.signal.aborted`\n * guard, NOT the message itself), so it just needs to be a stable\n * non-empty identifier the rejection can carry.\n */\nconst CANCELLED_BY_USER_SENTINEL = 'zidane:tool:cancelled-by-user'\n\n/**\n * Compute the effective thinking budget for a given run-relative turn, given\n * the configured decay schedule. Pure helper — exported for tests and so\n * downstream tooling can preview decay curves without spinning up the loop.\n *\n * - No `baseBudget` → returns `undefined`. Decay never invents a budget.\n * - No `decay` → identity (`baseBudget`).\n * - Function form → `decay(turn, baseBudget)`. The return is clamped to\n * `[0, baseBudget]` so a buggy curve can't request *more* budget than the\n * caller explicitly opted into.\n * - Struct form `{ afterTurn, factor, floor }` → for `turn <= afterTurn`,\n * returns `baseBudget`. For `turn > afterTurn`, returns\n * `max(floor, baseBudget * factor^(turn - afterTurn))` clamped to\n * `[floor, baseBudget]`.\n *\n * Result is rounded to the nearest integer (token counts are integers).\n */\nexport function applyThinkingDecay(\n baseBudget: number | undefined,\n decay: AgentBehavior['thinkingDecay'] | undefined,\n turn: number,\n): number | undefined {\n if (typeof baseBudget !== 'number' || baseBudget <= 0)\n return baseBudget\n if (!decay)\n return baseBudget\n\n let raw: number\n if (typeof decay === 'function') {\n raw = decay(turn, baseBudget)\n }\n else {\n if (turn <= decay.afterTurn)\n return baseBudget\n const k = turn - decay.afterTurn\n raw = Math.max(decay.floor, baseBudget * decay.factor ** k)\n }\n\n // Clamp into a sane envelope. Decay reduces; never raises above the caller's\n // explicit budget. NaN collapses to 0 (the curve is broken — silent zero is\n // safer than letting NaN propagate into the provider request). Negative also\n // collapses to 0. `+Infinity` is fine — it clamps to baseBudget below.\n if (Number.isNaN(raw) || raw <= 0)\n return 0\n return Math.round(Math.min(baseBudget, raw))\n}\n\n/** Convert turns to the SessionMessage[] format expected by providers. */\nfunction turnsToMessages(turns: SessionTurn[]): SessionMessage[] {\n return turns\n .filter((t): t is SessionTurn & { role: 'user' | 'assistant' } => t.role !== 'system')\n .map(t => ({ role: t.role, content: t.content }))\n}\n\n/**\n * Wire-level cutoff for {@link SessionContentBlock}'s `compact-summary`\n * markers.\n *\n * Walks `turns` to find the latest marker block. When found:\n *\n * 1. Turns whose id appears in the marker's `replacesTurnIds` are\n * dropped from the wire-level list (the model sees the summary\n * instead of those turns).\n * 2. The marker turn itself is replaced with a synthesized plain\n * user-text turn whose text is the summary — providers don't know\n * about the `compact-summary` block type, so we always normalize\n * before they see it.\n * 3. The synthetic marker is positioned at the slot of the **first\n * replaced turn**, so the summary appears CHRONOLOGICALLY where\n * the content it summarizes used to live. This keeps the wire-\n * level conversation in correct narrative order:\n * - older unreplaced turns (PTL head-drops) come first\n * - then the synthesized summary\n * - then later unreplaced turns (preserved tail + new prompts)\n * 4. Every other turn is passed through by reference. This includes:\n * - turns BEFORE the marker that aren't in `replacesTurnIds`\n * (e.g. head turns dropped by `compactConversation`'s PTL retry\n * path — the harness keeps them visible to the model verbatim\n * because the summary doesn't describe them)\n * - turns AFTER the marker (preserved tail + any new prompts the\n * user appended after compaction)\n *\n * If no marker exists, the input array is returned unchanged.\n *\n * Only the latest `compact-summary` block is honored — sessions\n * compacted twice cascade only if the newer marker's `replacesTurnIds`\n * explicitly subsumes the older marker's id (which the runner does\n * when re-compacting because its `turnsToMessages` inlines older\n * markers into the summary scope).\n *\n * Note on role-alternation: when the marker is immediately followed by\n * a new user turn (the common case — user submits a prompt right after\n * compacting), the wire output ends in `[…, synthetic_user, new_user]`.\n * Anthropic folds adjacent same-role messages server-side; other\n * providers tolerate them via their own normalization passes. The\n * harness does NOT fold them here — keeping them separate preserves\n * the per-turn `turnId` tags that the TUI's select-turn mode relies on.\n *\n * Pure: a fresh array is returned only when a marker is found. Input\n * turns are never mutated; the synthetic marker is the only new\n * `SessionTurn` allocated.\n */\nexport function applyCompactSummaryCutoff(turns: SessionTurn[]): SessionTurn[] {\n if (turns.length === 0)\n return turns\n\n // Find the latest marker turn (scan backward — only the most-recent\n // compaction boundary is honored).\n let markerIdx = -1\n let markerSummary = ''\n let markerReplacesIds: readonly string[] = []\n for (let i = turns.length - 1; i >= 0; i--) {\n const block = turns[i].content.find(b => b.type === 'compact-summary')\n if (block && block.type === 'compact-summary') {\n markerIdx = i\n markerSummary = block.summary\n markerReplacesIds = block.replacesTurnIds\n break\n }\n }\n if (markerIdx < 0)\n return turns\n\n const markerTurn = turns[markerIdx]\n const synthetic: SessionTurn = {\n id: markerTurn.id,\n role: 'user',\n content: [{ type: 'text', text: markerSummary }],\n createdAt: markerTurn.createdAt,\n ...(markerTurn.runId ? { runId: markerTurn.runId } : {}),\n }\n\n const replacedSet = new Set(markerReplacesIds)\n const out: SessionTurn[] = []\n let syntheticInserted = false\n\n for (let i = 0; i < turns.length; i++) {\n if (i === markerIdx) {\n // The marker turn never passes through verbatim — its rewritten\n // form (the `synthetic` turn) is inserted at the first-replaced\n // slot below. Skipping here ensures we never emit it twice.\n continue\n }\n if (replacedSet.has(turns[i].id)) {\n // First replaced turn becomes the synthetic's anchor — that's\n // where the summary lands chronologically.\n if (!syntheticInserted) {\n out.push(synthetic)\n syntheticInserted = true\n }\n continue\n }\n out.push(turns[i])\n }\n\n // Defensive fallback: marker has empty `replacesTurnIds` (or every id\n // pointed at turns that don't exist). The summary is a no-op boundary\n // but we still want it to appear at the marker's original chronological\n // slot. Count how many non-elided turns came before the marker and\n // splice the synthetic in at that offset so the narrative order holds.\n if (!syntheticInserted) {\n let insertAt = 0\n for (let i = 0; i < markerIdx; i++) {\n if (!replacedSet.has(turns[i].id))\n insertAt++\n }\n out.splice(insertAt, 0, synthetic)\n }\n\n return out\n}\n\n/**\n * Flatten image blocks inside previously-stored `tool_result` outputs to text\n * markers when the provider reports no vision capability. This handles the\n * session-resume / mid-session-provider-switch case:\n *\n * - A session was produced with a vision-capable provider (e.g. Claude) and\n * contains structured tool_result content with images.\n * - The agent resumes with a text-only provider (e.g. Cerebras).\n * - Without this pass, the persisted image blocks would re-encode via the\n * provider's wire format and leak base64 payloads into a non-vision model's\n * context.\n *\n * Scope: stored tool_result outputs only. User-authored image blocks are left\n * alone — those are consumer-controlled and the consumer is responsible for\n * matching prompt parts to the provider's capabilities.\n */\nconst COMPACTION_STUB = '[…elided by client-side tail compaction; ask the user or re-run the tool to retrieve.]'\n\n/**\n * Tail-compaction for non-Anthropic providers: when the cumulative byte size\n * of `tool_result` content across the wire-level message list exceeds\n * `threshold`, replace older `tool_result` outputs with a short stub. The\n * newest `keepTurns` messages (user/assistant alike) are left untouched so\n * the model retains the freshest tool context.\n *\n * Only `tool_result` blocks are touched — text and image blocks pass through\n * unchanged. Mutates a shallow-cloned message array; original `messages` is\n * not modified.\n *\n * For Anthropic users, prefer the server-side `context-management-2025-06-27`\n * beta (token-accurate, no client-side approximation). This function is the\n * client-side fallback for OpenAI-compatible / OpenRouter / Cerebras runs\n * against OSS models that lack a server-side equivalent.\n */\nexport function applyTailCompaction(\n messages: SessionMessage[],\n threshold: number,\n keepTurns: number,\n): SessionMessage[] {\n if (messages.length === 0)\n return messages\n\n let totalBytes = 0\n for (const msg of messages) {\n for (const block of msg.content) {\n if (block.type === 'tool_result')\n totalBytes += toolOutputByteLength(block.output)\n }\n }\n if (totalBytes <= threshold)\n return messages\n\n // Index range eligible for elision: everything before the trailing\n // `keepTurns` messages. Negative result means there's nothing old enough\n // to elide — bail out.\n const keep = Math.max(0, keepTurns)\n const cutoff = messages.length - keep\n if (cutoff <= 0)\n return messages\n\n let changed = false\n const out = messages.slice()\n for (let i = 0; i < cutoff; i++) {\n const msg = out[i]\n let msgChanged = false\n const newContent = msg.content.map((block) => {\n if (block.type !== 'tool_result')\n return block\n // Already a short string — nothing to compact.\n const existingBytes = toolOutputByteLength(block.output)\n if (existingBytes <= COMPACTION_STUB.length)\n return block\n // Already a disk-persisted stub (~2 KiB carrying preview + path).\n // Replacing it with COMPACTION_STUB would drop the path attribute,\n // stranding the on-disk blob: still on disk until session delete,\n // but no longer reachable from the conversation. Saving ~1.5 KiB\n // by overwriting isn't worth losing the reference.\n if (typeof block.output === 'string' && block.output.startsWith(PERSISTED_STUB_PREFIX))\n return block\n msgChanged = true\n changed = true\n return { ...block, output: COMPACTION_STUB }\n })\n if (msgChanged)\n out[i] = { ...msg, content: newContent }\n }\n return changed ? out : messages\n}\n\n/**\n * Replace `read_file` `tool_result` blocks with a short stub when a later\n * successful `edit` / `multi_edit` / `write_file` modified the same path.\n *\n * Eliminates the common waste pattern where the model carries the pre-edit\n * file body forward across many turns. Operates on the wire-level message\n * list only — the persisted session keeps the original content.\n *\n * Detection is conservative: success is gated on the corresponding\n * tool_result starting with `Edited ` (edit / multi_edit) or `Created ` /\n * `Updated ` (write_file). Failed edits and `No change needed` writes do\n * NOT invalidate prior reads — the file content is still what the read\n * returned.\n *\n * Pure function, exported for tests and so downstream tooling can preview\n * elision without spinning up the loop.\n */\nexport const STALE_READ_STUB = '[…elided: file edited later in this run; re-read if still needed.]'\n\nexport interface StaleReadElisionResult {\n messages: SessionMessage[]\n /**\n * Paths whose reads were stubbed this pass. The caller invalidates the\n * matching read-state entries so the next `read_file` misses its own\n * dedup (which would otherwise return the \"unchanged\" replay stub — see\n * `src/tools/read-file.ts`) and emits fresh content; the next edit's\n * read-before-edit guard fails so the model re-reads before re-editing.\n * Without this, both stubs combine to leave the model with zero visible\n * content for a path it has both read and edited.\n */\n elidedPaths: string[]\n}\n\nexport function applyStaleReadElision(messages: SessionMessage[]): StaleReadElisionResult {\n if (messages.length === 0)\n return { messages, elidedPaths: [] }\n\n // Map call_id → tool_result string (for success-marker checks).\n const resultByCallId = new Map<string, string>()\n for (const msg of messages) {\n for (const block of msg.content) {\n if (block.type === 'tool_result' && typeof block.output === 'string')\n resultByCallId.set(block.callId, block.output)\n }\n }\n\n // For each path, the highest message index where a successful mutation\n // landed. `read_file` tool_results whose corresponding read happened\n // BEFORE that index (for the same path) are stale.\n const maxMutationIdxByPath = new Map<string, number>()\n // call_id → { path, msgIdx } for read_file calls.\n const readCallInfo = new Map<string, { path: string, msgIdx: number }>()\n\n for (let i = 0; i < messages.length; i++) {\n for (const block of messages[i].content) {\n if (block.type !== 'tool_call')\n continue\n const path = (block.input as { path?: unknown }).path\n if (typeof path !== 'string')\n continue\n\n if (block.name === 'read_file') {\n readCallInfo.set(block.id, { path, msgIdx: i })\n continue\n }\n\n const isEdit = block.name === 'edit' || block.name === 'multi_edit'\n const isWrite = block.name === 'write_file'\n if (!isEdit && !isWrite)\n continue\n\n const result = resultByCallId.get(block.id)\n if (typeof result !== 'string')\n continue\n\n // Success markers — see edit.ts (`Edited ${target}: replaced …`),\n // multi-edit.ts (`Edited ${target}: applied …`), write-file.ts\n // (`Created … (N bytes)` / `Updated … (N bytes)`). The TUI's\n // `tool:transform` hook may append an `<edit-outcomes>` annotation\n // block to multi_edit results, but the prefix is preserved.\n const succeeded = isEdit\n ? result.startsWith('Edited ')\n : (result.startsWith('Created ') || result.startsWith('Updated '))\n if (!succeeded)\n continue\n\n const prior = maxMutationIdxByPath.get(path)\n if (prior === undefined || i > prior)\n maxMutationIdxByPath.set(path, i)\n }\n }\n\n if (maxMutationIdxByPath.size === 0)\n return { messages, elidedPaths: [] }\n\n // call_ids whose read happened before that path's last successful mutation.\n const staleCallIds = new Set<string>()\n const elidedPathSet = new Set<string>()\n for (const [callId, info] of readCallInfo) {\n const lastMutationIdx = maxMutationIdxByPath.get(info.path)\n if (typeof lastMutationIdx === 'number' && info.msgIdx < lastMutationIdx) {\n staleCallIds.add(callId)\n elidedPathSet.add(info.path)\n }\n }\n\n if (staleCallIds.size === 0)\n return { messages, elidedPaths: [] }\n\n // Paths that ALSO have a non-stale read (i.e. a read whose msgIdx\n // is at or after the path's last mutation). These shouldn't be\n // invalidated by the caller: the fresh read already populated the\n // read-state entry with the post-mutation hash, and invalidating\n // would nuke it — making the next edit's `requireReadBeforeEdit`\n // gate fire \"has not been read\" on a file the model demonstrably\n // re-read after the previous edit. The wire-level stub for the\n // stale read still rides; only the in-memory tracking is kept.\n const pathsWithFreshRead = new Set<string>()\n for (const [callId, info] of readCallInfo) {\n if (!staleCallIds.has(callId))\n pathsWithFreshRead.add(info.path)\n }\n\n let changed = false\n const out = messages.slice()\n for (let i = 0; i < out.length; i++) {\n const msg = out[i]\n let msgChanged = false\n const newContent = msg.content.map((block) => {\n if (block.type !== 'tool_result' || !staleCallIds.has(block.callId))\n return block\n // Idempotent: a second pass over an already-elided message must not\n // bump `changed`, otherwise the function would always allocate a new\n // array (no-op-but-not-stable). Match the exact stub string.\n if (block.output === STALE_READ_STUB)\n return block\n // Mirror of the `applyTailCompaction` guard at the top of this\n // file: a `<persisted-output …>` stub carries the path to the\n // on-disk blob. Replacing it with `STALE_READ_STUB` would drop\n // the path attribute, stranding the blob (still on disk until\n // session delete, but no longer reachable from the conversation).\n // The model already gets the staleness signal via the next\n // `read_file` call missing the read-state dedup; preserving the\n // stub keeps the persistence pointer intact.\n if (typeof block.output === 'string' && block.output.startsWith(PERSISTED_STUB_PREFIX))\n return block\n // Structured outputs (image reads via `read_file`) are intentionally\n // also collapsed to the string stub: the bytes ARE stale once the\n // path was overwritten, the marker explicitly tells the model to\n // re-read, and `output: string | ToolResultContent[]` accepts a\n // string. Downstream image-flattening for non-vision providers is\n // a no-op on strings.\n msgChanged = true\n changed = true\n return { ...block, output: STALE_READ_STUB }\n })\n if (msgChanged)\n out[i] = { ...msg, content: newContent }\n }\n return {\n messages: changed ? out : messages,\n elidedPaths: [...elidedPathSet].filter(p => !pathsWithFreshRead.has(p)),\n }\n}\n\n/**\n * Drop read-state entries for paths whose reads got elided. Keys are\n * canonical absolute paths produced by `readStateKey(cwd, path)` (see\n * `src/tools/read-state.ts`); the loop has `ctx.handle.cwd` in scope,\n * so we re-derive each elided path's canonical key and delete by\n * direct lookup. No suffix matching, no cross-cwd ambiguity.\n *\n * Resolves the map via `resolveReadStateMap` so a child agent running\n * with a parent's shared map (via `shareReadState`) invalidates the\n * shared entries too — otherwise a child's `read → edit → re-read`\n * would dedup-hit on the now-stale parent entry.\n */\nfunction invalidateReadStateForElidedPaths(\n ctx: { session?: Session, readState?: ReadStateMap },\n cwd: string,\n elidedPaths: readonly string[],\n): void {\n if (elidedPaths.length === 0)\n return\n const readState = resolveReadStateMap(ctx)\n if (!readState || readState.size === 0)\n return\n for (const p of elidedPaths)\n readState.delete(readStateKey(cwd, p))\n}\n\n/**\n * Run {@link ensureToolResultPairing} with the loop's hook + strict-mode\n * context plugged in. Centralized so the pre-send path and the schema-\n * enforcement path share identical telemetry + throw semantics.\n *\n * The captured `repairs` array is what `AgentToolPairingError` carries on\n * strict-mode throws, and it's what `pairing:repair` fires from. Hook\n * notifications run AFTER the pass completes so they don't interfere with\n * the synchronous walk — and we drop them on the floor in strict mode (the\n * throw is more informative than a fire-and-forget log).\n */\nfunction applyPairingRepair(\n ctx: LoopContext,\n messages: SessionMessage[],\n turnId: string,\n): SessionMessage[] {\n const repairs: PairingRepair[] = []\n const repaired = ensureToolResultPairing(messages, {\n onRepair: repair => repairs.push(repair),\n })\n\n if (repairs.length === 0)\n return repaired\n\n if (ctx.strictToolPairing) {\n throw new AgentToolPairingError({\n message:\n `Tool pairing corruption detected (${repairs.length} repair${repairs.length === 1 ? '' : 's'}); `\n + `strict mode is on so the request was not sent.`,\n ...(ctx.providerName ? { provider: ctx.providerName } : {}),\n repairs,\n })\n }\n\n for (const repair of repairs) {\n void ctx.hooks.callHook('pairing:repair', { ...repair, turnId })\n }\n\n return repaired\n}\n\nfunction sanitizeStoredToolResults(\n provider: Provider,\n messages: SessionMessage[],\n): SessionMessage[] {\n if (provider.meta.capabilities?.vision !== false)\n return messages\n\n return messages.map((msg) => {\n let changed = false\n const newContent = msg.content.map((block) => {\n if (block.type !== 'tool_result' || typeof block.output === 'string')\n return block\n changed = true\n const flattened = block.output\n .map(b => b.type === 'image' ? IMAGE_OMITTED_MARKER : b.text)\n .join('\\n')\n return { ...block, output: flattened }\n })\n return changed ? { ...msg, content: newContent } : msg\n })\n}\n\nexport async function runLoop(ctx: LoopContext): Promise<AgentStats> {\n let totalIn = 0\n let totalOut = 0\n let totalCacheRead = 0\n let totalCacheCreation = 0\n const turnUsages: TurnUsage[] = []\n const startTime = Date.now()\n // Default to no cap — runs are bounded by `result.ended` and the abort\n // signal. Callers wanting a runaway-loop safety net set `behavior.maxTurns`.\n const maxTurns = ctx.maxTurns ?? Number.POSITIVE_INFINITY\n let turnsCompleted = 0\n // 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 EXCEEDS `maxConsecutivePauseTurns` (default 5), the loop\n // ends the run with the last turn's `finishReason: 'pause'` instead of\n // injecting another \"Please continue.\" Without this, a model returning\n // empty pauses indefinitely (observed on some 4.6 streaming edge cases)\n // spins forever burning tokens — the host's only escape was an external\n // abort. Cap was added because Claude Code's v2 SDK collapses pause_turn\n // to stop outright; zidane's recovery is more forgiving but needs a\n // finite budget.\n const pauseCap = ctx.maxConsecutivePauseTurns ?? 5\n let consecutiveEmptyPauseTurns = 0\n\n // Track time-to-first-token across the entire run. Earliest of the first\n // stream:text, stream:thinking, or tool:before event latches the value.\n const ttft = { mark: undefined as number | undefined }\n const markTtft = () => {\n if (ttft.mark === undefined)\n ttft.mark = Date.now() - ctx.runStartMs\n }\n const unregisterTtftText = ctx.hooks.hook('stream:text', markTtft)\n const unregisterTtftThinking = ctx.hooks.hook('stream:thinking', markTtft)\n const unregisterTtftTool = ctx.hooks.hook('tool:before', markTtft)\n\n try {\n for (let turn = 0; turn < maxTurns; turn++) {\n if (ctx.signal.aborted) {\n await ctx.hooks.callHook('agent:abort', {})\n break\n }\n\n // Pass the run-cumulative usage state into the turn so its\n // `turn:after` hook can carry `cumulativeUsage` (priorUsage + this\n // turn's usage) without consumers re-accumulating. Includes the\n // current turn count (1-indexed including this one) so dashboards\n // can plot per-turn deltas without an external counter.\n const result = await executeTurn(ctx, turn, {\n input: totalIn,\n output: totalOut,\n cacheRead: totalCacheRead,\n cacheCreation: totalCacheCreation,\n priorCost: turnUsages.reduce((s, t) => s + (t.cost ?? 0), 0),\n priorTurns: turnsCompleted,\n })\n turnsCompleted = turn + 1\n\n totalIn += result.usage.input\n totalOut += result.usage.output\n totalCacheRead += result.usage.cacheRead ?? 0\n totalCacheCreation += result.usage.cacheCreation ?? 0\n turnUsages.push(result.usage)\n\n await ctx.hooks.callHook('usage', { turn, turnId: result.turnId, usage: result.usage, totalIn, totalOut })\n\n // Pause-turn cap. Increment on consecutive empty pauses, reset on\n // any productive turn. The check is post-`usage` so the offending\n // turn's tokens are billed back to the run AND the `usage` hook\n // fires for that turn before the loop exits — telemetry consumers\n // never see a missing-usage event for an in-`turnUsages` entry.\n // When the cap trips, we `break` cleanly: the last entry in\n // `turnUsages` carries `finishReason: 'pause'`, so consumers can\n // detect pause exhaustion from `stats` alone. We do NOT fire\n // `agent:abort` here — this is graceful loop termination, not a\n // user-driven cancel, and host code keyed on `agent:abort` would\n // misclassify it as such.\n if (result.pauseEmpty) {\n consecutiveEmptyPauseTurns += 1\n if (pauseCap > 0 && consecutiveEmptyPauseTurns >= pauseCap)\n break\n }\n else {\n consecutiveEmptyPauseTurns = 0\n }\n\n // Check abort after turn completes. This also catches a repeat-guard\n // abort: the guard's `tool:gate` handler called `abortController.abort()`\n // when a tracked tool's consecutive-identical streak hit its threshold,\n // which flips `ctx.signal.aborted`. A `repeat-guard:exceeded` event with\n // `action: 'abort'` fired first, so consumers can distinguish it from a\n // user-driven cancel.\n if (ctx.signal.aborted) {\n await ctx.hooks.callHook('agent:abort', {})\n break\n }\n\n // Run-level budget circuit breaker. Post-turn so the breaching\n // turn's usage is visible to consumers via `turn:after` + `usage`\n // hooks before the throw lands — a host can log the spend that\n // tripped the cap and then handle the typed error without scraping\n // a stats blob from a half-built `AgentStats`.\n //\n // Token ledger here sums `input + output` only. Cache reads/creates\n // are billed at a discount; including them at par would shift the\n // semantic away from \"what does this run cost in tokens\" toward\n // \"how much context did we shovel through\", which is rarely the\n // operator's actual concern. Cost path uses provider-reported\n // numbers directly so the discount is already baked in.\n const breach = checkRunBudget({\n maxCostUsd: ctx.maxCostUsd,\n maxTotalTokens: ctx.maxTotalTokens,\n cost: turnUsages.reduce((s, t) => s + (t.cost ?? 0), 0),\n tokens: totalIn + totalOut,\n })\n if (breach) {\n throw new AgentBudgetExceededError(breach)\n }\n\n // Check steering queue after tool execution\n if (ctx.steeringQueue.length > 0) {\n const steerMsg = ctx.steeringQueue.shift()!\n await ctx.hooks.callHook('steer:inject', { message: steerMsg })\n const steerUserMsg = ctx.provider.userMessage(steerMsg)\n ctx.turns.push({\n id: await ctx.generateTurnId(),\n runId: ctx.runId,\n role: steerUserMsg.role,\n content: steerUserMsg.content,\n createdAt: await ctx.clock.now(),\n })\n continue\n }\n\n if (result.ended) {\n // Check follow-up queue before finishing\n if (ctx.followUpQueue.length > 0) {\n const followUp = ctx.followUpQueue.shift()!\n await ctx.hooks.callHook('steer:inject', { message: followUp })\n const followUpMsg = ctx.provider.userMessage(followUp)\n ctx.turns.push({\n id: await ctx.generateTurnId(),\n runId: ctx.runId,\n role: followUpMsg.role,\n content: followUpMsg.content,\n createdAt: await ctx.clock.now(),\n })\n continue\n }\n\n return {\n totalIn,\n totalOut,\n totalCacheRead,\n totalCacheCreation,\n turns: turn + 1,\n elapsed: Date.now() - startTime,\n turnUsage: turnUsages,\n output: result.output,\n ...(ttft.mark !== undefined ? { timeTillFirstTokenMs: ttft.mark } : {}),\n }\n }\n }\n\n return {\n totalIn,\n totalOut,\n totalCacheRead,\n totalCacheCreation,\n turns: turnsCompleted,\n elapsed: Date.now() - startTime,\n turnUsage: turnUsages,\n ...(ttft.mark !== undefined ? { timeTillFirstTokenMs: ttft.mark } : {}),\n }\n }\n finally {\n unregisterTtftText()\n unregisterTtftThinking()\n unregisterTtftTool()\n }\n}\n\n// ---------------------------------------------------------------------------\n// Single turn\n// ---------------------------------------------------------------------------\n\ninterface TurnResult {\n ended: boolean\n turnId: string\n usage: TurnUsage\n output?: Record<string, unknown>\n /**\n * Set to `'empty-pause'` when the turn produced no tool calls, no text,\n * and the provider stop reason was `pause_turn` — the case the\n * \"Please continue.\" recovery exists for. `runLoop` uses this signal to\n * count consecutive empty pauses and abort with `finishReason: 'pause'`\n * after `maxConsecutivePauseTurns` to prevent infinite pause loops on\n * Anthropic 4.6+ streaming edge cases.\n *\n * `undefined` when the turn produced ANY visible output (tool calls,\n * text, or thinking) — including a non-empty pause turn (the loop\n * counter resets in that case).\n */\n pauseEmpty?: true\n}\n\n/**\n * Wrap a caught provider error in the matching typed error class.\n *\n * Uses the provider's `classifyError` seam when implemented; otherwise falls back\n * to wrapping in `AgentProviderError`. Abort signals always produce `AgentAbortedError`\n * regardless of the provider classification.\n */\n/**\n * Pure predicate for the run-level budget circuit breaker. Returns a\n * structured breach descriptor when EITHER the cost or token ledger has\n * crossed its configured ceiling, otherwise `null`.\n *\n * Order of checks: cost first when both are set, because cost is the\n * direct dollar accounting operators set ceilings against — emitting a\n * `'tokens'` breach when both knobs would have tripped on the same turn\n * would hide the more user-actionable signal.\n *\n * `undefined` / `0` / non-positive / non-finite ceilings disable the\n * corresponding axis without protest, matching the established\n * `behavior.toolOutputBudget` semantic. Exported only for tests; the\n * loop is the sole production caller.\n */\nexport function checkRunBudget(input: {\n maxCostUsd?: number\n maxTotalTokens?: number\n cost: number\n tokens: number\n}): { limit: 'cost' | 'tokens', limitValue: number, actualValue: number } | null {\n const { maxCostUsd, maxTotalTokens, cost, tokens } = input\n if (typeof maxCostUsd === 'number' && Number.isFinite(maxCostUsd) && maxCostUsd > 0 && cost >= maxCostUsd) {\n return { limit: 'cost', limitValue: maxCostUsd, actualValue: cost }\n }\n if (typeof maxTotalTokens === 'number' && Number.isFinite(maxTotalTokens) && maxTotalTokens > 0 && tokens >= maxTotalTokens) {\n return { limit: 'tokens', limitValue: maxTotalTokens, actualValue: tokens }\n }\n return null\n}\n\nfunction wrapProviderError(err: unknown, ctx: LoopContext): Error {\n if (ctx.signal.aborted || (err instanceof Error && err.name === 'AbortError'))\n return new AgentAbortedError('Agent run aborted', { cause: err })\n\n const classification = ctx.provider.classifyError?.(err)\n if (classification)\n return toTypedError(classification, ctx.provider.name, err)\n\n return new AgentProviderError(errorMessage(err), { provider: ctx.provider.name, cause: err })\n}\n\n/** Max bytes of provider error text inlined into the assistant turn placeholder. */\nconst ERROR_PLACEHOLDER_MAX = 280\n\n/**\n * Build the assistant-turn placeholder text when the provider throws before\n * streaming any output. Inlines the underlying error message (truncated and\n * stripped of stack-like newlines) so the persisted turn carries a useful\n * diagnostic — the human reading the transcript sees the failure mode\n * without having to attach a debugger, and `tool_search` schema rejections\n * become self-explanatory.\n *\n * The bracketed `[✗ Streaming failed: ...]` shape preserves the prior\n * format that hosts may pattern-match on while adding the new payload\n * suffix. Falls back to the original generic placeholder when no message\n * can be extracted.\n */\nfunction buildStreamErrorPlaceholder(err: unknown): string {\n const raw = errorMessage(err).trim()\n if (raw.length === 0)\n return '[✗ Streaming failed before any output.]'\n // Collapse newlines so the placeholder stays on one transcript line; a\n // multi-line provider stack trace would otherwise wreck the model's\n // continuation context.\n const oneLine = raw.replace(/\\s+/g, ' ')\n const trimmed = oneLine.length > ERROR_PLACEHOLDER_MAX\n ? `${oneLine.slice(0, ERROR_PLACEHOLDER_MAX - 1).trimEnd()}…`\n : oneLine\n return `[✗ Streaming failed before any output: ${trimmed}]`\n}\n\ninterface RunUsageSnapshot {\n input: number\n output: number\n cacheRead: number\n cacheCreation: number\n priorCost: number\n priorTurns: number\n}\n\nfunction buildCumulativeUsage(prior: RunUsageSnapshot, turnUsage: TurnUsage): Readonly<{\n input: number\n output: number\n cacheRead: number\n cacheCreation: number\n cost?: number\n turns: number\n}> {\n const cost = prior.priorCost + (turnUsage.cost ?? 0)\n return Object.freeze({\n input: prior.input + turnUsage.input,\n output: prior.output + turnUsage.output,\n cacheRead: prior.cacheRead + (turnUsage.cacheRead ?? 0),\n cacheCreation: prior.cacheCreation + (turnUsage.cacheCreation ?? 0),\n ...(cost > 0 ? { cost } : {}),\n turns: prior.priorTurns + 1,\n })\n}\n\n/**\n * Best-effort extraction of `statusCode` / `requestId` from native provider\n * SDK exceptions, attached to `stream:error` ctx so observability handlers\n * don't have to walk `cause` chains. Recognized shapes:\n *\n * - Anthropic SDK `APIError` (`status`, `headers['request-id']`)\n * - OpenAI SDK error (`status`, `headers['x-request-id']`)\n * - OpenRouter / OpenAI-compat HTTP errors (`status`)\n * - Cerebras (`status`)\n *\n * Silent no-op for arbitrary `Error`s and primitives — the fields stay\n * undefined and the raw `err` is still forwarded for handlers that need\n * full context.\n */\nfunction extractStreamErrorMeta(err: unknown): { statusCode?: number, requestId?: string } {\n if (!err || typeof err !== 'object')\n return {}\n const e = err as Record<string, unknown>\n const out: { statusCode?: number, requestId?: string } = {}\n\n const statusCandidate = typeof e.status === 'number'\n ? e.status\n : typeof e.statusCode === 'number'\n ? e.statusCode\n : undefined\n if (typeof statusCandidate === 'number' && Number.isFinite(statusCandidate))\n out.statusCode = statusCandidate\n\n if (typeof e.requestId === 'string') {\n out.requestId = e.requestId\n }\n else {\n const headers = e.headers\n if (headers && typeof headers === 'object') {\n const h = headers as Record<string, unknown>\n const candidate = h['request-id'] ?? h['x-request-id'] ?? h.requestId\n if (typeof candidate === 'string')\n out.requestId = candidate\n }\n }\n return out\n}\n\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\ninterface ResolvedRetryConfig {\n maxAttempts: number\n initialDelayMs: number\n maxDelayMs: number\n}\n\n/**\n * Normalize a user-supplied `RetryConfig` (any field may be missing or invalid)\n * into a fully-populated config the retry loop can use without per-field guards.\n *\n * Invalid values (non-finite, < 1 for `maxAttempts`, < 0 for delays) fall back\n * to defaults rather than throwing — this is a behavior knob, not a validation\n * boundary, and a bad value here shouldn't crash a run.\n */\nfunction resolveRetryConfig(cfg: RetryConfig | undefined): ResolvedRetryConfig {\n const maxAttempts = Number.isFinite(cfg?.maxAttempts) && (cfg!.maxAttempts as number) >= 1\n ? Math.floor(cfg!.maxAttempts as number)\n : RETRY_DEFAULTS.maxAttempts\n const initialDelayMs = Number.isFinite(cfg?.initialDelayMs) && (cfg!.initialDelayMs as number) >= 0\n ? (cfg!.initialDelayMs as number)\n : RETRY_DEFAULTS.initialDelayMs\n const maxDelayMs = Number.isFinite(cfg?.maxDelayMs) && (cfg!.maxDelayMs as number) >= 0\n ? (cfg!.maxDelayMs as number)\n : RETRY_DEFAULTS.maxDelayMs\n return { maxAttempts, initialDelayMs, maxDelayMs }\n}\n\n/**\n * Compute the backoff delay for the upcoming retry.\n *\n * Strategy:\n * - If the server returned `retry-after` / `retry-after-ms`, honor it (capped at `maxDelayMs`).\n * - Otherwise: `initialDelayMs * 2^(attempt-1)`, capped at `maxDelayMs`, then \"full jitter\"\n * (`Math.random() * computed`). Full jitter scatters retries from concurrent clients\n * across the whole window — empirically better than fixed exponential under thundering-herd\n * (per AWS Architecture Blog's analysis of EBO+jitter strategies).\n *\n * `attempt` is the 1-indexed number of the attempt that just *failed*, so the first\n * post-failure delay uses `attempt=1` → `initialDelayMs * 1` before jitter.\n */\nfunction computeRetryDelayMs(\n attempt: number,\n cfg: ResolvedRetryConfig,\n retryAfterMs: number | undefined,\n): number {\n if (retryAfterMs !== undefined && retryAfterMs >= 0)\n return Math.min(retryAfterMs, cfg.maxDelayMs)\n const exponential = cfg.initialDelayMs * 2 ** (attempt - 1)\n const capped = Math.min(exponential, cfg.maxDelayMs)\n return Math.floor(Math.random() * capped)\n}\n\n/**\n * Extract a `retry-after` value (in milliseconds) from an error's `.headers`, if any.\n *\n * Supports both `retry-after-ms` (non-standard but Stainless-emitted; already in ms,\n * always integer) and the standard `retry-after` header (integer seconds, RFC 7231).\n * Returns `undefined` when neither is present, non-numeric, or negative.\n *\n * `parseInt` over `parseFloat` here matches the actual contract — Stainless emits\n * `retry-after-ms` as an integer and RFC 7231 specifies a non-negative integer for\n * the seconds form. The HTTP-date form of `retry-after` is deliberately unsupported\n * (Anthropic + OpenAI both emit numeric seconds; supporting dates invites clock-skew\n * concerns for negligible benefit).\n */\nfunction extractRetryAfterMs(err: unknown): number | undefined {\n if (!err || typeof err !== 'object')\n return undefined\n const headers = (err as { headers?: unknown }).headers\n if (!headers || typeof headers !== 'object')\n return undefined\n // Support both `Map`/`Headers`-like with `.get()` and plain objects.\n const get = (key: string): string | undefined => {\n const h = headers as { get?: (k: string) => string | null | undefined } & Record<string, unknown>\n if (typeof h.get === 'function') {\n const v = h.get(key)\n return typeof v === 'string' ? v : undefined\n }\n const v = h[key]\n return typeof v === 'string' ? v : undefined\n }\n const ms = get('retry-after-ms')\n if (ms !== undefined) {\n const parsed = Number.parseInt(ms, 10)\n if (Number.isFinite(parsed) && parsed >= 0)\n return parsed\n }\n const seconds = get('retry-after')\n if (seconds !== undefined) {\n const parsed = Number.parseInt(seconds, 10)\n if (Number.isFinite(parsed) && parsed >= 0)\n return parsed * 1000\n }\n return undefined\n}\n\n/**\n * `setTimeout` that rejects if the given signal aborts before the timer fires.\n *\n * Resolves with `undefined` on normal completion. Rejects with the signal's\n * `reason` (or a generic AbortError when no reason is set) on abort. Cleans\n * up its `abort` listener in both paths so callers don't leak handlers.\n */\nfunction abortableSleep(ms: number, signal: AbortSignal): Promise<void> {\n if (signal.aborted)\n return Promise.reject(signal.reason ?? new DOMException('Aborted', 'AbortError'))\n return new Promise((resolve, reject) => {\n let timer: ReturnType<typeof setTimeout>\n const onAbort = () => {\n clearTimeout(timer)\n signal.removeEventListener('abort', onAbort)\n reject(signal.reason ?? new DOMException('Aborted', 'AbortError'))\n }\n timer = setTimeout(() => {\n signal.removeEventListener('abort', onAbort)\n resolve()\n }, ms)\n signal.addEventListener('abort', onAbort, { once: true })\n })\n}\n\nasync function executeTurn(\n ctx: LoopContext,\n turn: number,\n priorUsage: RunUsageSnapshot,\n): Promise<TurnResult> {\n // Generate turn ID early so streaming hooks have access to it\n const turnId = await ctx.generateTurnId()\n\n // Build provider-bound messages: apply outbound alias rewrite so the LLM sees\n // tool_call blocks with their wire (aliased) names. Canonical names stay in ctx.turns.\n //\n // Order of operations is load-bearing:\n // 1. `applyCompactSummaryCutoff` first — turns before the latest\n // `compact-summary` marker never reach the provider, so every\n // transform below sees a smaller, marker-free slice. Operating on\n // `SessionTurn[]` lets the cutoff identify the marker block type\n // without leaking the type into the wire-level path.\n // 2. `turnsToMessages` second — converts the post-cutoff turns into\n // the `SessionMessage[]` shape providers expect.\n // 3. `applyStaleReadElision` walks tool_calls + tool_results to\n // replace pre-edit `read_file` outputs. Runs BEFORE alias rewrite\n // so the function never has to learn about the alias map.\n // 4. Alias rewrite, image sanitization, tail compaction follow.\n const turnsAfterCutoff = applyCompactSummaryCutoff(ctx.turns)\n let canonicalMessages = turnsToMessages(turnsAfterCutoff)\n\n if (ctx.elideStaleReads === true) {\n const elision = applyStaleReadElision(canonicalMessages)\n canonicalMessages = elision.messages\n // Force the next read_file / edit on the elided paths to start from\n // fresh content — the model can no longer see the original read in\n // the message history, so the cheap dedup stub would mislead it.\n invalidateReadStateForElidedPaths(ctx, ctx.handle.cwd, elision.elidedPaths)\n }\n\n const wireMessages = rewriteMessagesToWire(canonicalMessages, ctx.aliasMaps)\n // Flatten any stored structured tool_result images to text markers when the\n // provider is non-vision. No-op on vision-capable providers and on sessions\n // without structured outputs. See `sanitizeStoredToolResults` for scope.\n let sanitizedMessages = sanitizeStoredToolResults(ctx.provider, wireMessages)\n\n // Client-side 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 sanitizedMessages = applyTailCompaction(sanitizedMessages, threshold, keep)\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 let totalBytes = 0\n for (const msg of sanitizedMessages) {\n for (const block of msg.content) {\n if (block.type === 'tool_result')\n totalBytes += toolOutputByteLength(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 // unsanitized payload. The pair-repair pass downstream still has\n // a chance to fix any structural damage; we surface the error\n // via `stream:error` (after the request is built) so hosts can\n // log it without losing the in-flight model response.\n console.error('[zidane] compactStrategy function threw:', err)\n }\n }\n\n const effectiveThinkingBudget = applyThinkingDecay(ctx.thinkingBudget, ctx.thinkingDecay, turn)\n\n // Lazy tool disclosure — when active, rebuild the tool list each turn so\n // tools surfaced through `tool_search` since the last provider call land\n // in this request. The set only grows within a run (entries are appended,\n // never reordered or removed), so the prefix-cache breakpoint advances\n // monotonically rather than thrashing.\n const formattedTools = ctx.rebuildFormattedTools\n ? ctx.rebuildFormattedTools()\n : ctx.formattedTools\n\n const streamOptions: StreamOptions = {\n model: ctx.model,\n system: ctx.system,\n tools: formattedTools,\n messages: sanitizedMessages,\n maxTokens: ctx.maxTokens ?? 16384,\n thinking: ctx.thinking,\n thinkingBudget: effectiveThinkingBudget,\n ...(ctx.modelOptions ? { modelOptions: ctx.modelOptions } : {}),\n cache: ctx.cache ?? true,\n signal: ctx.signal,\n }\n\n // Context transform hook — lets consumers prune/modify messages before LLM call\n // ctx.messages here is the wire-level view (aliases applied) so transformations are\n // consistent with what the provider actually sees.\n const transformCtx = { messages: streamOptions.messages }\n await ctx.hooks.callHook('context:transform', transformCtx)\n streamOptions.messages = transformCtx.messages\n\n // Last line of defense before the wire: repair any `tool_use` ↔\n // `tool_result` adjacency violations. Anthropic 400s on orphans loudly\n // (`'tool_use' ids were found without 'tool_result' blocks immediately\n // after`, `tool_result must be preceded by a tool_call with the same\n // toolCallId`); OpenAI is more forgiving but still rejects most\n // mismatches. Common sources: interrupted runs persisted mid-pair, the\n // schema-enforcement `__output__` tail, `context:transform` hooks that\n // prune messages without preserving pairing, duplicate tool_use ids\n // surfaced by buggy providers, compaction passes that strand assistant\n // half of a pair.\n //\n // Strict mode (`behavior.strictToolPairing`) collects every repair and\n // throws `AgentToolPairingError` instead of patching the wire — for\n // training-data collectors that need to reject corrupted transcripts\n // rather than ship synthetic placeholders.\n streamOptions.messages = applyPairingRepair(ctx, streamOptions.messages, turnId)\n\n // Final defense: guarantee the wire ends with a user message. Pair-repair\n // mode 2 already appends a synthetic `tool_result` user turn for orphan\n // `tool_use`s, but a *plain* assistant text tail (no tool_use blocks)\n // slips through — typically when a `context:transform` hook trims the\n // last user turn. Models that don't support assistant prefill (opus 4.7,\n // o-series translation layers) 400 with \"This model does not support\n // assistant message prefill. The conversation must end with a user\n // message.\" Idempotent on a healthy tail.\n streamOptions.messages = ensureEndsWithUserMessage(streamOptions.messages, ctx.provider)\n\n // System transform hook — runtime-derived system-prompt sections. Fires\n // after `context:transform` so handlers can branch on the wire-level\n // message list. Cache breakpoints are applied inside the provider after\n // this point, so mutations land in the cache key naturally.\n const systemCtx: {\n system: string\n messages: readonly SessionMessage[]\n turn: number\n turnId: string\n session?: Session\n } = {\n system: streamOptions.system,\n messages: streamOptions.messages,\n turn,\n turnId,\n ...(ctx.session ? { session: ctx.session } : {}),\n }\n await ctx.hooks.callHook('system:transform', systemCtx)\n streamOptions.system = systemCtx.system\n\n await ctx.hooks.callHook('turn:before', { turn, turnId, options: streamOptions })\n\n // 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 = 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 ctx.hooks.callHook('stream:start', { turnId, startedAt: streamStartedAt })\n\n const retryCfg = resolveRetryConfig(ctx.retry)\n let result\n let attempt = 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 ctx.provider.stream(\n streamOptions,\n {\n onText(delta) {\n markTurnTtft()\n currentText += delta\n ctx.hooks.callHook('stream:text', { delta, text: currentText, turnId })\n },\n onThinking(delta) {\n markTurnTtft()\n currentThinking += delta\n ctx.hooks.callHook('stream:thinking', { delta, thinking: currentThinking, turnId })\n },\n onServerToolUse({ id, name, input }) {\n ctx.hooks.callHook('stream:server_tool_use', { id, name, input, turnId })\n },\n onServerToolResult({ toolUseId, toolName, content }) {\n 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 )\n break\n }\n catch (caught) {\n // `terminalErr` is what eventually feeds the placeholder/`stream:error`/\n // `wrapProviderError` path. Starts as the caught error; gets replaced by\n // a synthesized AbortError when `agent.abort()` lands mid-backoff so the\n // existing abort handling (`AgentAbortedError`, abort placeholder) kicks in.\n let terminalErr: unknown = caught\n let wasAborted = ctx.signal.aborted || (caught instanceof Error && caught.name === 'AbortError')\n const classified = !wasAborted ? ctx.provider.classifyError?.(caught) : null\n const isRetryable = classified?.kind === 'provider_error' && classified.retryable === true\n // No partial output yet — safe to discard the half-built stream and re-issue.\n const cleanSlate = currentText === '' && currentThinking === ''\n const canRetry = !wasAborted && isRetryable && cleanSlate && attempt < retryCfg.maxAttempts\n\n if (canRetry) {\n const meta = extractStreamErrorMeta(caught)\n const delayMs = computeRetryDelayMs(attempt, retryCfg, extractRetryAfterMs(caught))\n await 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 // 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 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. Aborts are routed through\n // `agent:abort` elsewhere — don't double-fire here.\n if (!wasAborted) {\n const meta = extractStreamErrorMeta(terminalErr)\n await ctx.hooks.callHook('stream:error', { err: terminalErr, turnId, ...meta })\n }\n await ctx.hooks.callHook('turn:after', {\n turn,\n turnId,\n usage: errorUsage,\n message: errorTurn,\n toolCounts: { turn: Object.freeze({}), run: Object.freeze({ ...ctx.runToolCounts }) },\n cumulativeUsage: buildCumulativeUsage(priorUsage, errorUsage),\n })\n throw wrapProviderError(terminalErr, ctx)\n }\n }\n\n if (currentText) {\n await ctx.hooks.callHook('stream:end', { text: currentText, turnId })\n }\n\n // Tool-only turns (no text / no thinking deltas) still need a TTFT\n // number — the stream returned silently with just tool_use blocks. Use\n // the wall-clock duration of `provider.stream()` as the proxy. Tool\n // dispatch fires `tool:before` later, but that's post-stream and would\n // double-count provider latency.\n if (turnTtftMs === undefined && result.toolCalls.length > 0)\n turnTtftMs = Date.now() - streamStartedAt\n\n // Inbound alias → canonical rewrite. After this, tool calls and assistant\n // content blocks use canonical names everywhere downstream (turns, dispatch, hooks).\n const canonicalToolCalls = result.toolCalls.map(tc => ({\n ...tc,\n name: toCanonicalName(tc.name, ctx.aliasMaps),\n }))\n const canonicalContent = rewriteContentToCanonical(\n result.assistantMessage?.content ?? [],\n ctx.aliasMaps,\n )\n\n // Stamp per-turn TTFT onto the usage record so the assistant turn\n // carries it through session persistence (downstream consumers reading\n // `SessionTurn.usage` for postmortem analysis get it without re-running\n // the loop) and the hook ctx exposes it for metrics histograms.\n if (turnTtftMs !== undefined && result.usage.timeToFirstTokenMs === undefined)\n result.usage.timeToFirstTokenMs = turnTtftMs\n\n // Build the assistant turn and push BEFORE firing turn:after\n const assistantTurn: SessionTurn = {\n id: turnId,\n runId: ctx.runId,\n role: 'assistant',\n content: result.done\n ? (canonicalContent.length > 0 ? canonicalContent : [{ type: 'text', text: currentText }])\n : canonicalContent,\n usage: result.usage,\n createdAt: await ctx.clock.now(),\n }\n ctx.turns.push(assistantTurn)\n\n // Per-turn tool counts: how many of each tool the model emitted in this\n // assistant turn. Counts emissions, not dispatches — `turn:after` fires\n // before tool dispatch, so blocked calls are counted here too. For \"actually\n // dispatched\" cumulative numbers, use `toolCounts.run`.\n const turnCounts: Record<string, number> = {}\n for (const tc of canonicalToolCalls)\n turnCounts[tc.name] = (turnCounts[tc.name] ?? 0) + 1\n\n await ctx.hooks.callHook('turn:after', {\n turn,\n turnId,\n usage: result.usage,\n message: assistantTurn,\n toolCounts: { turn: Object.freeze(turnCounts), run: Object.freeze({ ...ctx.runToolCounts }) },\n cumulativeUsage: buildCumulativeUsage(priorUsage, result.usage),\n })\n\n if (result.done) {\n // Schema enforcement: force one more call with a synthetic output tool\n if (ctx.schema && !ctx.signal.aborted) {\n const outputSpec: ToolSpec = {\n name: '__output__',\n description: 'Return the final structured output matching the required schema.',\n inputSchema: ctx.schema,\n }\n // Apply the same compact-summary cutoff as the main path — without\n // this, a session compacted before schema enforcement would leak\n // the `compact-summary` block type to the provider. Same pairing\n // repair too, so the synthetic `__output__` tool_call doesn't 400\n // on its own (the schema turn legitimately has no matching\n // tool_result; mode 2 inserts one).\n // Same two-pass defense the main wire-send uses (see above): pair-\n // repair fills orphan `tool_use` adjacency; ensureEndsWithUserMessage\n // covers the plain assistant-text-tail case (`result.done` after a\n // text-only assistant turn lands here with an assistant-terminated\n // history — pairing repair can't fix it because pairing is already\n // valid).\n const schemaMessages = ensureEndsWithUserMessage(\n applyPairingRepair(\n ctx,\n rewriteMessagesToWire(\n turnsToMessages(applyCompactSummaryCutoff(ctx.turns)),\n ctx.aliasMaps,\n ),\n turnId,\n ),\n ctx.provider,\n )\n let schemaResult\n try {\n schemaResult = await ctx.provider.stream(\n {\n model: ctx.model,\n system: ctx.system,\n tools: ctx.provider.formatTools([outputSpec]),\n messages: schemaMessages,\n maxTokens: ctx.maxTokens ?? 16384,\n signal: ctx.signal,\n toolChoice: { type: 'tool', name: '__output__' },\n },\n {\n onText: () => {},\n onOAuthRefresh(refreshCtx) {\n return ctx.hooks.callHook('oauth:refresh', refreshCtx)\n },\n },\n )\n }\n catch (err) {\n throw wrapProviderError(err, ctx)\n }\n\n const output = schemaResult.toolCalls.find(tc => tc.name === '__output__')?.input\n\n if (output) {\n await ctx.hooks.callHook('output', { output, schema: ctx.schema })\n }\n\n const schemaTurn: SessionTurn = {\n id: await ctx.generateTurnId(),\n runId: ctx.runId,\n role: 'assistant',\n content: schemaResult.assistantMessage.content,\n usage: schemaResult.usage,\n createdAt: await ctx.clock.now(),\n }\n ctx.turns.push(schemaTurn)\n\n return {\n ended: true,\n turnId,\n usage: {\n input: result.usage.input + schemaResult.usage.input,\n output: result.usage.output + schemaResult.usage.output,\n },\n output,\n }\n }\n\n return { ended: true, turnId, usage: result.usage }\n }\n\n // Pause-turn recovery (Anthropic 4.6+ `pause_turn`): the model stopped\n // mid-turn for a server-side pause but no tools were requested. Push a\n // synthetic continue prompt as a user message so the next turn resumes\n // with valid (non-empty) input — Anthropic rejects empty user content.\n // Gated on `finishReason === 'pause'` to avoid masking provider bugs that\n // legitimately produce zero tool calls and zero text.\n //\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: await ctx.generateTurnId(),\n runId: ctx.runId,\n role: toolResultMsg.role,\n content: toolResultMsg.content,\n createdAt: await ctx.clock.now(),\n }\n ctx.turns.push(toolResultsTurn)\n\n // Fire `tool-results:after` so persistence layers can durably store the\n // tool_use ↔ tool_result pair together — closes the crash window between\n // `turn:after` (assistant turn persisted) and the next loop iteration's\n // `turn:after` (where these tool results would otherwise piggyback). A\n // process death in that window left the DB with an orphan tool_use that\n // Anthropic rejects on resume.\n await ctx.hooks.callHook('tool-results:after', {\n turn,\n turnId,\n message: toolResultsTurn,\n results: toolResults,\n })\n\n // Enforce per-turn tool-output budget. Sum the post-transform bytes of every\n // tool result; on overshoot, push a synthetic user message that nudges the\n // model toward summarization, and surface the event via `budget:exceeded`.\n //\n // Tools listed in `toolOutputBudgetExcludeTools` are skipped entirely:\n // `tool_search` / `skills_use` exist to LOAD context into the conversation,\n // so counting their bytes against the budget would steer the model away\n // from the very call it needs to make progress (the \"summarize before\n // calling more tools\" message is nonsensical for schema-loading tools).\n // Lookup is canonical-name keyed because `canonicalToolCalls` was already\n // alias-rewritten upstream; map result id → canonical name so the filter\n // works for parallel batches where order in `toolResults` is dispatch\n // order, not call order.\n if (typeof ctx.toolOutputBudget === 'number' && ctx.toolOutputBudget > 0) {\n const excludeSet = ctx.toolOutputBudgetExcludeTools && ctx.toolOutputBudgetExcludeTools.length > 0\n ? new Set(ctx.toolOutputBudgetExcludeTools)\n : undefined\n const nameById = excludeSet\n ? new Map(canonicalToolCalls.map(c => [c.id, c.name]))\n : undefined\n const totalBytes = toolResults.reduce(\n (sum, r) => {\n if (excludeSet && nameById) {\n const name = nameById.get(r.id)\n if (name !== undefined && excludeSet.has(name))\n return sum\n }\n return sum + toolOutputByteLength(r.content)\n },\n 0,\n )\n if (totalBytes > ctx.toolOutputBudget) {\n const warning = `[Tool output budget exceeded: ${totalBytes} bytes returned in this turn (cap: ${ctx.toolOutputBudget}). Summarize the salient findings before calling more tools.]`\n const userMsg = ctx.provider.userMessage(warning)\n ctx.turns.push({\n id: await ctx.generateTurnId(),\n runId: ctx.runId,\n role: userMsg.role,\n content: userMsg.content,\n createdAt: await ctx.clock.now(),\n })\n await ctx.hooks.callHook('budget:exceeded', {\n turn,\n turnId,\n bytes: totalBytes,\n budget: ctx.toolOutputBudget,\n })\n }\n }\n\n return { ended: false, turnId, usage: result.usage }\n}\n\n// ---------------------------------------------------------------------------\n// Tool execution\n// ---------------------------------------------------------------------------\n\n/**\n * Strip image blocks from a tool output when the provider is not vision-capable.\n * Each image is replaced with a short text marker so the model sees an honest\n * \"no image\" signal instead of JSON-stringified base64 it might confabulate over.\n *\n * Returns the output unchanged when:\n * - The provider reports `capabilities.vision: true` (native routing handles it),\n * - The provider omits `capabilities` entirely (we default to vision-capable\n * so third-party providers without the field aren't penalized),\n * - The output is a plain string (no image blocks to strip).\n *\n * With the current `text | image` union, replacing every image with a text marker\n * leaves an all-text array — collapse to a plain string to keep the downstream wire\n * shape as narrow as possible.\n */\nfunction stripImagesForNonVision(\n provider: Provider,\n output: string | ToolResultContent[],\n): string | ToolResultContent[] {\n if (typeof output === 'string')\n return output\n if (provider.meta.capabilities?.vision !== false)\n return output\n\n return output\n .map(b => b.type === 'image' ? IMAGE_OMITTED_MARKER : b.text)\n .join('\\n')\n}\n\n/**\n * Build the per-call base for every `tool:*` hook ctx (and the\n * matching shape for `mcp:tool:*`). Centralized so the `runId` /\n * `parentRunId` / `depth` identity fields land uniformly on every\n * event the loop fires — without one helper they drift across ~14\n * inline construction sites. The returned object IS the\n * {@link ToolHookContext} canonical shape; specialized hook payloads\n * (`gateCtx`, `transformCtx`, etc.) spread it and append their own\n * fields.\n */\nfunction buildToolHookBase(\n ctx: LoopContext,\n turnId: string,\n callId: string,\n name: string,\n displayName: string,\n input: Record<string, unknown>,\n): ToolHookContext {\n return {\n turnId,\n callId,\n name,\n displayName,\n input,\n ...(ctx.runId !== undefined ? { runId: ctx.runId } : {}),\n ...(ctx.parentRunId !== undefined ? { parentRunId: ctx.parentRunId } : {}),\n ...(typeof ctx.depth === 'number' ? { depth: ctx.depth } : {}),\n }\n}\n\nasync function executeSingleTool(\n ctx: LoopContext,\n call: ToolCall,\n turnId: string,\n): Promise<{ result: ToolResult }> {\n const toolDef = ctx.tools[call.name]\n const callId = call.id\n const displayName = toWireName(call.name, ctx.aliasMaps)\n\n // Frozen pre-call snapshot of run-cumulative tool counts. Shared across the\n // gate, tool:before, and tool:after hooks so consumers see a consistent view\n // for the lifecycle of this call. A consumer that wants the post-increment\n // count can add 1 to the entry for `ctx.name`.\n const runToolCounts: Readonly<Record<string, number>> = Object.freeze({ ...ctx.runToolCounts })\n\n // Per-call cancellation slot. Registered ASAP (before the gate) so a user\n // pressing \"cancel this tool\" between `tool:before` and `tool:after` finds\n // a live controller to flip. The `finally` below always removes the entry,\n // so the map only ever holds *currently dispatching* calls — never grows\n // unbounded across a run.\n //\n // The race against this controller's signal lives down in the execute\n // section; gate-blocked / substituted / unknown-tool / invalid-input paths\n // return early without ever reaching it, which is fine — those exits are\n // immediate and the user has nothing to cancel.\n const perCallAbort = new AbortController()\n ctx.pendingToolCancels?.set(callId, perCallAbort)\n try {\n return await runSingleToolDispatch(ctx, call, turnId, {\n toolDef,\n callId,\n displayName,\n runToolCounts,\n perCallAbort,\n })\n }\n finally {\n // Best-effort remove: a concurrent unregister (the agent dropping its\n // map on destroy) is a no-op.\n ctx.pendingToolCancels?.delete(callId)\n }\n}\n\n/**\n * Body of {@link executeSingleTool}. Hoisted into its own function purely so\n * the per-call cancel registration (which spans every exit path) can sit in\n * a tight `try / finally` at the call site. Behavior is unchanged from the\n * pre-cancel implementation aside from the user-cancel branch in the\n * execute body — see {@link TOOL_USE_CANCELLED_MESSAGE}.\n */\nasync function runSingleToolDispatch(\n ctx: LoopContext,\n call: ToolCall,\n turnId: string,\n fixed: {\n toolDef: ToolDef | undefined\n callId: string\n displayName: string\n runToolCounts: Readonly<Record<string, number>>\n perCallAbort: AbortController\n },\n): Promise<{ result: ToolResult }> {\n const { toolDef, callId, displayName, runToolCounts, perCallAbort } = fixed\n\n // Gate hook — handlers can `block` (refuse the call), substitute a `result`\n // (skip execute, send the substitute back as a normal tool_result), or do\n // nothing (tool runs as usual).\n const gateCtx: ToolHookContext & {\n block: boolean\n reason: string\n result?: string | ToolResultContent[]\n runToolCounts: Readonly<Record<string, number>>\n } = {\n ...buildToolHookBase(ctx, turnId, callId, call.name, displayName, call.input),\n block: false,\n reason: 'Tool execution was blocked',\n runToolCounts,\n }\n await ctx.hooks.callHook('tool:gate', gateCtx)\n\n // Conflict resolution: block wins over result. This handles two cases\n // cleanly without forcing every middleware to defensively coordinate:\n //\n // - A consumer hook substitutes a `result` (e.g. dedup cache), then a\n // policy gate (skills allowed-tools, custom security) refuses the call\n // via `block`. The refusal must take precedence — security beats\n // convenience.\n // - A buggy single handler sets both. The call gets blocked; the spurious\n // `result` is dropped silently. The buggy code path can be caught with a\n // tool:gate observability hook downstream of the framework gates.\n if (gateCtx.block) {\n // Blocked calls do not count — the model \"asked\" but the framework refused\n // before any side effect could happen. Treating them as charged would make\n // budget guards self-defeating (block on N triggers Nth call to count).\n //\n // `tool:dispatched` fires with `outcome: 'gate-block'` so consumers\n // wanting symmetric per-call notification (chat-layer transcript\n // rebuilders, downstream message-history consumers) see every refused\n // call. `tool:after` / `tool:transform` deliberately do NOT fire here:\n // preserving their pre-existing \"tool body produced a result\" contract\n // keeps the TUI, tracing-span lifecycle, and budget-byte counters from\n // having to special-case blocked calls.\n //\n // `isError: true` on the returned tool_result so the model treats the\n // refusal as a failed attempt rather than a successful `Blocked` string.\n await fireDispatched(ctx, {\n turnId,\n callId,\n name: call.name,\n displayName,\n input: gateCtx.input,\n outcome: 'gate-block',\n reason: gateCtx.reason,\n runToolCounts,\n })\n return { result: { id: callId, content: `Blocked: ${gateCtx.reason}`, isError: true } }\n }\n\n // The call passed gate; record it in the run counter. Counted once here so\n // `result`-substituted calls still count — the model emitted the call.\n ctx.runToolCounts[call.name] = (ctx.runToolCounts[call.name] ?? 0) + 1\n\n // Z20 substitute path. Skip validate / `tool:before` / execute and emit a\n // synthetic successful `tool_result`. `tool:after` and `tool:transform`\n // still fire so byte-budgeting, telemetry, and post-mutation hooks see\n // the substitute consistently with executed calls. `tool:transform` may\n // still flip the `isError` flag on its way through (e.g. a hook that\n // demotes a cached error reply to a graceful retry hint).\n if (gateCtx.result !== undefined) {\n await fireDispatched(ctx, {\n turnId,\n callId,\n name: call.name,\n displayName,\n input: gateCtx.input,\n outcome: 'gate-substitute',\n runToolCounts,\n })\n const emitted = await emitToolResult(ctx, {\n turnId,\n callId,\n name: call.name,\n displayName,\n input: gateCtx.input,\n output: gateCtx.result,\n isError: false,\n runToolCounts,\n })\n return {\n result: {\n id: callId,\n content: emitted.output,\n ...(emitted.isError ? { isError: true } : {}),\n },\n }\n }\n\n // Input that downstream hooks + execute see, after any tool:gate mutation.\n let effectiveInput = gateCtx.input\n\n if (!toolDef) {\n // Hallucinated tool name (model invented it) or dangling reference (MCP\n // server dropped, alias removed). Give consumers a chance to substitute a\n // friendly response or suppress the companion `tool:error` so the trace\n // doesn't carry a noisy \"Unknown tool\" message back to the model.\n //\n // Fire `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`. Now the pair\n // is `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 await ctx.hooks.callHook('tool:before', {\n ...buildToolHookBase(ctx, turnId, callId, call.name, displayName, effectiveInput),\n runToolCounts,\n unknown: true,\n })\n\n const unknownCtx: ToolHookContext & {\n result?: string | ToolResultContent[]\n suppressError: boolean\n } = {\n ...buildToolHookBase(ctx, turnId, callId, call.name, displayName, effectiveInput),\n suppressError: false,\n }\n await ctx.hooks.callHook('tool:unknown', unknownCtx)\n\n const content = unknownCtx.result ?? `Tool error: Unknown tool: ${call.name}`\n // Treat unknown-tool as an error iff the consumer didn't substitute a\n // successful result via `tool:unknown` (which is the substitute-and-\n // recover path). A non-substituted unknown tool always carries\n // `Tool error: …` text and should ride the `is_error` channel so the\n // model sees the wire-level error flag.\n const isError = unknownCtx.result === undefined\n\n if (!unknownCtx.suppressError) {\n const err = new Error(`Unknown tool: ${call.name}`)\n await ctx.hooks.callHook('tool:error', {\n ...buildToolHookBase(ctx, turnId, callId, call.name, displayName, effectiveInput),\n error: err,\n })\n }\n // `tool:dispatched` fires for symmetry; `tool:after` / `tool:transform`\n // do NOT — see the comment in the gate-block branch above for the\n // backward-compat rationale.\n await fireDispatched(ctx, {\n turnId,\n callId,\n name: call.name,\n displayName,\n input: effectiveInput,\n outcome: 'unknown',\n runToolCounts,\n })\n return { result: { id: callId, content, ...(isError ? { isError: true } : {}) } }\n }\n\n // Validate arguments (respect mutations from tool:gate hook). Auto-coerces\n // string→boolean/number/etc. for OSS models that don't always honor types.\n const validation = validateToolArgs(effectiveInput, toolDef.spec.inputSchema)\n if (!validation.valid) {\n await ctx.hooks.callHook('validation:reject', {\n ...buildToolHookBase(ctx, turnId, callId, call.name, displayName, effectiveInput),\n reason: validation.error ?? 'invalid input',\n schema: toolDef.spec.inputSchema,\n })\n // Symmetric `tool:dispatched`; narrow `tool:after` semantics preserved.\n await fireDispatched(ctx, {\n turnId,\n callId,\n name: call.name,\n displayName,\n input: effectiveInput,\n outcome: 'invalid-input',\n runToolCounts,\n })\n return { result: { id: callId, content: `Validation error: ${validation.error}`, isError: true } }\n }\n // Pass the coerced input to the tool — `\"true\"` is now `true`, etc.\n effectiveInput = validation.coercedInput ?? effectiveInput\n\n // Surface successful coercions so consumers can count \"model wrongness rate\".\n // Only fires when at least one field was coerced; perfectly-typed inputs stay\n // silent so the hook bus isn't noisy on the happy path.\n const coercions = validation.coercions && validation.coercions.length > 0\n ? validation.coercions\n : undefined\n if (coercions) {\n await ctx.hooks.callHook('validation:coerce', {\n ...buildToolHookBase(ctx, turnId, callId, call.name, displayName, effectiveInput),\n coercions,\n schema: toolDef.spec.inputSchema,\n })\n }\n\n await fireDispatched(ctx, {\n turnId,\n callId,\n name: call.name,\n displayName,\n input: effectiveInput,\n outcome: 'execute',\n runToolCounts,\n })\n await ctx.hooks.callHook('tool:before', {\n ...buildToolHookBase(ctx, turnId, callId, call.name, displayName, effectiveInput),\n runToolCounts,\n ...(coercions ? { coercions } : {}),\n })\n\n // `output` is set in the success branch and in the regular-error branch\n // of the catch; the user-cancel branch returns early *above* the\n // post-execute emit so it never reaches the `output` read. Initialising\n // to a never-used sentinel keeps TS happy without disabling strict mode.\n let output: string | ToolResultContent[] = ''\n let isError = false\n let cancelledByUser = false\n\n // Per-call abort composes with the run-scoped / sibling-scoped `ctx.signal`\n // so the tool body sees one combined signal. The body still gets aborted\n // when the run / sibling fleet aborts; the new case the union enables is\n // a user-issued cancel of *this specific call* without unwinding the rest\n // of the batch. Falls back to `ctx.signal` if `AbortSignal.any` is\n // unavailable (Node < 20.3 / Bun < 0.6) — we lose per-call cancellation\n // but the body's existing observation of `ctx.signal` keeps working.\n const childSignal: AbortSignal = typeof AbortSignal.any === 'function'\n ? AbortSignal.any([ctx.signal, perCallAbort.signal])\n : ctx.signal\n\n try {\n const toolCtx: ToolContext = {\n provider: ctx.provider,\n signal: childSignal,\n execution: ctx.execution,\n handle: ctx.handle,\n hooks: ctx.hooks,\n tools: ctx.agentTools,\n ...(ctx.agentName !== undefined ? { name: ctx.agentName } : {}),\n ...(ctx.agentSystem !== undefined ? { system: ctx.agentSystem } : {}),\n ...(ctx.agentToolAliases !== undefined ? { toolAliases: ctx.agentToolAliases } : {}),\n ...(ctx.agentMcpServers !== undefined ? { mcpServers: ctx.agentMcpServers } : {}),\n ...(ctx.agentSkills !== undefined ? { skills: ctx.agentSkills } : {}),\n ...(ctx.agentBehavior !== undefined ? { behavior: ctx.agentBehavior } : {}),\n turnId,\n callId,\n runId: ctx.runId,\n ...(ctx.parentRunId !== undefined ? { parentRunId: ctx.parentRunId } : {}),\n ...(ctx.session ? { session: ctx.session } : {}),\n ...(ctx.readState ? { readState: ctx.readState } : {}),\n ...(typeof ctx.depth === 'number' ? { depth: ctx.depth } : {}),\n clock: ctx.clock,\n }\n\n // Race the tool body against a per-call cancellation. Two design points:\n //\n // 1. The body promise is kicked off synchronously, then we attach a\n // no-op `.catch` to it so a late settle (the user cancels mid-\n // flight, we win the race with cancellation, then the body\n // eventually throws into the void) never surfaces as an unhandled\n // rejection. Misbehaving tool bodies that ignore `ctx.signal`\n // continue running in the background until they naturally finish;\n // the loop has already moved on with the cancellation marker.\n // 2. The rejection-side promise listens on `perCallAbort.signal` only.\n // Sibling / run aborts flow through `ctx.signal` into `childSignal`\n // and into the body — the existing AbortError path catches them\n // and the `cancelledByUser` guard below correctly leaves them in\n // the regular error branch (preserving the existing\n // `INTERRUPT_MESSAGE_FOR_TOOL_USE` semantics at the batch layer).\n const bodyPromise = toolDef.execute(effectiveInput, toolCtx)\n bodyPromise.catch(() => {})\n\n let removeAbortListener: (() => void) | undefined\n const cancellationPromise = new Promise<never>((_, reject) => {\n if (perCallAbort.signal.aborted) {\n reject(new Error(CANCELLED_BY_USER_SENTINEL))\n return\n }\n const onAbort = () => reject(new Error(CANCELLED_BY_USER_SENTINEL))\n perCallAbort.signal.addEventListener('abort', onAbort, { once: true })\n removeAbortListener = () => perCallAbort.signal.removeEventListener('abort', onAbort)\n })\n\n try {\n output = await Promise.race([bodyPromise, cancellationPromise])\n }\n finally {\n // Detach the abort listener on the happy path so a subsequent cancel\n // (race-condition-ish: body resolved, then user clicks cancel before\n // we've returned) doesn't reject a promise nobody awaits.\n removeAbortListener?.()\n }\n }\n catch (err) {\n // Classify the failure as user-cancellation vs regular error.\n //\n // Two signals say \"this was the user's per-call cancel\":\n // 1. Our cancellation promise won the race — it always rejects with\n // the sentinel-message Error we built above. This is the\n // definitive path because it pins the cause to OUR promise; no\n // other code path produces this message.\n // 2. The body observed `ctx.signal` and threw an `AbortError`-shaped\n // rejection, AND `perCallAbort` is the one that aborted (regardless\n // of whether the sibling/run signal also aborted, which can happen\n // when shell-cascade fires after our first cancelled shell returns\n // — see `dispatch`'s onresolved branch below for the cascade-skip\n // that prevents this in practice).\n //\n // The previous check `(perCallAbort.signal.aborted && !ctx.signal.aborted)`\n // was a too-narrow proxy: it false-rejected when the cascade had already\n // flipped `ctx.signal` between our perCallAbort and the catch, which\n // leaked the raw sentinel string to the model on parallel-cancel-all.\n const isOurSentinel = err instanceof Error && err.message === CANCELLED_BY_USER_SENTINEL\n const isAbortError = err instanceof Error && err.name === 'AbortError'\n if (isOurSentinel || (isAbortError && perCallAbort.signal.aborted)) {\n cancelledByUser = true\n }\n else {\n const error = err instanceof Error ? err : new Error(String(err))\n // Hook can mutate `result` to substitute a custom payload for the model —\n // e.g. OSS-model error rewriting, collapsing stack traces. Default\n // `Tool error: <msg>` is used when no handler sets it.\n const errorCtx: ToolHookContext & {\n error: Error\n result?: string | ToolResultContent[]\n } = {\n ...buildToolHookBase(ctx, turnId, callId, call.name, displayName, effectiveInput),\n error,\n }\n await ctx.hooks.callHook('tool:error', errorCtx)\n output = errorCtx.result ?? `Tool error: ${error.message}`\n isError = true\n }\n }\n\n if (cancelledByUser) {\n const reason = typeof perCallAbort.signal.reason === 'string'\n ? perCallAbort.signal.reason\n : 'cancelled-by-user'\n await ctx.hooks.callHook('tool:cancelled', {\n ...buildToolHookBase(ctx, turnId, callId, call.name, displayName, effectiveInput),\n reason,\n runToolCounts,\n })\n return { result: { id: callId, content: TOOL_USE_CANCELLED_MESSAGE, isError: true } }\n }\n\n const emitted = await emitToolResult(ctx, {\n turnId,\n callId,\n name: call.name,\n displayName,\n input: effectiveInput,\n output,\n isError,\n runToolCounts,\n ...(coercions ? { coercions } : {}),\n })\n\n return {\n result: {\n id: callId,\n content: emitted.output,\n ...(emitted.isError ? { isError: true } : {}),\n },\n }\n}\n\n/**\n * Fire `tool:dispatched` with the resolved path discriminator. Every code\n * path in {@link executeSingleTool} that produces a tool_result must call\n * this exactly once — that contract is what makes `tool:dispatched` ↔\n * `tool:after` a guaranteed symmetric pairing for live-event consumers\n * (the chat layer, SDK consumers reconstructing wire history from events,\n * tracing spans that want to record refused calls separately from\n * executed ones).\n *\n * Helper exists to centralize the optional-field plumbing for `reason`\n * (only set on `gate-block`) without sprinkling spread-conditional logic\n * across five call sites.\n */\nasync function fireDispatched(\n ctx: LoopContext,\n params: {\n turnId: string\n callId: string\n name: string\n displayName: string\n input: Record<string, unknown>\n outcome: 'execute' | 'gate-substitute' | 'gate-block' | 'unknown' | 'invalid-input'\n reason?: string\n runToolCounts: Readonly<Record<string, number>>\n },\n): Promise<void> {\n const { turnId, callId, name, displayName, input, outcome, reason, runToolCounts } = params\n await ctx.hooks.callHook('tool:dispatched', {\n ...buildToolHookBase(ctx, turnId, callId, name, displayName, input),\n outcome,\n runToolCounts,\n ...(reason !== undefined ? { reason } : {}),\n })\n}\n\n/**\n * Shared post-output emission: fire `tool:transform` (mutate-allowed), strip\n * images for non-vision providers, fire `tool:after`. Used by both the\n * gate-substitute (Z20) and post-execute paths so they stay byte-for-byte\n * identical from the consumer's perspective.\n *\n * Returns both the (possibly transformed) output and the final `isError`\n * flag — `tool:transform` listeners can flip the flag in either direction\n * (e.g. rewrite a structured error response to a graceful retry hint), and\n * the caller needs the post-transform value to populate `ToolResult.isError`\n * on the wire.\n */\nasync function emitToolResult(\n ctx: LoopContext,\n params: {\n turnId: string\n callId: string\n name: string\n displayName: string\n input: Record<string, unknown>\n output: string | ToolResultContent[]\n isError: boolean\n runToolCounts: Readonly<Record<string, number>>\n coercions?: readonly string[]\n },\n): Promise<{ output: string | ToolResultContent[], isError: boolean }> {\n const { turnId, callId, name, displayName, input, runToolCounts, coercions } = params\n let output = params.output\n let isError = params.isError\n\n // Transform hook — mutate ctx.result / ctx.isError to modify output. The\n // `outputBytes` field is the byte-length of the result *before* any\n // consumer mutation, so a truncation hook can decide whether to act.\n const transformCtx = {\n ...buildToolHookBase(ctx, turnId, callId, name, displayName, input),\n result: output,\n isError,\n outputBytes: toolOutputByteLength(output),\n ...(coercions ? { coercions } : {}),\n }\n await ctx.hooks.callHook('tool:transform', transformCtx)\n output = transformCtx.result\n isError = transformCtx.isError\n\n // Disk persistence — runs AFTER `tool:transform` so consumer transforms\n // get first crack at the result (compress / redact / annotate) before\n // we decide whether the post-transform bytes blow past the threshold.\n // Substitution mutates `output` in place; `tool:after` below then sees\n // the stub's byte size, and the stub is what lands in `session.turns`,\n // so subsequent turns re-emit the same bytes and ride the prompt cache.\n //\n // The truthy-check on `threshold` + `persistDir` short-circuits when\n // persistence is fully off (saves an async hop + struct allocation per\n // tool result) AND narrows both locals to non-nullable inside the block,\n // so the helper call site stays clean. The helper still validates its\n // own inputs (isAbsolute, threshold > 0, callId shape) — single source\n // of truth for \"is this call eligible\".\n const threshold = ctx.persistThreshold\n const persistDir = ctx.persistDir\n if (threshold && threshold > 0 && persistDir) {\n const outcome = await maybePersistToolResult({\n toolName: name,\n callId,\n output,\n threshold,\n persistDir,\n ...(ctx.persistExcludeTools ? { excludeTools: ctx.persistExcludeTools } : {}),\n ...(typeof ctx.persistMaxBytes === 'number' ? { maxBytes: ctx.persistMaxBytes } : {}),\n })\n if (outcome.kind === 'persisted') {\n output = outcome.output\n }\n // `error` and `skip` outcomes leave `output` untouched. Write failures\n // are intentionally swallowed at this layer — a permissions blip on\n // the persistence dir shouldn't take down a tool call whose result is\n // otherwise valid. The loop continues with the full inline payload.\n else if (outcome.kind === 'error' && process.env.ZIDANE_DEBUG) {\n process.stderr.write(`[zidane/loop] persistence write failed for ${name}/${callId}: ${outcome.error.message}\\n`)\n }\n }\n\n // Strip images for non-vision providers before they hit the wire. Done\n // after the transform hook so consumers can still observe the raw\n // structured output.\n output = stripImagesForNonVision(ctx.provider, output)\n\n // `tool:after` carries the post-mutation byte size — what actually goes\n // back to the model. Telemetry consumers should prefer this over\n // recomputing.\n await ctx.hooks.callHook('tool:after', {\n ...buildToolHookBase(ctx, turnId, callId, name, displayName, input),\n result: output,\n outputBytes: toolOutputByteLength(output),\n runToolCounts,\n ...(coercions ? { coercions } : {}),\n })\n\n return { output, isError }\n}\n\n/** Default cap on in-flight tools per turn. Mirrors Claude Code's `CLAUDE_CODE_MAX_TOOL_USE_CONCURRENCY`. */\nconst DEFAULT_MAX_CONCURRENT_TOOLS = 10\n\n/** Canonical name of the shell tool — referenced for cascade-cancel semantics. */\nconst SHELL_TOOL_NAME = 'shell'\n\n/** Reason surfaced on `siblingAbort.signal` when a shell error cancels its fleet. */\nconst SHELL_CASCADE_REASON = 'sibling-shell-error'\n\n/**\n * Canonical `tool_result.content` text emitted to siblings that were\n * cancelled by a `shell` error in the same batch. Distinct from\n * {@link INTERRUPT_MESSAGE_FOR_TOOL_USE} (user-issued abort) and\n * {@link TOOL_USE_SKIPPED_MESSAGE} (steered) so consumers can split\n * the three causes by string-match.\n */\nexport const SHELL_CASCADE_CANCEL_MESSAGE = 'Cancelled: a sibling `shell` call in the same batch errored; re-run independently if still needed.'\n\n/**\n * Resolve a tool's concurrency-safety verdict for a specific call.\n *\n * - Missing toolDef (unknown tool) → `false`. `executeSingleTool` handles\n * the unknown-tool path itself; barriering it keeps the unknown-tool\n * error from racing with siblings.\n * - Static `true` / `false` → use as-is.\n * - Function → invoke; any throw is treated as `false` (fail-closed) so a\n * buggy predicate can't accidentally widen the safety window.\n *\n * Pure / sync — pre-computed once per call before dispatch begins, so the\n * scheduler's hot path stays branch-light.\n */\nfunction resolveConcurrencySafe(\n def: ToolDef | undefined,\n input: Record<string, unknown>,\n): boolean {\n if (!def)\n return false\n const flag = def.isConcurrencySafe\n if (flag === undefined)\n return false\n if (typeof flag === 'boolean')\n return flag\n try {\n return flag(input) === true\n }\n catch {\n return false\n }\n}\n\n/**\n * Unified per-turn tool dispatcher.\n *\n * Walks `toolCalls` in submission order. For each call:\n *\n * - **Concurrency-safe + fleet is all safe + room under the cap** → fires\n * asynchronously and the loop advances to the next call. The fleet runs\n * in parallel up to `behavior.maxConcurrentTools` (default {@link\n * DEFAULT_MAX_CONCURRENT_TOOLS}).\n * - **Unsafe** (or the in-flight fleet contains anything unsafe) → acts\n * as a barrier: waits for the fleet to drain, then runs alone, then\n * unblocks the queue.\n *\n * Results are written into a fixed `results[index]` array on completion\n * and yielded back in submission order, so the model sees deterministic\n * adjacency regardless of which call finished first.\n *\n * **Failure modes:**\n *\n * - **Hook throws / tool body throws** — captured per-call into a\n * `tool_result` so the assistant turn's `tool_use` IDs always have\n * matching `tool_result` IDs (providers reject orphan IDs).\n * - **Parent abort** mid-batch — drains the in-flight fleet (their\n * `AbortError` becomes `INTERRUPT_MESSAGE_FOR_TOOL_USE`), then synthesizes\n * interrupt results for any unstarted calls so the turn closes cleanly.\n * - **Steering queue populated** between dispatches — same drain + a\n * `TOOL_USE_SKIPPED_MESSAGE` result for unstarted calls. The outer loop\n * picks up the steer at the next checkpoint.\n * - **Shell error in a fleet** — `siblingAbort.abort('sibling-shell-error')`\n * tears down concurrently-running siblings. Mirrors the convention that\n * shell commands often chain (`mkdir foo && cd foo`); one failing\n * sibling commonly invalidates the rest. Non-shell errors are isolated.\n *\n * A child `AbortController` (`siblingAbort`) forwards the parent abort\n * AND carries the shell-cascade signal — siblings see one signal source.\n */\nasync function executeToolBatch(\n ctx: LoopContext,\n toolCalls: ToolCall[],\n turnId: string,\n): Promise<ToolResult[]> {\n if (toolCalls.length === 0)\n return []\n\n const N = toolCalls.length\n const maxConcurrent = Math.max(1, ctx.maxConcurrentTools ?? DEFAULT_MAX_CONCURRENT_TOOLS)\n const results: (ToolResult | undefined)[] = Array.from({ length: N })\n\n // Pre-resolve every call's safety verdict so the scheduler's main loop\n // doesn't re-invoke (potentially throwing) predicates inside its hot\n // path. Indices line up with `toolCalls` and `results`.\n const safe: boolean[] = Array.from({ length: N })\n for (let i = 0; i < N; i++)\n safe[i] = resolveConcurrencySafe(ctx.tools[toolCalls[i].name], toolCalls[i].input)\n\n // One controller for the whole batch — siblings share it. The parent\n // abort is forwarded via a single listener that we MUST clean up\n // when the batch returns: `ctx.signal` is the run-scoped\n // `agent.run()` signal that outlives this batch, so a never-removed\n // listener would accumulate one entry per batch over a multi-turn\n // run. Captured in `parentAbortListener` so the `finally` below\n // can call `removeEventListener`. `{ once: true }` short-circuits\n // the listener if the abort fires, but we still need to remove it\n // on the (typical) non-abort exit.\n const siblingAbort = new AbortController()\n let parentAbortListener: (() => void) | undefined\n if (ctx.signal.aborted) {\n siblingAbort.abort(ctx.signal.reason ?? 'parent-aborted')\n }\n else {\n parentAbortListener = () => siblingAbort.abort(ctx.signal.reason ?? 'parent-aborted')\n ctx.signal.addEventListener('abort', parentAbortListener, { once: true })\n }\n\n // Tools see the sibling-scoped signal — a shell cascade cancels them\n // without unwinding the parent. Spreading `ctx` keeps every other\n // field (hooks, behavior, readState, parentRunId, …) reaching tools.\n const childCtx: LoopContext = { ...ctx, signal: siblingAbort.signal }\n\n /** Indices currently in flight. Tracked for fleet-safety + cap checks. */\n const inFlight = new Map<number, Promise<void>>()\n\n /**\n * Distinguish a shell-cascade kill from a user-issued abort so the\n * model sees actionable text. When BOTH the parent signal and the\n * sibling signal are aborted, the parent wins — user-issued aborts\n * take precedence (the model is being interrupted by the human, not\n * by a sibling's failure).\n */\n const cancelMessage = (): string => {\n if (ctx.signal.aborted)\n return INTERRUPT_MESSAGE_FOR_TOOL_USE\n if (siblingAbort.signal.reason === SHELL_CASCADE_REASON)\n return SHELL_CASCADE_CANCEL_MESSAGE\n return INTERRUPT_MESSAGE_FOR_TOOL_USE\n }\n\n const dispatch = (index: number): Promise<void> => {\n const call = toolCalls[index]\n return executeSingleTool(childCtx, call, turnId).then(\n ({ result }) => {\n results[index] = result\n // Cascade-cancel only for shell, and only on the first error\n // that lands in a still-live fleet — keeps the abort reason\n // pinned to the first culprit. We deliberately EXCLUDE\n // user-cancelled results: a `agent.cancelTool('A')` on a shell\n // means \"I'm done with A\", not \"A failed in a way that\n // invalidates B and C\". Without this exclusion, cancelling one\n // shell would cascade-kill its siblings (surprising and wrong\n // — the user only cancelled one), and `cancel all` on a fleet\n // of shells would mis-classify B and C as cascade-aborts\n // instead of user-cancellations, leaking the internal sentinel\n // string to the wire.\n const contentIsString = typeof result.content === 'string'\n const isUserCancel = contentIsString && result.content === TOOL_USE_CANCELLED_MESSAGE\n if (\n result.isError\n && !isUserCancel\n && call.name === SHELL_TOOL_NAME\n && !siblingAbort.signal.aborted\n ) {\n siblingAbort.abort(SHELL_CASCADE_REASON)\n }\n },\n (err: unknown) => {\n const isAbort = siblingAbort.signal.aborted\n || ctx.signal.aborted\n || (err instanceof Error && err.name === 'AbortError')\n results[index] = {\n id: call.id,\n content: isAbort ? cancelMessage() : `Error: ${errorMessage(err)}`,\n isError: true,\n }\n },\n ).finally(() => {\n inFlight.delete(index)\n })\n }\n\n const drain = async (): Promise<void> => {\n if (inFlight.size > 0)\n await Promise.all([...inFlight.values()])\n }\n\n /**\n * Free a SINGLE slot: wait for the first in-flight call to settle rather\n * than the whole fleet. Used when an all-safe fleet hits `maxConcurrent` —\n * a sliding window dispatches the next safe call the moment ANY sibling\n * finishes, instead of stalling the entire batch on the slowest one (which\n * a full `drain()` would do). `dispatch()` never rejects (it captures both\n * outcomes into `results[]`), so `Promise.race` here can't throw. Each\n * dispatched promise carries a `.finally` that deletes its own slot from\n * `inFlight` BEFORE the promise resolves, so on return `inFlight.size` has\n * dropped by at least one and a dispatch is guaranteed to fit under the cap.\n */\n const waitForSlot = async (): Promise<void> => {\n if (inFlight.size > 0)\n await Promise.race([...inFlight.values()])\n }\n\n /** Whether every in-flight call is concurrency-safe. */\n const fleetAllSafe = (): boolean => {\n for (const idx of inFlight.keys()) {\n if (!safe[idx])\n return false\n }\n return true\n }\n\n /**\n * Fill all unstarted slots (`results[j]` still undefined) with the\n * canonical text + `isError: true`. Used at every short-circuit\n * branch (abort / steer) so the assistant turn's `tool_use` IDs\n * always have matching `tool_result` IDs — providers reject orphan\n * IDs loudly.\n */\n const fillUnstarted = (from: number, content: string): void => {\n for (let j = from; j < N; j++) {\n if (!results[j])\n results[j] = { id: toolCalls[j].id, content, isError: true }\n }\n }\n\n try {\n for (let i = 0; i < N; i++) {\n // Barrier semantics: an unsafe call (or an unsafe-poisoned fleet)\n // flushes everything in-flight before dispatching. After the drain,\n // the fleet is empty and the unsafe call runs alone; subsequent safe\n // calls re-form a new fleet behind it.\n if (!safe[i] || !fleetAllSafe())\n await drain()\n // All-safe fleet at the cap: free just one slot (sliding window) so\n // the next safe call dispatches as soon as ANY sibling finishes,\n // rather than barriering on the slowest. Without this, N safe calls\n // with N > maxConcurrent run as fixed batches and each batch waits on\n // its slowest member — turning a parallel fan-out into staircased\n // latency. The barrier branch above already handled the unsafe cases.\n else if (inFlight.size >= maxConcurrent)\n await waitForSlot()\n\n // Re-check abort + steer AFTER any drain — tools that resolved\n // during the drain may have flipped either signal (e.g. a tool\n // calls `agent.abort()` from its body, or `tool:after` enqueues\n // a steer). Checking only at the top of the iteration would\n // miss these and dispatch one extra tool. Cheap re-check; no I/O.\n //\n // On either early-return path we MUST drain the live fleet\n // first: dispatched-but-unresolved promises still own slots in\n // `results[]`, so returning before they finish would leave\n // those slots `undefined` and the caller would see orphan\n // `tool_use` IDs without paired `tool_result`s. Aborted tools\n // propagate through `siblingAbort.signal` and resolve with an\n // error result; steered tools complete normally (the steer is\n // a post-batch concern, not an in-flight kill).\n if (ctx.signal.aborted) {\n await drain()\n fillUnstarted(i, INTERRUPT_MESSAGE_FOR_TOOL_USE)\n return results as ToolResult[]\n }\n if (ctx.steeringQueue.length > 0) {\n await drain()\n fillUnstarted(i, TOOL_USE_SKIPPED_MESSAGE)\n return results as ToolResult[]\n }\n\n inFlight.set(i, dispatch(i))\n }\n\n // Drain the trailing fleet — last call may have been safe + non-\n // blocking. After the drain, results may still contain interrupt\n // messages from dispatch's onrejected branch for tools that saw a\n // mid-flight abort.\n await drain()\n return results as ToolResult[]\n }\n finally {\n // Detach the parent-abort listener so we don't leak one listener\n // per batch on `ctx.signal` (run-scoped, outlives many batches).\n // No-op if the listener already fired (`{ once: true }`) or was\n // never registered (parent was already aborted at batch start).\n if (parentAbortListener)\n ctx.signal.removeEventListener('abort', parentAbortListener)\n }\n}\n","/**\n * Prompt canonicalization + default multimodal message builder.\n *\n * `agent.run({ prompt })` accepts either `string` or `PromptPart[]`. The\n * agent normalizes both to `PromptPart[]` before handing to the provider.\n *\n * Providers may implement `Provider.promptMessage` for native-format support\n * (e.g. Anthropic's document blocks). Providers that don't fall back to\n * `defaultPromptMessage`, which inlines text-encoded documents and throws on\n * base64-encoded documents.\n */\n\nimport type { Provider } from './providers'\nimport type { PromptPart, SessionContentBlock, SessionMessage } from './types'\n\n/**\n * Coerce the run-level prompt into a `PromptPart[]`.\n *\n * - `string` prompt → a single `text` part. Empty string returns `undefined`\n * so callers skip pushing an empty user turn.\n * - `PromptPart[]` prompt → validated and returned as-is. An empty array, or\n * an array whose text parts are all empty with no image/document parts,\n * returns `undefined`.\n * - `undefined` → `undefined` (promptless resume path).\n */\nexport function canonicalizePrompt(\n prompt: string | PromptPart[] | undefined,\n): PromptPart[] | undefined {\n if (prompt === undefined)\n return undefined\n\n if (typeof prompt === 'string') {\n if (prompt.length === 0)\n return undefined\n return [{ type: 'text', text: prompt }]\n }\n\n // Array path — drop if empty, or if every text part is empty and there are no\n // image/document parts (nothing meaningful to send).\n if (prompt.length === 0)\n return undefined\n\n // Reject malformed parts up front rather than letting them slip through to\n // `defaultPromptMessage` / `provider.promptMessage`, where the failure\n // mode is a confusing \"Cannot read property 'text' of undefined\" or a\n // base64-document throw far from the call-site. The check is cheap and\n // catches the common typo (wrong `type` field) loudly.\n for (const part of prompt) {\n if (!part || typeof part !== 'object' || typeof (part as { type?: unknown }).type !== 'string') {\n throw new Error('Invalid PromptPart: each part must be an object with a `type` field.')\n }\n const type = (part as { type: string }).type\n if (type !== 'text' && type !== 'image' && type !== 'document') {\n throw new Error(`Invalid PromptPart type \"${type}\". Expected \"text\" | \"image\" | \"document\".`)\n }\n }\n\n const hasMeaningfulPart = prompt.some(part => (\n (part.type === 'text' && part.text.length > 0)\n || part.type === 'image'\n || part.type === 'document'\n ))\n if (!hasMeaningfulPart)\n return undefined\n\n return prompt\n}\n\n/**\n * Build a user `SessionMessage` from prompt parts without provider-specific handling.\n *\n * - `text` parts map to `{ type: 'text', text }` blocks.\n * - `image` parts map to `{ type: 'image', mediaType, data }` blocks.\n * - `document` parts with `encoding: 'text'` are inlined as an attachment-tagged\n * text block so every provider can read them.\n * - `document` parts with `encoding: 'base64'` throw — the caller should switch\n * to a provider that implements `promptMessage` (e.g. Anthropic for PDFs).\n */\nexport function defaultPromptMessage(parts: PromptPart[]): SessionMessage {\n const content: SessionContentBlock[] = []\n\n for (const part of parts) {\n if (part.type === 'text') {\n if (part.text.length > 0)\n content.push({ type: 'text', text: part.text })\n continue\n }\n\n if (part.type === 'image') {\n content.push({ type: 'image', mediaType: part.mediaType, data: part.data, ...(part.name ? { name: part.name } : {}) })\n continue\n }\n\n // document\n if (part.encoding === 'text') {\n const header = part.name\n ? `<attachment name=\"${part.name}\" media_type=\"${part.mediaType}\">`\n : `<attachment media_type=\"${part.mediaType}\">`\n content.push({ type: 'text', text: `${header}\\n${part.data}\\n</attachment>` })\n continue\n }\n\n throw new Error(\n `Provider does not support base64 document parts (mediaType: ${part.mediaType}). `\n + `Use a text-encoded document or a provider that implements promptMessage (e.g. Anthropic).`,\n )\n }\n\n return { role: 'user', content }\n}\n\n/**\n * Build the prompt `SessionMessage` for a given provider.\n *\n * Prefers `provider.promptMessage` when defined, falling back to `defaultPromptMessage`.\n */\nexport function buildPromptMessage(provider: Provider, parts: PromptPart[]): SessionMessage {\n if (provider.promptMessage)\n return provider.promptMessage(parts)\n return defaultPromptMessage(parts)\n}\n","/**\n * `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 **per-tool streak of back-to-back\n * identical normalized payloads** and escalates:\n *\n * - At `blockThreshold` (default 4): refuse via `ctx.block` with a pivot\n * suggestion. Fires `repeat-guard:exceeded` (`action: 'block'`).\n * - At `abortThreshold` (default 8): block the offending call AND call the\n * injected `abort()` (bound to the agent's `AbortController`). The loop's\n * existing post-turn `signal.aborted` check then fires `agent:abort` and\n * terminates the run — the same path a user-driven cancel takes. Fires\n * `repeat-guard:exceeded` (`action: 'abort'`) first so consumers can\n * distinguish a guard abort from a user abort. This is the only layer that\n * stops a model which ignores the `Blocked:` reason and keeps retrying the\n * same payload.\n *\n * \"Consecutive\" is **per tracked tool**: a call to a different tracked tool,\n * or the same tool with a different normalized payload, resets that tool's\n * streak. Calls to untracked tools never reset any streak — so\n * `shell X / read_file Y / shell X` still counts as two consecutive\n * `shell X`.\n *\n * State is run-local (closure-scoped, keyed by `(runId, toolName)`) 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 * Default tracked-tool predicate: `shell` exactly, plus any tool whose\n * canonical name ends in `_execute_sql` (the MCP SQL-runner shape,\n * `mcp_<server>_execute_sql`). Both are auto-approved, side-effect-light\n * retry magnets — the exact loop shapes the abort escalation exists for.\n */\nexport function defaultRepeatGuardTracked(name: string): boolean {\n return name === 'shell' || name.endsWith('_execute_sql')\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}; SQL-runner tools get a whitespace-\n * collapsed query; everything else falls back to a stable JSON encoding.\n * Returns `undefined` when there's no meaningful payload to key on (so the\n * call is excluded from streak tracking rather than keyed on `'{}'`).\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 if (name.endsWith('_execute_sql')) {\n const q = input.query ?? input.sql ?? input.statement\n if (typeof q !== 'string' || q.trim().length === 0)\n return undefined\n return `sql:${q.replace(/\\s+/g, ' ').trim().toLowerCase()}`\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. */\nfunction 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 formatBlockReason(\n reason: RepeatGuardConfig['blockReason'],\n name: string,\n count: number,\n): string {\n if (typeof reason === 'string')\n return reason\n if (typeof reason === 'function') {\n try {\n const out = reason(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 in a row. `\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\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-`(runId, toolName)` streak. Run-scoped so a parent and its subagents\n // never share a streak. Closure-local — dies with the run on uninstall.\n const streaks = new Map<string, StreakEntry>()\n\n function streakKey(runId: string | undefined, name: string): string {\n return `${runId ?? '-'}::${name}`\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 } {\n const blockThreshold = Math.max(\n 2,\n Math.floor(\n typeof config.blockThreshold === 'number' && Number.isFinite(config.blockThreshold)\n ? config.blockThreshold\n : DEFAULT_BLOCK_THRESHOLD,\n ),\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 rawAbort\n = typeof config.abortThreshold === 'number' && Number.isFinite(config.abortThreshold)\n ? config.abortThreshold\n : DEFAULT_ABORT_THRESHOLD\n const abortThreshold = rawAbort > 0 ? Math.max(blockThreshold + 1, Math.floor(rawAbort)) : Infinity\n return {\n isTracked: compileMatchers(config.tools),\n normalize: config.normalize ?? defaultRepeatGuardNormalize,\n blockThreshold,\n abortThreshold,\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 // Don't override prior gate handlers (a budget block, a dedup substitute,\n // a skill refusal). Those already broke the call; counting it here would\n // double-penalize.\n if (ctx.block || ctx.result !== undefined)\n return\n\n const config = getConfig()\n if (!config)\n return\n\n const { isTracked, normalize, blockThreshold, abortThreshold } = resolve(config)\n if (!isTracked(ctx.name))\n return\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 return\n }\n if (typeof key !== 'string' || key.length === 0)\n return\n\n const slot = streakKey(ctx.runId, ctx.name)\n const prior = streaks.get(slot)\n const count = prior && prior.key === key ? prior.count + 1 : 1\n streaks.set(slot, { key, count })\n\n if (count < blockThreshold)\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.\n if (count >= abortThreshold) {\n ctx.block = true\n ctx.reason = formatBlockReason(config.blockReason, ctx.name, count)\n await hooks.callHook('repeat-guard:exceeded', {\n tool: ctx.name,\n count,\n threshold: abortThreshold,\n turnId: ctx.turnId,\n action: 'abort',\n })\n abort()\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 = formatBlockReason(config.blockReason, ctx.name, count)\n await hooks.callHook('repeat-guard:exceeded', {\n tool: ctx.name,\n count,\n threshold: blockThreshold,\n turnId: ctx.turnId,\n action: 'block',\n })\n }\n\n const unregister = hooks.hook('tool:gate', gateHandler)\n\n return function uninstall() {\n unregister()\n streaks.clear()\n }\n}\n","/**\n * `tool-budgets` middleware — per-tool soft call caps on top of the\n * `tool:gate` writable-`block`/`result` slots and the run-cumulative\n * `runToolCounts` (Z24).\n *\n * Two modes:\n *\n * - `'block'` — refuse the over-budget call via `ctx.block = true`. The model\n * sees a `Blocked: <message>` tool result.\n * - `'steer'` — let the call run, but enqueue a synthetic user message for\n * the NEXT turn so the model sees the budget warning before deciding to\n * call again. Reuses the existing `steeringQueue` plumbing.\n *\n * Either mode fires `tool-budget:exceeded` so observability layers can react.\n */\n\nimport type { Hookable } from 'hookable'\nimport type { AgentHooks } from './agent'\nimport type { AgentBehavior, ToolHookContext, ToolResultContent } from './types'\n\n/**\n * Install the per-tool soft-budget middleware on a hook bus.\n * `getToolBudgets` returns the resolved per-tool budgets (run override merged\n * with agent defaults). `enqueueSteer` pushes a synthetic user message into\n * the run's steeringQueue so the loop drains it between turns.\n *\n * The middleware maintains its OWN approval counter (`approvedCounts`),\n * incremented at gate-time — independent of the loop's `runToolCounts`,\n * which is incremented after gate completes. This gives atomic per-call\n * reservation in parallel batches: when a batch of N calls all fire\n * `tool:gate` before any increments propagate, each gate handler still\n * sees the prior approvals and refuses past `max`.\n *\n * Returns an `uninstall` fn.\n */\nexport function installToolBudgetsGate(\n hooks: Hookable<AgentHooks>,\n getToolBudgets: () => AgentBehavior['toolBudgets'] | undefined,\n enqueueSteer: (message: string) => void,\n): () => void {\n // Per-run set of tools that have ALREADY had a steer fired. Without this,\n // every call past `max` queues another nudge and the conversation drowns\n // in identical reminders. Cleared on uninstall (run-end).\n const steeredOnce = new Set<string>()\n\n // Per-tool approval counter the middleware owns. Incremented synchronously\n // when a call passes gate, so within-batch ordering is preserved even when\n // the loop's `runToolCounts` lags (handlers across a parallel batch can\n // fire before any of the batch's calls have been incremented loop-side).\n const approvedCounts: Record<string, number> = {}\n\n async function gateHandler(ctx: ToolHookContext & {\n block: boolean\n reason: string\n result?: string | ToolResultContent[]\n runToolCounts: Readonly<Record<string, number>>\n }) {\n // Don't override prior gate handlers.\n if (ctx.block || ctx.result !== undefined)\n return\n\n const toolBudgets = getToolBudgets()\n const budget = toolBudgets?.[ctx.name]\n if (!budget)\n return\n\n const max = budget.max\n if (typeof max !== 'number' || max <= 0)\n return\n\n // Read our internal approval counter — independent of the loop's\n // `runToolCounts` so within-batch ordering is correct even in parallel\n // mode. Each gate that approves a call increments this synchronously.\n const count = approvedCounts[ctx.name] ?? 0\n if (count < max) {\n // Approve atomically: bump the counter now, before yielding to the\n // next handler in the chain. Subsequent calls in the same parallel\n // batch see this approval and may refuse if the budget is depleted.\n approvedCounts[ctx.name] = count + 1\n return\n }\n\n // Resolve mode + message. Function form runs untrusted consumer code —\n // a throw would crash the hook bus, so we fall back to the default\n // 'steer' shape on error rather than poisoning the run.\n const onExceed = budget.onExceed ?? 'steer'\n let mode: 'steer' | 'block'\n let message: string\n if (typeof onExceed === 'function') {\n try {\n const out = onExceed({ tool: ctx.name, count, max })\n mode = out.mode\n message = out.message\n }\n catch {\n mode = 'steer'\n message = defaultSteerMessage(ctx.name, count, max)\n }\n }\n else if (onExceed === 'block') {\n mode = 'block'\n message = defaultBlockMessage(ctx.name, max)\n }\n else {\n mode = 'steer'\n message = defaultSteerMessage(ctx.name, count, max)\n }\n\n if (mode === 'block') {\n ctx.block = true\n ctx.reason = message\n await hooks.callHook('tool-budget:exceeded', {\n tool: ctx.name,\n count,\n max,\n turnId: ctx.turnId,\n mode: 'block',\n })\n return\n }\n\n // Steer mode — let the call go through, but queue a single nudge per\n // tool so the model sees the budget warning before its next turn.\n if (!steeredOnce.has(ctx.name)) {\n steeredOnce.add(ctx.name)\n enqueueSteer(message)\n await hooks.callHook('tool-budget:exceeded', {\n tool: ctx.name,\n count,\n max,\n turnId: ctx.turnId,\n mode: 'steer',\n })\n }\n }\n\n const unregister = hooks.hook('tool:gate', gateHandler)\n\n return function uninstall() {\n unregister()\n steeredOnce.clear()\n }\n}\n\nfunction defaultSteerMessage(tool: string, count: number, max: number): string {\n return `[Tool budget reached: '${tool}' has been called ${count} times this run (cap: ${max}). Avoid calling it again unless strictly necessary; commit to a result and move on.]`\n}\n\nfunction defaultBlockMessage(tool: string, max: number): string {\n return `Tool '${tool}' has reached its per-run budget of ${max} calls; further invocations are refused.`\n}\n","/**\n * Per-command exit-code interpretation for the `shell` tool.\n *\n * Many command-line tools use non-zero exit codes to signal information that\n * is *not* an error — `grep` returns 1 for \"no matches found\", `diff` returns\n * 1 for \"files differ\", `find` returns 1 when some directories were\n * inaccessible, and `test`/`[` return 1 for \"condition false\". Treating\n * those uniformly as failures wastes turns: models retry or pivot when there\n * is nothing wrong.\n *\n * Mirrors `claude-code/tools/BashTool/commandSemantics.ts`.\n */\n\nexport interface CommandSemanticResult {\n /** Whether to surface this as an error to the model (`Exit code N` prefix). */\n isError: boolean\n /** Optional human-readable footer appended after the body, e.g. \"No matches found\". */\n message?: string\n}\n\ntype CommandSemantic = (exitCode: number) => CommandSemanticResult\n\nconst DEFAULT_SEMANTIC: CommandSemantic = exitCode => ({\n isError: exitCode !== 0,\n message: exitCode !== 0 ? `Command failed with exit code ${exitCode}` : undefined,\n})\n\nconst COMMAND_SEMANTICS: ReadonlyMap<string, CommandSemantic> = new Map<string, CommandSemantic>([\n // grep / ripgrep: 0 = matches, 1 = no matches, ≥2 = error.\n ['grep', exit => ({ isError: exit >= 2, message: exit === 1 ? 'No matches found' : undefined })],\n ['rg', exit => ({ isError: exit >= 2, message: exit === 1 ? 'No matches found' : undefined })],\n // diff: 0 = identical, 1 = differ, ≥2 = error.\n ['diff', exit => ({ isError: exit >= 2, message: exit === 1 ? 'Files differ' : undefined })],\n // find: 0 = ok, 1 = some dirs inaccessible (warning), ≥2 = error.\n ['find', exit => ({ isError: exit >= 2, message: exit === 1 ? 'Some directories were inaccessible' : undefined })],\n // test / [: 0 = condition true, 1 = condition false, ≥2 = error.\n ['test', exit => ({ isError: exit >= 2, message: exit === 1 ? 'Condition is false' : undefined })],\n ['[', exit => ({ isError: exit >= 2, message: exit === 1 ? 'Condition is false' : undefined })],\n])\n\n/**\n * Pick the semantic for a command line. Best-effort: walks the command from\n * right to left, taking the last segment after `|` / `&&` / `||` / `;` —\n * that's the segment whose exit code propagates. Don't depend on this for\n * security; it's a heuristic, not a parser.\n */\nexport function interpretShellResult(\n command: string,\n exitCode: number,\n): CommandSemanticResult {\n const base = extractTrailingCommand(command)\n const semantic = COMMAND_SEMANTICS.get(base) ?? DEFAULT_SEMANTIC\n return semantic(exitCode)\n}\n\nfunction extractTrailingCommand(command: string): string {\n // Split on the common chain operators. The exit code we see is the trailing\n // segment's. Quoted operators escape the split, but we don't try to be\n // perfect — false positives just fall back to default semantics.\n const segments = command.split(/\\|\\||&&|[;|\\n]/)\n const last = segments[segments.length - 1]?.trim() ?? command\n // First whitespace-delimited token of the trailing segment, sans leading\n // env assignments (`FOO=bar baz` → `baz`).\n const tokens = last.split(/\\s+/).filter(t => !/^[A-Z_]\\w*=/i.test(t))\n return tokens[0] ?? ''\n}\n","import type { ToolContext, ToolDef } from './types'\nimport { Buffer } from 'node:buffer'\nimport { previewLine } from '../chat/format'\nimport { interpretShellResult } from './shell-semantics'\n\n/**\n * Execute a shell command in the agent's execution context.\n *\n * Truncation is **tail-priority**: when stdout+stderr combined exceeds\n * `maxOutputBytes`, the head is dropped and a marker `…(N bytes truncated\n * from head)…` is inserted before the tail. Errors and exit summaries\n * usually live at the end of output, so keeping the tail preserves the\n * model's most useful signal.\n *\n * Defaults are tuned for typical commands (build output, test runs): the\n * combined cap is 32 KiB and the per-call timeout follows the execution\n * context's own default (30 s for in-process).\n */\n\nconst DEFAULT_MAX_OUTPUT_BYTES = 32_768\n\n/**\n * Best-effort read-only allow-list for the leading command token. Members\n * are commands whose stock behavior cannot mutate the workspace under any\n * argument combination — `ls`, `cat`, `pwd`, etc. Commands that *can*\n * mutate depending on flags (`find -delete`, `git tag <name>`, `tar -x`)\n * are intentionally excluded; the input-aware {@link isReadOnlyShellCommand}\n * predicate falls back to the conservative \"not safe\" answer for them, so\n * the scheduler barriers them.\n */\nconst SHELL_READ_ONLY_COMMANDS: ReadonlySet<string> = new Set([\n 'ls',\n 'cat',\n 'head',\n 'tail',\n 'wc',\n 'pwd',\n 'whoami',\n 'id',\n 'date',\n 'uname',\n 'hostname',\n 'tty',\n 'echo',\n 'printf',\n 'env',\n 'printenv',\n 'which',\n 'type',\n 'command',\n 'file',\n 'stat',\n 'grep',\n 'rg',\n 'ag',\n 'true',\n 'false',\n 'test',\n // Intentionally NOT included: `sed`, `awk`, `find`, `tar`, `xargs`,\n // `tee` — all can write to disk depending on flags / script body\n // (`sed -i`, `awk 'BEGIN { print > \"f\" }'`, `find -delete`, `tar\n // -x`). Conservative omission keeps the predicate fail-closed.\n])\n\n/**\n * `git` subcommands that are pure reads regardless of arguments. Excludes\n * `branch`/`tag`/`remote` (which can mutate when given a name) and\n * `config` (which writes when given a value).\n */\nconst GIT_READ_ONLY_SUBCOMMANDS: ReadonlySet<string> = new Set([\n 'status',\n 'log',\n 'diff',\n 'show',\n 'blame',\n 'rev-parse',\n 'ls-files',\n 'ls-tree',\n 'cat-file',\n 'reflog',\n 'shortlog',\n 'describe',\n 'rev-list',\n 'name-rev',\n 'whatchanged',\n 'merge-base',\n 'symbolic-ref',\n])\n\n/**\n * Conservative read-only verdict for a shell command — used to opt a\n * `shell` invocation into the scheduler's concurrent fleet. Returns\n * `false` (fail-closed) on anything ambiguous so the scheduler barriers\n * it. Specifically:\n *\n * - Rejects compound commands (`;`, `&&`, `||`, `|`) and redirects (`>`,\n * `>>`, `<`) — even a pipe to a read-only sink is treated as too\n * complex to analyze.\n * - Rejects subshell / process substitution (`$(...)`, `` `...` ``,\n * `<(...)`, `>(...)`).\n * - Skips leading `VAR=value` env assignments to find the real\n * command token.\n * - Strips a possible absolute path on the command (`/usr/bin/ls` → `ls`).\n * - Allows the command iff its base name is in\n * {@link SHELL_READ_ONLY_COMMANDS} OR it's `git <subcmd>` where\n * `<subcmd>` is in {@link GIT_READ_ONLY_SUBCOMMANDS}.\n *\n * Cheap (no spawned process; regex + token scan). Safe to call from the\n * hot scheduler path.\n */\nexport function isReadOnlyShellCommand(command: unknown): boolean {\n if (typeof command !== 'string')\n return false\n const trimmed = command.trim()\n if (trimmed === '')\n return false\n\n // Shell metacharacters that could enable mutation, command chaining,\n // or backgrounding — any of these takes the command out of the\n // \"single read invocation\" shape we can analyze safely. Pipes (`|`)\n // are rejected even when piping to a read-only sink because parsing\n // both sides is more complexity than the win.\n //\n // >, <, >> — redirects\n // ;, &, \\n — command separators (and `&` covers backgrounding)\n // |, ||, && — pipes + boolean chains\n // `…`, $(…) — command substitution\n // <(…), >(…) — process substitution\n if (/[<>;&|`\\n]/.test(trimmed))\n return false\n if (trimmed.includes('$(') || trimmed.includes('<(') || trimmed.includes('>('))\n return false\n\n const tokens = trimmed.split(/\\s+/)\n let i = 0\n // Skip leading VAR=value env assignments (e.g. `FOO=bar ls`).\n while (i < tokens.length && /^[A-Z_]\\w*=/i.test(tokens[i]))\n i++\n const head = tokens[i]\n if (!head)\n return false\n\n // Strip a leading path: `/usr/bin/ls` → `ls`.\n const base = head.split('/').pop() ?? head\n if (SHELL_READ_ONLY_COMMANDS.has(base))\n return true\n if (base === 'git') {\n const sub = tokens[i + 1]\n return typeof sub === 'string' && GIT_READ_ONLY_SUBCOMMANDS.has(sub)\n }\n return false\n}\n\n/**\n * Canonical sibling tools that, when registered alongside `shell`, are worth\n * nudging the model toward — re-running `ls`/`cat` to \"verify\" a path the\n * model already inspected through a dedicated tool is a measurable Kimi/Sonnet\n * failure mode. Each entry maps the canonical tool name to the line rendered\n * in the description; the rendered tool name itself is alias-resolved at\n * render time so deployments that wire-rename (e.g. `read_file` → `Read`)\n * still point at a name the model recognizes from the tool spec.\n */\nconst SHELL_SWAP_HINTS: ReadonlyArray<{ canonical: string, label: string, instead: string }> = [\n { canonical: 'read_file', label: 'Read files', instead: 'cat/head/tail' },\n { canonical: 'glob', label: 'File search', instead: 'find/ls' },\n { canonical: 'grep', label: 'Content search', instead: 'grep/rg' },\n { canonical: 'list_files', label: 'Directory listings', instead: 'ls' },\n { canonical: 'edit', label: 'Edit files', instead: 'sed/awk' },\n { canonical: 'write_file', label: 'Write files', instead: 'echo>/heredoc' },\n]\n\n/**\n * Build the `shell` tool's description text.\n *\n * - The background-mode paragraphs are appended only when `allowBackground`\n * is true so the model isn't pointed at a feature the agent has disabled.\n * - The \"prefer the dedicated tool\" swap block is appended only for siblings\n * actually present in `registeredCanonicals`. Hosts that ship `shell`\n * without `read_file`/`glob`/etc. don't see misleading nudges. Aliased\n * names are rendered via `toolAliases` so the printed name matches what\n * the model sees in the tool spec.\n */\nfunction buildShellDescription({\n allowBackground,\n registeredCanonicals,\n toolAliases,\n}: {\n allowBackground: boolean\n registeredCanonicals?: ReadonlySet<string>\n toolAliases?: Record<string, string>\n}): string {\n const lines = [\n 'Execute a shell command in the project root and return its combined stdout/stderr.',\n 'Output is tail-priority truncated at 32 KiB by default; errors and exit-code summaries live in the tail.',\n 'By default each call appends a `(exit N, Nms)` footer and surfaces non-empty stderr in a separate section even on success — set `metadata: false` to return only stdout. Set maxOutputBytes=0 to disable truncation.',\n '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 })`.',\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.${bgQualifier}` },\n maxOutputBytes: { type: 'integer', description: `Truncate combined stdout+stderr beyond this many bytes. Default: 32768. Set 0 for unlimited.${bgQualifierOutput}` },\n metadata: { type: 'boolean', description: `Append \\`(exit N, Nms)\\` footer and surface non-empty stderr on success. Default: true.${bgQualifier}` },\n }\n if (allowBackground) {\n properties.run_in_background = { type: 'boolean', description: 'Start the command in the background, returning a task handle. See the tool description for the full flow.' }\n }\n return {\n type: 'object',\n properties,\n required: ['command'],\n }\n}\n\nexport interface CreateShellToolOptions {\n /**\n * Whether to expose the `run_in_background` flag in the input schema +\n * the background-mode paragraphs in the description. When `false`, the\n * model never sees the flag and won't try to use it. The execute path\n * still has a defensive fallback: an explicit `run_in_background: true`\n * call (e.g. from a hand-crafted message) returns a clean error rather\n * than silently running foreground.\n *\n * Default: `true`.\n */\n allowBackground?: boolean\n\n /**\n * Canonical names of tools registered alongside `shell` on the same\n * agent. When non-empty, the description gains a \"prefer the dedicated\n * tool\" block for each known sibling (`read_file`, `glob`, `grep`,\n * `list_files`, `edit`, `write_file`) — useful against the\n * `ls`/`cat`-to-re-verify loop some models fall into when both a\n * dedicated tool AND `shell` are visible. Unknown / unrecognized names\n * are ignored.\n *\n * Set by `createAgent` per-run from the tool registry; hosts that\n * construct a `shell` directly can pass it explicitly. Omit to suppress\n * the block entirely (no nudge for shell-only agents, no nudge for\n * hosts that prefer to author their own anti-loop prose).\n */\n registeredCanonicals?: ReadonlySet<string>\n\n /**\n * The agent's `toolAliases` map, used to render the wire-level name of\n * each sibling in the swap block. Without this, the block always prints\n * canonical names — fine for the default preset, wrong for hosts that\n * alias-rename (the model would be told to call a name it doesn't see\n * in the tool spec).\n */\n toolAliases?: Record<string, string>\n}\n\n/**\n * Factory for the `shell` tool. The default exported `shell` is\n * equivalent to `createShellTool({ allowBackground: true })`. The\n * factory is the entry point hosts use when they want to override the\n * default — e.g. to ship a preset that always disables background mode\n * regardless of `behavior.tasksDir`.\n *\n * Hosts that use the framework's `createAgent` typically don't need to\n * call this directly: when `behavior.tasksDir` is unset or\n * `behavior.disableBackgroundTasks: true` is set, the agent\n * automatically rewrites the registered `shell` (if it's the\n * framework's built-in) using this factory.\n */\nexport function createShellTool(opts: CreateShellToolOptions = {}): ToolDef {\n const allowBackground = opts.allowBackground !== false\n const { registeredCanonicals, toolAliases } = opts\n return {\n // Conditional concurrency safety. Read-only commands (`ls`, `cat`,\n // `git status`, `rg`, …) fan out with other safe siblings; anything\n // mutating or ambiguous barriers. See {@link isReadOnlyShellCommand}\n // for the exact allow-list.\n isConcurrencySafe: input => isReadOnlyShellCommand(input.command),\n spec: {\n name: 'shell',\n description: buildShellDescription({ allowBackground, registeredCanonicals, toolAliases }),\n inputSchema: buildShellInputSchema({ allowBackground }),\n },\n async execute({ command, timeout, maxOutputBytes, metadata, run_in_background }, ctx: ToolContext) {\n const cmd = command as string\n\n // Background mode — dispatches via `execBackground` and returns\n // immediately. The model gets a structured one-liner naming the\n // task id + output path; the actual completion notification arrives\n // on the next turn via the agent's `<task-notification>` injection\n // (see `pendingTaskNotifications` in `src/agent.ts`).\n //\n // Defense-in-depth: when this tool variant has `allowBackground:\n // false` the field isn't in the schema, but a forged input could\n // still set it — surface a clean error instead of silently\n // running foreground (which would surprise the caller).\n if (run_in_background === true) {\n if (!allowBackground)\n return 'shell error: background mode is disabled for this agent (no `behavior.tasksDir` set, or `behavior.disableBackgroundTasks: true`). Fall back to foreground (drop `run_in_background`).'\n return runBackground(cmd, ctx)\n }\n\n // `ExecutionContext.exec` accepts a timeout in seconds; the tool surface\n // takes it in milliseconds so the value lines up with the rest of zidane's\n // ms-based timing. Round up so a 500ms request doesn't collapse to 0.\n //\n // `signal` is what makes `agent.cancelTool(callId)` actually reach\n // the underlying OS process. The loop hands us a unioned signal\n // (`AbortSignal.any([ctx.signal_run, perCallAbort.signal])`) via\n // `ctx.signal`; forwarding it into `exec` lets `child_process.exec`\n // send SIGTERM the moment any of the three layers (run abort,\n // per-call cancel, sibling-cascade) aborts. Without this, a\n // `sleep 60` survived its own cancellation message — orphaned\n // process, observable via `ps` after the run had supposedly\n // finished.\n const execOpts: { timeout?: number, signal?: AbortSignal } = { signal: ctx.signal }\n if (typeof timeout === 'number' && Number.isFinite(timeout) && timeout > 0)\n execOpts.timeout = Math.max(1, Math.ceil(timeout / 1000))\n\n const wantMetadata = metadata !== false\n const startedAt = Date.now()\n const result = await ctx.execution.exec(ctx.handle, cmd, execOpts)\n const durationMs = Date.now() - startedAt\n\n const cap = normalizeCap(maxOutputBytes)\n const semantic = interpretShellResult(cmd, result.exitCode)\n\n // True success (exit 0): stdout body, tail-truncated. With metadata,\n // surface non-empty stderr in a separate section (warnings, deprecation\n // notices) and append an `(exit 0, Nms)` footer.\n if (result.exitCode === 0) {\n const stdoutTail = truncateTail(result.stdout || '(no output)', cap)\n if (!wantMetadata)\n return stdoutTail\n const stderrTrimmed = result.stderr.trim()\n const stderrSection = stderrTrimmed\n ? `\\n[stderr]\\n${truncateTail(stderrTrimmed, Math.min(cap, 2048))}`\n : ''\n return `${stdoutTail}${stderrSection}\\n(exit 0, ${durationMs}ms)`\n }\n\n // Per-command semantic override: non-zero exit codes that are NOT errors\n // (grep no-match, diff differ, find partial, test false). Don't surface\n // `Exit code N` — that misleads the model into retrying. Append a short\n // footer with the semantic interpretation so the model sees the why.\n if (!semantic.isError) {\n const body = (result.stdout || result.stderr || '').trim()\n const tail = truncateTail(body, cap)\n const semanticFooter = semantic.message ? `\\n(${semantic.message})` : ''\n const timingFooter = wantMetadata ? `\\n(exit ${result.exitCode}, ${durationMs}ms)` : ''\n const head = tail.length > 0 ? tail : (semantic.message ?? '(no output)')\n return `${head}${semanticFooter}${timingFooter}`\n }\n\n // Real failure → keep the `Exit code N` line outside the truncation budget.\n // Truncating the joined body otherwise drops the exit-code prefix (it sits at\n // the head) the moment stdout overflows, leaving the model unable to tell\n // success from failure on large-output commands.\n const combined = `${result.stdout}\\n${result.stderr}`.trim()\n const header = wantMetadata\n ? `Exit code ${result.exitCode} (${durationMs}ms)`\n : `Exit code ${result.exitCode}`\n return `${header}\\n${truncateTail(combined, cap)}`\n },\n }\n}\n\n/**\n * Default `shell` tool with background mode enabled.\n *\n * Most hosts use this directly via `basicTools`. When the agent's\n * `behavior.tasksDir` is unset OR `behavior.disableBackgroundTasks:\n * true` is set, `createAgent` auto-rewrites this identity to a\n * `createShellTool({ allowBackground: false })` variant so the model\n * never sees a flag it can't use. Hosts who want to bypass that\n * auto-rewrite can register a `createShellTool({ allowBackground })`\n * directly — the rewrite only fires on identity-equal references to\n * this constant.\n */\nexport const shell: ToolDef = createShellTool({ allowBackground: true })\n\n/**\n * Background-mode entry point for the `shell` tool. Settles fast,\n * registers an `onExit` callback that fires `background:exit` on the\n * agent's hook bus, and returns a structured one-liner to the model.\n *\n * Reachable only via the `allowBackground: true` variant; the\n * `allowBackground: false` variant short-circuits before this is\n * called and `createAgent` auto-rewrites the built-in to the\n * `false` variant when `behavior.tasksDir` is unset or\n * `behavior.disableBackgroundTasks: true` is set. The runtime checks\n * below are defense-in-depth for hosts who skip the auto-rewrite\n * (custom shell tool, run-level tools override, etc.):\n *\n * - `behavior.tasksDir` unset → host opted into the schema but\n * forgot to wire the log dir. Clean error, model can fall back to\n * foreground.\n * - `ctx.execution.execBackground` undefined → the context doesn't\n * support it (some remote sandboxes).\n * - `mkdir` on the output dir fails → the underlying filesystem\n * can't accommodate. Surface the error verbatim.\n */\nasync function runBackground(command: string, ctx: ToolContext): Promise<string> {\n const tasksDir = ctx.behavior?.tasksDir\n if (typeof tasksDir !== 'string' || tasksDir.length === 0) {\n return 'shell error: background mode requires `behavior.tasksDir` to be set on the agent. The host has not opted into background tasks — either fall back to foreground (drop `run_in_background`) or ask the user to enable it.'\n }\n if (!ctx.execution.execBackground) {\n return `shell error: the active execution context (${ctx.execution.type}) does not support background tasks. Fall back to foreground (drop \\`run_in_background\\`).`\n }\n\n try {\n const handle = await ctx.execution.execBackground(ctx.handle, command, {\n outputDir: tasksDir,\n onExit: (info) => {\n // Fire-and-forget. `callHook` returns a Promise (it may run\n // async listeners); we don't await — the agent's listener is\n // sync map.set, and the lifecycle is \"task already exited;\n // notify whoever cares\". Surface unhandled rejections under\n // ZIDANE_DEBUG only.\n Promise.resolve(ctx.hooks.callHook('background:exit', info))\n .catch((err: unknown) => {\n if (process.env.ZIDANE_DEBUG)\n process.stderr.write(`[zidane/shell] background:exit hook rejected: ${err instanceof Error ? err.message : String(err)}\\n`)\n })\n },\n })\n\n // Fire `background:start` AFTER the handle is returned so listeners\n // see a usable id + pid + path. Observational only — no caller\n // depends on this hook for behavior, so we don't await it on the\n // critical path.\n Promise.resolve(ctx.hooks.callHook('background:start', {\n taskId: handle.taskId,\n pid: handle.pid,\n command,\n cwd: ctx.handle.cwd,\n outputPath: handle.outputPath,\n startedAt: Date.now(),\n })).catch((err: unknown) => {\n if (process.env.ZIDANE_DEBUG)\n process.stderr.write(`[zidane/shell] background:start hook rejected: ${err instanceof Error ? err.message : String(err)}\\n`)\n })\n\n const cmdPreview = previewLine(command, 60)\n // Depth-aware wake-up instruction. Subagents (depth > 0) get a\n // run-once `agent.run()` so there's no \"next turn\" for them to\n // observe the notification on; the notification ends up on the\n // PARENT's next user-turn after `spawn.ts`'s `reassignBackgroundTasks`\n // transfers ownership at child-destroy time. Telling a subagent to\n // \"wait for your next turn\" misleads it into polling, which blocks\n // the parent's spawn call until maxTurns hits.\n const inSubagent = (ctx.depth ?? 0) > 0\n const wakeupHint = inSubagent\n ? 'You are inside a `spawn`\\'d subagent — you have NO next user-turn to receive the notification on. Return a brief summary INCLUDING this task_id and end your turn now. Ownership of the task transfers to the parent agent when your run completes; the parent will see the `<task-notification>` on its next user-turn.'\n : 'The task is running in the background. You\\'ll receive a <task-notification> on your NEXT user-turn with the final status. End your current turn now — do NOT poll the output file in a loop, the notification IS the wake-up. To inspect progress, call `read_file({ path: <output> })` once. To terminate, use `shell_kill({ task_id })`.'\n return [\n `Started ${handle.taskId} (pid ${handle.pid}).`,\n ` command: ${cmdPreview}`,\n ` output: ${handle.outputPath}`,\n '',\n wakeupHint,\n ].join('\\n')\n }\n catch (err) {\n const msg = err instanceof Error ? err.message : String(err)\n return `shell error: failed to start background task: ${msg}`\n }\n}\n\nfunction normalizeCap(value: unknown): number {\n if (typeof value !== 'number' || !Number.isFinite(value))\n return DEFAULT_MAX_OUTPUT_BYTES\n if (value < 0)\n return DEFAULT_MAX_OUTPUT_BYTES\n return Math.floor(value)\n}\n\n/**\n * Tail-priority byte truncation. When `text` exceeds `cap` bytes, the head is\n * dropped and replaced with a marker. Always cuts on character boundaries (no\n * mid-codepoint splits) by walking from the end with `Buffer.byteLength`.\n *\n * `cap === 0` disables truncation. `cap` is interpreted as a UTF-8 byte budget\n * for the tail itself — the marker is added on top and may push the visible\n * length slightly past `cap`. That tradeoff is intentional: a marker that\n * always fits inside the budget would shrink the actual content displayed.\n */\nfunction truncateTail(text: string, cap: number): string {\n if (cap === 0)\n return text\n\n const totalBytes = Buffer.byteLength(text)\n if (totalBytes <= cap)\n return text\n\n // Walk back from the end, accumulating UTF-8 bytes per character, until\n // we've gathered up to `cap` bytes. Slicing on char index ensures we never\n // split a multi-byte codepoint mid-stream.\n let bytes = 0\n let charIdx = text.length\n while (charIdx > 0) {\n const ch = text[charIdx - 1]\n const chBytes = Buffer.byteLength(ch)\n if (bytes + chBytes > cap)\n break\n bytes += chBytes\n charIdx--\n }\n\n const tail = text.slice(charIdx)\n const droppedBytes = totalBytes - Buffer.byteLength(tail)\n return `…(${droppedBytes} bytes truncated from head)…\\n${tail}`\n}\n","/**\n * Heuristics for detecting binary content in UTF-8-decoded strings.\n *\n * `ExecutionContext.readFile` always returns text. Invalid bytes survive\n * decoding as U+FFFD (replacement char), and genuinely binary files often\n * contain NUL bytes — UTF-8 text legitimately should not. These helpers\n * give tools a cheap way to bail before drowning the model in mojibake.\n */\n\nconst SNIFF_BYTES = 8192\nconst REPLACEMENT_RATIO_THRESHOLD = 0.01\nconst REPLACEMENT_MIN_COUNT = 5\n\n/**\n * True if a NUL (`\\x00`) appears in the leading sample. Cheap, no false\n * positives on real text — UTF-8 never embeds NUL, so any NUL means the\n * source bytes were binary.\n */\nexport function containsNullByte(text: string, sniffBytes = SNIFF_BYTES): boolean {\n const sample = text.length > sniffBytes ? text.slice(0, sniffBytes) : text\n for (let i = 0; i < sample.length; i++) {\n if (sample.charCodeAt(i) === 0)\n return true\n }\n return false\n}\n\n/**\n * Heavier check used by `read_file`:\n * - NUL byte ⇒ binary,\n * - or U+FFFD count ≥ minimum AND ratio over threshold ⇒ binary.\n *\n * The replacement-char ratio + minimum guards against tripping on a\n * one-or-two-stray-replacement-char text file (config dumps, editor logs).\n */\nexport function looksBinary(text: string, sniffBytes = SNIFF_BYTES): boolean {\n const sample = text.length > sniffBytes ? text.slice(0, sniffBytes) : text\n if (sample.length === 0)\n return false\n\n let replacementCount = 0\n for (let i = 0; i < sample.length; i++) {\n const code = sample.charCodeAt(i)\n if (code === 0)\n return true\n if (code === 0xFFFD)\n replacementCount++\n }\n return replacementCount >= REPLACEMENT_MIN_COUNT\n && replacementCount / sample.length > REPLACEMENT_RATIO_THRESHOLD\n}\n","/**\n * `skills_read` tool — reads a bundled resource file from an active skill.\n *\n * Requires the skill to be active (model must have called `skills_use` first).\n * Paths are validated against the skill's `baseDir` to prevent directory\n * traversal. File I/O goes through `ctx.execution.readFile` so docker/sandbox\n * execution contexts work identically to in-process.\n */\n\nimport type { SkillActivationState } from '../skills/activation'\nimport type { SkillConfig } from '../skills/types'\nimport type { ToolContext, ToolDef } from './types'\nimport { errorMessage } from '../errors'\nimport { validateResourcePath } from '../skills/validate'\nimport { containsNullByte } from './binary-detect'\n\nexport interface SkillsReadToolOptions {\n catalog: readonly SkillConfig[]\n state: SkillActivationState\n}\n\nexport function createSkillsReadTool(options: SkillsReadToolOptions): ToolDef {\n const byName = new Map(options.catalog.map(s => [s.name, s]))\n\n return {\n // Read-only over the skill catalog — concurrency-safe.\n isConcurrencySafe: true,\n spec: {\n name: 'skills_read',\n description:\n 'Read a bundled resource file from an active skill. '\n + 'The skill must have been activated via skills_use first. '\n + 'Path is relative to the skill\\'s directory (e.g. \"references/REFERENCE.md\", \"assets/template.txt\").',\n inputSchema: {\n type: 'object',\n properties: {\n name: {\n type: 'string',\n enum: options.catalog.map(s => s.name),\n description: 'The name of the active skill.',\n },\n path: {\n type: 'string',\n description: 'Path to the resource, relative to the skill root. Cannot escape the skill directory.',\n },\n },\n required: ['name', 'path'],\n additionalProperties: false,\n },\n },\n\n async execute(input, ctx: ToolContext): Promise<string> {\n const skillName = input.name as string\n const relPath = input.path as string\n\n const skill = byName.get(skillName)\n if (!skill)\n return `Error: unknown skill \"${skillName}\".`\n\n if (!options.state.isActive(skillName))\n return `Error: skill \"${skillName}\" is not active. Call skills_use with name: \"${skillName}\" first.`\n\n if (!skill.baseDir) {\n return (\n `Error: skill \"${skillName}\" has no base directory `\n + '(likely an inline skill without bundled resources); cannot read files.'\n )\n }\n\n const validated = validateResourcePath(relPath, skill.baseDir)\n if (!validated.valid)\n return `Error: ${validated.error}`\n\n let content: string\n try {\n content = await ctx.execution.readFile(ctx.handle, validated.absolutePath)\n }\n catch (err) {\n return `Error reading \"${relPath}\" in skill \"${skillName}\": ${errorMessage(err)}`\n }\n\n if (containsNullByte(content)) {\n // `ExecutionContext.readFile` returns a UTF-8 string — invalid UTF-8\n // bytes get replacement chars, so a round-trip through base64 would\n // corrupt the payload. Instead of pretending we can deliver binary\n // bytes, return a descriptor that tells the agent where the file is.\n // A future `readFileBytes(handle, path)` API on ExecutionContext\n // would unlock proper inline delivery; until then, host adapters\n // needing binaries should stream them out-of-band.\n return JSON.stringify({\n kind: 'binary-unsupported',\n path: validated.absolutePath,\n note:\n 'This file appears to be binary. The skills_read tool returns text only; '\n + 'binary files are not delivered through the execution context\\'s text-based readFile API.',\n })\n }\n\n return content\n },\n }\n}\n","/**\n * Shared shell-argument quoter for tool implementations.\n *\n * Single source of truth so `grep`, `binary-read`, and `skills-run-script`\n * don't drift on the POSIX `'\\''` escape pattern.\n */\n\nconst SAFE_TOKEN_RE = /^[\\w@%+=:,./-]+$/\nconst SINGLE_QUOTE_RE = /'/g\n\n/**\n * Wrap an argument in single quotes, escaping embedded single quotes via the\n * standard POSIX `'\\''` close-escape-reopen trick. Tokens that are already\n * shell-safe pass through unchanged so command lines stay readable in logs.\n *\n * NOT a sandbox — only safe when the caller controls the surrounding shell\n * context. Use it for arguments only, never for the verb / subcommand.\n */\nexport function shellQuote(arg: string): string {\n if (SAFE_TOKEN_RE.test(arg))\n return arg\n return `'${arg.replace(SINGLE_QUOTE_RE, '\\'\\\\\\'\\'')}'`\n}\n\n/**\n * Variant that always quotes — useful when the caller doesn't want a\n * conditional `unquoted-when-safe` branch (consistent log shape, paranoid\n * inputs that contain whitespace by construction).\n */\nexport function alwaysQuote(arg: string): string {\n return `'${arg.replace(SINGLE_QUOTE_RE, '\\'\\\\\\'\\'')}'`\n}\n","/**\n * `skills_run_script` tool — executes a script from an active skill's\n * `scripts/` directory via the agent's execution context.\n *\n * Path is validated against the skill's `baseDir` and constrained to the\n * `scripts/` subdirectory. Timeout is configurable via\n * `SkillsConfig.scriptTimeoutMs` (default 60 s).\n */\n\nimport type { SkillActivationState } from '../skills/activation'\nimport type { SkillConfig } from '../skills/types'\nimport type { ToolContext, ToolDef } from './types'\nimport { errorMessage } from '../errors'\nimport { validateResourcePath } from '../skills/validate'\nimport { alwaysQuote } from './shell-quote'\n\nexport interface SkillsRunScriptToolOptions {\n catalog: readonly SkillConfig[]\n state: SkillActivationState\n /** Script timeout in milliseconds. Default 60000. */\n scriptTimeoutMs?: number\n}\n\nconst ABS_WINDOWS_RE = /^[a-z]:[\\\\/]/i\nconst COLLAPSE_SLASHES_RE = /\\/+/g\n\nexport function createSkillsRunScriptTool(options: SkillsRunScriptToolOptions): ToolDef {\n const byName = new Map(options.catalog.map(s => [s.name, s]))\n const timeoutMs = options.scriptTimeoutMs ?? 60_000\n\n return {\n spec: {\n name: 'skills_run_script',\n description:\n 'Execute a script bundled with an active skill (from its scripts/ directory). '\n + 'The skill must have been activated via skills_use first. '\n + 'Returns stdout, stderr, and the exit code. Honors the script\\'s shebang.',\n inputSchema: {\n type: 'object',\n properties: {\n name: {\n type: 'string',\n enum: options.catalog.map(s => s.name),\n description: 'The name of the active skill.',\n },\n script: {\n type: 'string',\n description: 'Path to the script relative to the skill\\'s scripts/ directory (e.g. \"extract.py\", \"merge.sh\").',\n },\n args: {\n type: 'array',\n items: { type: 'string' },\n description: 'Optional argv array passed to the script.',\n },\n },\n required: ['name', 'script'],\n additionalProperties: false,\n },\n },\n\n async execute(input, ctx: ToolContext): Promise<string> {\n const skillName = input.name as string\n const scriptRel = input.script as string\n const args = (input.args as string[] | undefined) ?? []\n\n const skill = byName.get(skillName)\n if (!skill)\n return `Error: unknown skill \"${skillName}\".`\n\n if (!options.state.isActive(skillName))\n return `Error: skill \"${skillName}\" is not active. Call skills_use with name: \"${skillName}\" first.`\n\n if (!skill.baseDir)\n return `Error: skill \"${skillName}\" has no base directory (likely an inline skill); cannot run scripts.`\n\n // Reject absolute script paths up front — otherwise they'd be spuriously\n // \"normalized\" under scripts/ by the join below.\n if (scriptRel.startsWith('/') || ABS_WINDOWS_RE.test(scriptRel))\n return `Error: Absolute paths are not allowed (\"${scriptRel}\").`\n\n // Resolve under scripts/ and validate — rejects escapes via `..`.\n const joinedPath = `scripts/${scriptRel}`.replace(COLLAPSE_SLASHES_RE, '/')\n const validated = validateResourcePath(joinedPath, skill.baseDir)\n if (!validated.valid)\n return `Error: ${validated.error}`\n\n // Build a shell command that honors shebang (via POSIX exec). For\n // languages without shebang (e.g. Python on Windows-authored files),\n // the script's first-line `#!/usr/bin/env python3` handles it.\n const cmd = [validated.absolutePath, ...args].map(alwaysQuote).join(' ')\n try {\n // Forward `ctx.signal` so a per-call cancel (or run abort) sends\n // SIGTERM to the script — same rationale as the shell tool: without\n // it, a long-running script keeps consuming compute after the\n // model has already moved on with the cancellation marker.\n const result = await ctx.execution.exec(ctx.handle, cmd, {\n timeout: Math.max(1, Math.round(timeoutMs / 1000)),\n signal: ctx.signal,\n })\n return JSON.stringify({\n exitCode: result.exitCode,\n stdout: result.stdout,\n stderr: result.stderr,\n })\n }\n catch (err) {\n return `Error running script \"${scriptRel}\" for skill \"${skillName}\": ${errorMessage(err)}`\n }\n },\n }\n}\n","/**\n * `skills_use` tool — activates or deactivates a skill from the model side.\n *\n * Implements tier 2 of progressive disclosure per the Agent Skills spec.\n *\n * Two modes:\n *\n * - `mode: \"activate\"` (default, spec-mandated) — loads the skill's full\n * instructions, fires `skills:activate` with `via: 'model'`, and\n * returns the frontmatter-stripped body wrapped in `<skill_content>`\n * tags. Shell-interpolation (`!`` `cmd` ``) runs per-activation so\n * values like `gh pr diff` reflect the current state.\n * - `mode: \"deactivate\"` (zidane extension) — releases an active skill,\n * fires `skills:deactivate` with `reason: 'model'`. The matching\n * skill's `allowed-tools` restrictions stop applying on the next\n * dispatch. Use this when the skill's job is done and its tool\n * allow-list is now blocking unrelated follow-up work.\n *\n * The deactivate mode is what lets the model recover from a stuck\n * `AgentToolNotAllowedError` (the canonical hint of which points the\n * model right back here) without waiting for a run boundary.\n */\n\nimport type { Hookable } from 'hookable'\nimport type { AgentHooks } from '../agent'\nimport type { SkillActivationState } from '../skills/activation'\nimport type { SkillConfig } from '../skills/types'\nimport type { ToolContext, ToolDef } from './types'\nimport { interpolateShellCommands } from '../skills/interpolate'\nimport { escapeXml } from '../xml'\n\nexport interface SkillsUseToolOptions {\n /** Resolved skills catalog for this run. */\n catalog: readonly SkillConfig[]\n /** Per-agent activation state the tool mutates. */\n state: SkillActivationState\n /** Agent hooks — used to fire `skills:activate` on first activation. */\n hooks: Hookable<AgentHooks>\n}\n\nconst MAX_RESOURCE_LIST = 50\n\nfunction buildSkillContentWrapper(skill: SkillConfig, body: string): string {\n const parts: string[] = []\n parts.push(`<skill_content name=\"${escapeXml(skill.name)}\" spec_version=\"0.1\">`)\n parts.push(body)\n\n if (skill.baseDir) {\n parts.push('')\n parts.push(`Skill directory: ${skill.baseDir}`)\n parts.push('Relative paths resolve against this directory.')\n }\n\n if (skill.resources?.length) {\n parts.push('')\n parts.push('<skill_resources>')\n const shown = skill.resources.slice(0, MAX_RESOURCE_LIST)\n for (const res of shown) {\n parts.push(` <file type=\"${res.type}\">${escapeXml(res.path)}</file>`)\n }\n if (skill.resources.length > MAX_RESOURCE_LIST) {\n parts.push(` <!-- …(${skill.resources.length - MAX_RESOURCE_LIST} more) -->`)\n }\n parts.push('</skill_resources>')\n }\n\n if (skill.compatibility) {\n parts.push('')\n parts.push(`Compatibility: ${skill.compatibility}`)\n }\n\n if (skill.allowedTools?.length) {\n parts.push(`Allowed tools: ${skill.allowedTools.join(' ')}`)\n }\n\n parts.push('</skill_content>')\n return parts.join('\\n')\n}\n\n/**\n * Factory for `skills_use`. Auto-injected into the agent's tool set by the\n * agent runtime when a non-empty skills catalog is available (unless\n * `SkillsConfig.tool === false`).\n *\n * The tool schema's `name` property is `enum`-constrained to the resolved\n * catalog so the LLM cannot hallucinate a skill that doesn't exist.\n */\nexport function createSkillsUseTool(options: SkillsUseToolOptions): ToolDef {\n const byName = new Map(options.catalog.map(s => [s.name, s]))\n // Cache interpolated bodies for the lifetime of this tool instance. The\n // agent rebuilds skills tools per-run, so this cache naturally resets on\n // each run boundary — avoiding stale shell output from a prior run.\n const interpolatedBodyCache = new Map<string, string>()\n\n return {\n spec: {\n name: 'skills_use',\n description:\n 'Activate or deactivate a specialized skill. '\n + 'Call with `mode: \"activate\"` (default) to load a skill\\'s full instructions when a task matches its catalog description. '\n + 'Call with `mode: \"deactivate\"` to release a skill whose allowed-tools restrictions are now in the way — e.g. when the skill\\'s work is done or the active skill is blocking unrelated tool calls. '\n + 'After activating, follow the returned instructions; use skills_read to load referenced files and skills_run_script to execute bundled scripts.',\n inputSchema: {\n type: 'object',\n properties: {\n name: {\n type: 'string',\n enum: options.catalog.map(s => s.name),\n description: 'The name of the skill to activate or deactivate (must be in the available skills catalog).',\n },\n mode: {\n type: 'string',\n enum: ['activate', 'deactivate'],\n description: 'Whether to activate (load + apply the skill) or deactivate (release an active skill). Default: \"activate\".',\n },\n },\n required: ['name'],\n additionalProperties: false,\n },\n },\n\n async execute(input, ctx: ToolContext): Promise<string> {\n const skillName = input.name as string\n const skill = byName.get(skillName)\n if (!skill) {\n const available = [...byName.keys()].join(', ') || '<none>'\n return `Error: unknown skill \"${skillName}\". Available skills: ${available}.`\n }\n\n const mode = (input.mode as string | undefined) ?? 'activate'\n\n if (mode === 'deactivate') {\n const removed = options.state.deactivate(skillName)\n if (!removed) {\n // Idempotent: deactivating a non-active skill is a no-op rather\n // than an error — the model has no reliable way to know whether a\n // prior run-end pass already cleared the activation, and a\n // confirmation is more useful than a failure here.\n return `Skill \"${skillName}\" was not active — nothing to deactivate.`\n }\n await options.hooks.callHook('skills:deactivate', { skill: removed.skill, reason: 'model' })\n const remaining = options.state.active().map(a => a.skill.name)\n const tail = remaining.length > 0\n ? ` Remaining active skills: ${remaining.join(', ')}.`\n : ' No skills are currently active.'\n return `Skill \"${skillName}\" deactivated — its allowed-tools restrictions no longer apply.${tail}`\n }\n\n const wasActive = options.state.isActive(skillName)\n\n if (!wasActive) {\n const outcome = options.state.activate(skill, 'model')\n if (outcome === 'cap-reached') {\n const activeNames = options.state.active().map(a => a.skill.name).join(', ')\n return (\n `Error: cannot activate \"${skillName}\" — the maxActive skill cap has been reached. `\n + `Currently active: ${activeNames}. Call \\`skills_use\\` with \\`mode: \"deactivate\"\\` and one of those names first.`\n )\n }\n await options.hooks.callHook('skills:activate', { skill, via: 'model' })\n }\n\n // Interpolate `!\\`cmd\\`` once per (skill, tool-instance). A second\n // `skills_use` call on the same skill returns the cached body.\n let body = interpolatedBodyCache.get(skillName)\n if (body === undefined) {\n body = skill.instructions.includes('!`')\n ? await interpolateShellCommands(skill.instructions, ctx.execution, ctx.handle)\n : skill.instructions\n interpolatedBodyCache.set(skillName, body)\n }\n\n return buildSkillContentWrapper(skill, body)\n },\n }\n}\n","/**\n * `tool_search` — progressive tool disclosure.\n *\n * Counterpart to `skills_use` for the MCP tool surface: the catalog (name +\n * description) lives in the system prompt; full `inputSchema` payloads load\n * lazily through this tool. After a successful match, the matched tools'\n * canonical names are added to the per-run \"unlocked\" set and become callable\n * immediately (the loop rebuilds `formattedTools` before the next provider\n * request, advertising the unlocked tools to the provider).\n *\n * Aliasing: the tool advertises and matches on the **wire** (alias-rewritten)\n * name — the only name the model ever sees. Internally each lazy entry also\n * carries its `canonicalName`, which is what gets added to the `unlocked` set\n * (the loop's tool registry is keyed by canonical name).\n *\n * Result shape: structured pseudo-XML wrapping a JSON `inputSchema` payload.\n * The schema is inlined verbatim (NOT XML-escaped) so the model can reuse it\n * directly when constructing arguments — escaping the JSON would force the\n * model to mentally un-escape `&quot;` etc. when reasoning about field types.\n */\n\nimport type { ToolContext, ToolDef } from './types'\nimport { escapeXml } from '../xml'\n\nexport interface LazyToolEntry {\n /**\n * Wire name (after `toolAliases` rewrite). What the model sees in the\n * catalog, what `tool_search` matches against, and what the provider's\n * tool list will carry once the entry is unlocked.\n */\n name: string\n /**\n * Canonical (registry-key) name used for unlock-set membership and for the\n * loop's `ctx.tools[name]` dispatch lookup. Equal to `name` when no alias\n * is configured for this tool.\n */\n canonicalName: string\n description: string\n inputSchema: Record<string, unknown>\n /** Source MCP server, when applicable. Used for `server`-bulk unlock. */\n server?: string\n}\n\nexport interface ToolSearchToolOptions {\n /**\n * Snapshot of every lazy tool the model can discover. Built once per run by\n * the agent — the tool closes over this array and never mutates it.\n */\n catalog: readonly LazyToolEntry[]\n /**\n * Mutable per-run set of unlocked **canonical** tool names. The tool adds\n * matches in place; the loop reads the set when rebuilding the wire-level\n * tool list. Keyed by canonical (not wire) so dispatch lookups stay\n * alias-stable.\n *\n * 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 * Agent creation and state management.\n */\n\nimport type { Hookable } from 'hookable'\n\nimport type { ContextBreakdown, ContextBreakdownOptions, ContextExactCounts, ContextMcpGroup } from './chat/context-breakdown'\nimport type { ExecutionContext, ExecutionHandle, TaskExitInfo } from './contexts'\nimport type { McpConnection } from './mcp'\nimport type { Provider, StreamOptions, ToolResult, ToolSpec } from './providers'\nimport type { Session } from './session'\nimport type { PairingRepair } from './session/messages'\nimport type { ActivationVia, ActiveSkill, DeactivationReason } from './skills/activation'\nimport type { SkillConfig, SkillsConfig } from './skills/types'\nimport type { ReadStateMap } from './tools/read-state'\nimport type { LazyToolEntry } from './tools/tool-search'\nimport type { ToolDef } from './tools/types'\nimport type { AgentBehavior, AgentClock, AgentRunOptions, AgentStats, ChildRunStats, McpServerConfig, McpToolHookContext, McpToolSchema, OAuthRefreshHookContext, SessionContentBlock, SessionEndStatus, SessionHookContext, SessionMessage, SessionTurn, SpawnHookContext, StreamHookContext, ToolHookContext, ToolResultContent, TurnUsage } from './types'\nimport { resolve as pathResolve } from 'node:path'\nimport { createHooks } from 'hookable'\nimport { augmentMcpDoubleUnderscoreAliases, buildAliasMaps } from './aliasing'\nimport { buildContextBreakdown } from './chat/context-breakdown'\nimport { formatTaskSummary } from './chat/format'\nimport { OUTPUT_RESERVE_TOKENS } from './chat/providers'\nimport { createProcessContext } from './contexts'\nimport { installDedupToolsGate } from './dedup-tools'\nimport { AgentBudgetExceededError, errorMessage } from './errors'\nimport { runLoop } from './loop'\nimport { connectMcpServers } from './mcp'\nimport { buildPromptMessage, canonicalizePrompt } from './prompt'\nimport { installRepeatGuard } from './repeat-guard'\nimport { detectTurnInterruption, filterUnresolvedToolUses, SYNTHETIC_TOOL_RESULT_PLACEHOLDER } from './session/messages'\nimport { createSkillActivationState } from './skills/activation'\nimport { installAllowedToolsGate } from './skills/allowed-tools'\nimport { buildCatalog } from './skills/catalog'\nimport { resolveSkills } from './skills/resolve'\nimport { effectiveInputFromTurn } from './stats'\nimport { appendStaticSection, renderSystemForWire } from './system-prompt'\nimport { installToolBudgetsGate } from './tool-budgets'\nimport { shell as builtinShell, createShellTool } from './tools/shell'\nimport { createSkillsReadTool } from './tools/skills-read'\nimport { createSkillsRunScriptTool } from './tools/skills-run-script'\nimport { createSkillsUseTool } from './tools/skills-use'\nimport { applyToolSearchToUnlocked, createToolSearchTool } from './tools/tool-search'\nimport { DEFAULT_AGENT_CLOCK } from './types'\nimport { escapeXml } from './xml'\n\n// ---------------------------------------------------------------------------\n// 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 'turn:after': (ctx: {\n turn: number\n turnId: string\n usage: TurnUsage\n message: SessionTurn\n toolCounts: {\n turn: Readonly<Record<string, number>>\n run: Readonly<Record<string, number>>\n }\n /**\n * Run-cumulative token + cost rollup **including this turn**. Computed\n * locally in the loop from `priorUsage + usage`. Lets observability\n * consumers stamp running totals onto turn spans / metrics without\n * re-accumulating across handlers.\n *\n * `turns` is the number of completed parent-loop turns inclusive of this\n * one (1-indexed for the first turn). `cost` is summed from per-turn\n * `TurnUsage.cost` reports; absent when nothing reported a non-zero cost.\n *\n * Children's spend is NOT folded in here — `turn:after` fires on parent\n * turns; child rollup lands on `spawn:complete` / final `agent:done`.\n */\n cumulativeUsage: Readonly<{\n input: number\n output: number\n cacheRead: number\n cacheCreation: number\n cost?: number\n turns: number\n }>\n }) => void\n\n /**\n * Fires after a tool-results user turn is pushed onto the conversation,\n * before* any byte-budget enforcement or follow-up steering. Symmetric\n * with `turn:after` but for the user-role tool_result turn that closes the\n * round-trip.\n *\n * Why it exists: persistence layers must write tool_use/tool_result turn\n * pairs together — if only the assistant turn (carrying tool_use blocks)\n * is durable, a process death between turns leaves the DB with an orphan\n * tool_use that Anthropic rejects on resume. Subscribe here to persist\n * the tool-results turn before the loop continues.\n */\n 'tool-results:after': (ctx: {\n turn: number\n turnId: string\n message: SessionTurn\n /** Frozen list of tool results that were packed into `message`. */\n results: readonly ToolResult[]\n }) => void\n\n // Streaming\n /**\n * Fires immediately before the loop calls `provider.stream()`. Pairs with\n * `stream:end` / `stream:error`; lets observers timestamp the moment the\n * request leaves the client to compute wait-vs-generate split and TTFT\n * relative to provider dispatch (not run start).\n *\n * `startedAt` is `Date.now()` at fire time. Observational — handlers\n * cannot mutate the in-flight `StreamOptions` (use `turn:before` /\n * `context:transform` / `system:transform` for that).\n */\n 'stream:start': (ctx: StreamHookContext & { startedAt: number }) => void\n 'stream:text': (ctx: StreamHookContext & { delta: string, text: string }) => void\n 'stream:end': (ctx: StreamHookContext & { text: string }) => void\n 'stream:thinking': (ctx: StreamHookContext & { delta: string, thinking: string }) => void\n /**\n * Fires 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 'oauth:refresh': (ctx: OAuthRefreshHookContext) => void\n\n // Tool execution\n /**\n * Fires before validation, `tool:before`, and `execute`. Two ways to\n * intercept:\n *\n * - Set `block = true` (with a `reason`) to refuse the call. The model\n * sees a `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 '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 * Pair with `tool:after` (which also fires uniformly across paths) to\n * get a guaranteed `tool` ↔ `tool-result` event pairing for downstream\n * consumers reconstructing message history from live events.\n *\n * Distinct from `tool:before`: `tool:before` 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 }) => void\n /**\n * Fires when a tool throws during execution. Mutate `result` to substitute a\n * tool-output payload that gets sent back to the model in place of the\n * default `Tool error: <msg>` string — useful for OSS-model error rewriting\n * (collapse stack traces, hide internal paths, prepend recovery hints).\n *\n * The post-hook value flows through `tool:transform` like a normal output, so\n * downstream byte-budgeting and image-stripping still apply.\n */\n 'tool:error': (ctx: ToolHookContext & { error: Error, result?: string | ToolResultContent[] }) => void\n 'tool:transform': (ctx: ToolHookContext & { result: string | ToolResultContent[], isError: boolean, outputBytes: number, coercions?: readonly string[] }) => void\n /**\n * Fires before the generic \"Unknown tool\" error when the model invokes a tool\n * that isn't registered (hallucinated names, dropped MCP servers, dangling\n * aliases). Mutate `result` to substitute a friendly response or set\n * `suppressError: true` to skip the companion `tool:error` emission.\n *\n * Fires for any unknown tool name — including hallucinated MCP-style names\n * (`mcp_supabase_xxx`); branch on `name.startsWith('mcp_')` to differentiate.\n */\n 'tool:unknown': (ctx: ToolHookContext & {\n result?: string | ToolResultContent[]\n suppressError: boolean\n }) => void\n /**\n * Fires when a single in-flight tool call is cancelled mid-flight via\n * `agent.cancelTool(callId)` — typically the TUI's \"cancel this tool\"\n * affordance. Distinguished from `tool:error` (the tool body threw),\n * `agent:abort` (whole run aborted), and the batch-layer\n * `INTERRUPT_MESSAGE_FOR_TOOL_USE` shape (sibling drain on full abort)\n * so consumers can split the four causes.\n *\n * Semantics:\n * - The tool's body has already received an abort signal (via the\n * combined per-call + run signal); whether it actually unwound is\n * up to the body's signal awareness.\n * - The framework returns the canonical\n * {@link TOOL_USE_CANCELLED_MESSAGE} to the model with\n * `isError: true` so the assistant turn closes cleanly and the\n * model can continue with the next batch call.\n * - `tool:after` and `tool:transform` do **not** fire for a cancelled\n * call — there's no real result the loop should account for.\n *\n * `runToolCounts` mirrors the `tool:gate` / `tool:before` snapshot for\n * this call (frozen, pre-increment) so a single per-call observer can\n * subscribe to either hook without re-walking the run.\n */\n 'tool:cancelled': (ctx: ToolHookContext & {\n reason: string\n runToolCounts: Readonly<Record<string, number>>\n }) => void\n /**\n * Fires when `validateToolArgs` rejects an input that could not be auto-coerced\n * to satisfy the tool's `inputSchema`. Observational — the tool call still\n * surfaces a `Validation error: …` string back to the model. Useful for\n * counting validation failures separately from runtime tool errors.\n */\n 'validation:reject': (ctx: ToolHookContext & {\n reason: string\n schema: Record<string, unknown>\n }) => void\n /**\n * Fires when `validateToolArgs` successfully auto-coerced one or more input\n * fields to satisfy the tool's `inputSchema`. **Only fires when at least one\n * coercion happened** — never on perfectly-shaped inputs. Useful for counting\n * model \"wrongness rate\" without re-running validation downstream.\n *\n * `coercions` lists the field names that were coerced. The values landed in\n * the input that the tool actually received; consumers wanting before/after\n * comparison can re-run `validateToolArgs(ctx.input, ctx.schema)`.\n */\n 'validation:coerce': (ctx: ToolHookContext & {\n coercions: readonly string[]\n schema: Record<string, unknown>\n }) => void\n\n // Context\n 'context:transform': (ctx: { messages: SessionMessage[] }) => void\n /**\n * Fires per request, after `context:transform` and before the request goes\n * out. Mutating `ctx.system` updates the system prompt the provider sends\n * for this turn — useful for runtime-derived sections (e.g. listing files\n * already read in the session, surfacing live tool budgets, injecting\n * skill activation reminders).\n *\n * Cache breakpoints are applied inside the provider after this hook, so\n * mutations land in the cache key naturally — repeated turns with the\n * same derived system text still hit the cache.\n *\n * `messages` is read-only here; use `context:transform` for message\n * surgery. `session` is `undefined` when the run is sessionless.\n */\n 'system:transform': (ctx: { system: string, messages: readonly SessionMessage[], turn: number, turnId: string, session?: Session }) => void\n 'steer:inject': (ctx: { message: string }) => void\n\n // Spawn\n 'spawn:before': (ctx: SpawnHookContext) => void\n 'spawn:complete': (ctx: ChildRunStats) => void\n 'spawn:error': (ctx: SpawnHookContext & { error: Error }) => void\n\n // Child hook bubbling — re-fires of a subagent's in-turn events on the\n // parent's hook bus. Each context is the original child event payload\n // augmented with `childId` (spawn id) and `depth` (subagent depth). Parents\n // opt in by listening; default behavior is silent. Grandchildren bubble\n // through their immediate parent's spawn tool, so a top-level listener\n // sees the full transitive stream.\n 'child:stream:text': (ctx: StreamHookContext & { delta: string, text: string, childId: string, depth: number }) => void\n 'child:stream:thinking': (ctx: StreamHookContext & { delta: string, thinking: string, childId: string, depth: number }) => void\n 'child:stream:end': (ctx: StreamHookContext & { text: string, childId: string, depth: number }) => void\n /** Bubbled `stream: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 childId: string\n depth: number\n }) => void\n /**\n * Same-ref bubble of a subagent's `tool:transform` hook. Parent\n * listeners may mutate `ctx.result` to rewrite the child tool's\n * output — used by the TUI to append per-edit annotation blocks\n * onto a child agent's `multi_edit` / `edit` / `write_file`\n * results when a parent-level approval gate produced a partial\n * decision.\n */\n 'child:tool:transform': (ctx: ToolHookContext & {\n result: string | ToolResultContent[]\n isError: boolean\n outputBytes: number\n coercions?: readonly string[]\n childId: string\n depth: number\n }) => void\n 'child:tool:error': (ctx: ToolHookContext & { error: Error, childId: string, depth: number }) => void\n /**\n * Bubbled sibling of {@link AgentHooks['tool:cancelled']} — fires when a\n * subagent's in-flight tool was cancelled (either directly on the parent\n * via `agent.cancelTool(callId)` reaching a child's call, or by the child\n * agent's own consumers). Symmetric with `child:tool:error` and\n * `child:tool:after` so the TUI's transcript renderer can paint the\n * cancelled badge against the right subagent row.\n */\n 'child:tool:cancelled': (ctx: ToolHookContext & {\n reason: string\n runToolCounts: Readonly<Record<string, number>>\n childId: string\n depth: number\n }) => void\n /** Bubbled from a subagent's `background:start`. Same payload + `childId` / `depth`. */\n 'child:background:start': (ctx: {\n taskId: string\n pid: number\n command: string\n cwd: string\n outputPath: string\n startedAt: number\n childId: string\n depth: number\n }) => void\n /** Bubbled from a subagent's `background:exit`. Same payload + `childId` / `depth`. */\n 'child:background:exit': (ctx: {\n taskId: string\n status: 'exited' | 'killed'\n exitCode: number\n signal?: NodeJS.Signals\n outputPath: string\n durationMs: number\n command: string\n childId: string\n depth: number\n }) => void\n /**\n * Bubbled from a subagent's `background:reassign`. Fires when a\n * grandchild's tasks get promoted to the child's handle on the\n * grandchild's destroy — gives the parent a chance to mirror the\n * ownership change in any UI it maintains.\n */\n 'child:background:reassign': (ctx: {\n taskId: string\n fromHandleId: string\n toHandleId: string\n childId: string\n pid: number\n command: string\n outputPath: string\n startedAt: number\n depth: number\n }) => void\n 'child:turn:after': (ctx: {\n turn: number\n turnId: string\n usage: TurnUsage\n message: SessionTurn\n toolCounts: { turn: Readonly<Record<string, number>>, run: Readonly<Record<string, number>> }\n childId: string\n depth: number\n }) => void\n\n // MCP server lifecycle\n /**\n * Fires once a server's client is live (transport connected, SDK handshake\n * complete). For servers bootstrapped eagerly, fires right after bootstrap;\n * for servers with `lazyConnect: true`, fires after the first `tools/call`\n * triggers the deferred connect — `lazy: true` lets the host distinguish.\n */\n 'mcp:connect': (ctx: { name: string, transport: string, tools: string[], lazy?: boolean }) => void\n 'mcp:error': (ctx: { name: string, error: Error }) => void\n /** 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 once per successful bootstrap with the **raw, pre-filter** tool\n * schemas the server advertised via `tools/list` (or that the host\n * pre-seeded via `McpServerConfig.cachedTools`). Useful for hosts that\n * want to:\n *\n * - Persist a per-server tool catalog to disk so a settings UI can\n * show every available tool — including ones currently hidden by\n * `disabledTools` — without re-bootstrapping.\n * - Build per-tool toggle UIs that reflect the full server surface.\n * - Diff successive bootstraps to detect newly-added / removed tools.\n *\n * Read-only — handlers must NOT mutate `tools`. Shape the model-facing\n * list via `mcp:tools:filter` instead (which fires immediately after\n * this hook and IS the mutation surface).\n *\n * Does NOT fire when the bootstrap fails. Does NOT fire on lazy-connect\n * servers' deferred first-call (the schemas were already known from\n * `cachedTools` at the agent-construction boundary).\n */\n 'mcp:tools:list': (ctx: { name: string, transport: 'stdio' | 'sse' | 'streamable-http', tools: readonly McpToolSchema[] }) => void\n /**\n * Fires when a server's bootstrap was skipped because it requires OAuth\n * login and no tokens are stored. The host (TUI) surfaces this as a\n * \"needs-auth\" status so the user can opt in to interactive login via\n * `loginMcpServer`. Pairs with `mcp:bootstrap:end ok:false` — both fire,\n * since the bootstrap did fail (just for a recoverable reason).\n *\n * `reason`:\n * - `'no-tokens'` — config has `auth: 'oauth'` set (or normalized from\n * Cursor's `authMethod: 'mcpOAuth'`) and the credential store is empty.\n * - `'auto-promoted'` — server returned 401 + RFC 9728 metadata. No\n * static Authorization header was set, so the server is eligible for\n * OAuth login even though the config didn't explicitly request it.\n */\n 'mcp:auth:required': (ctx: {\n name: string\n transport: 'stdio' | 'sse' | 'streamable-http'\n reason: 'no-tokens' | 'auto-promoted'\n }) => void\n /**\n * Fires during an INTERACTIVE OAuth login flow — when the SDK wants the\n * user agent to navigate to the authorization URL. Hosts open a browser\n * and surface the URL in the TUI (in case the browser auto-open fails).\n * Distinct from `mcp:auth:required` (which fires from bootstrap when\n * tokens are missing); this only fires from `loginMcpServer`.\n */\n 'mcp:auth:url': (ctx: { name: string, url: string }) => void\n /** Interactive login completed and tokens were persisted. */\n 'mcp:auth:success': (ctx: { name: string }) => void\n /** Interactive login failed (browser error, abort, exchange failure). */\n 'mcp:auth:error': (ctx: { name: string, error: Error }) => void\n /**\n * Fires once per server after `listTools()` and after the config-side filters\n * (`enabledTools` / `disabledTools` / `toolFilter`) have applied, but BEFORE\n * tools are registered. Handlers may mutate `ctx.tools` in place — splicing,\n * reordering, or replacing entries — to further narrow what the model sees.\n *\n * Composes with config-side filters: config drops tools the host's static\n * policy excludes; this hook is the runtime escape hatch for per-user, per-\n * environment, or capability-driven decisions that the config can't express.\n *\n * Items are upstream tool descriptors (NOT yet namespaced as `mcp_<server>_<tool>`).\n */\n 'mcp:tools:filter': (ctx: {\n server: string\n transport: 'stdio' | 'sse' | 'streamable-http'\n tools: Array<{ name: string, description?: string | null, inputSchema?: unknown }>\n }) => void\n\n // MCP tool execution\n /**\n * MCP-side counterpart of `tool:gate`. Same shape: set `block` to refuse,\n * set `result` to substitute a successful payload and skip the upstream\n * MCP `callTool`. When both are set across the handler chain, `block` wins.\n *\n * Fires INSIDE the MCP wrapper's `execute`, after the loop's `tool:gate`\n * already ran. Does **not** carry `runToolCounts` — those are loop-level\n * and already exposed on `tool:gate` for MCP tools (which are registered\n * as agent tools under their namespaced name `mcp_<server>_<tool>`). Use\n * `tool:gate` for budget / dedup logic; reserve `mcp:tool:gate` for\n * MCP-specific concerns (per-server routing, transport-aware refusals).\n */\n 'mcp:tool:gate': (ctx: McpToolHookContext & {\n block: boolean\n reason: string\n result?: string | ToolResultContent[]\n }) => void\n 'mcp:tool:before': (ctx: McpToolHookContext) => void\n 'mcp:tool:after': (ctx: McpToolHookContext & { result: string | ToolResultContent[], outputBytes: number }) => void\n 'mcp:tool:transform': (ctx: McpToolHookContext & { result: string | ToolResultContent[], outputBytes: number }) => void\n 'mcp:tool:error': (ctx: McpToolHookContext & { error: Error }) => void\n\n // Background tasks\n /**\n * Fires when a background task is spawned via `ExecutionContext.execBackground`.\n * Observational — telemetry consumers can mint a \"task started\" span.\n *\n * `outputPath` is the file the context is streaming stdout+stderr to;\n * the model reads it via `read_file` on demand. `pid` is the OS pid of\n * the spawned shell wrapper (a process-group leader on POSIX, so a\n * `process.kill(-pid)` on cleanup tears down the whole subtree).\n */\n 'background:start': (ctx: {\n taskId: string\n pid: number\n command: string\n cwd: string\n outputPath: string\n startedAt: number\n }) => void\n\n /**\n * Fires exactly once per task when the child process terminates —\n * natural exit, host-issued kill, or `agent.destroy()` teardown. Same\n * payload the agent enqueues into `pendingTaskNotifications` for\n * injection on the next turn; useful for hosts that want to render\n * a \"task done\" toast immediately rather than wait for the model's\n * next prompt.\n */\n 'background:exit': (ctx: {\n taskId: string\n status: 'exited' | 'killed'\n exitCode: number\n signal?: NodeJS.Signals\n outputPath: string\n durationMs: number\n command: string\n }) => void\n\n /**\n * Ownership of a background task was transferred from one execution\n * handle to another — typically the spawn tool reassigning a\n * subagent's still-running tasks up to the parent's handle so they\n * outlive the subagent's `destroy()`.\n *\n * Hosts that listed the task under the source handle should update\n * their view to reflect the new owner. The TUI's `backgroundTasks`\n * registry drops the `childId` tag on the matching entry so the\n * `ctrl+k` picker shows it as parent-level (and `killBackgroundTask`\n * actually finds it).\n *\n * Fires on the new owner's hook bus (the parent's, in the spawn\n * case). The old owner is typically already torn down — wiring the\n * event the other way would deliver it to a destroyed hookable.\n */\n 'background:reassign': (ctx: {\n taskId: string\n /** `ExecutionHandle.id` of the previous owner. */\n fromHandleId: string\n /** `ExecutionHandle.id` of the new owner. */\n toHandleId: string\n /** Subagent label (`child-N`) the task originated under, when known. */\n childId?: string\n pid: number\n command: string\n outputPath: string\n startedAt: number\n }) => void\n\n // Skills\n 'skills:resolve': (ctx: { skills: SkillConfig[] }) => void\n 'skills:catalog': (ctx: { catalog: string, skills: SkillConfig[] }) => void\n 'skills:activate': (ctx: { skill: SkillConfig, via: ActivationVia }) => void\n 'skills:deactivate': (ctx: { skill: SkillConfig, reason: DeactivationReason }) => void\n\n // Pairing repair\n /**\n * Fires once per repair performed by the pre-send pairing pass\n * ({@link ensureToolResultPairing}). The pass runs immediately before every\n * provider call and patches over the wire-level corruption modes that\n * Anthropic / OpenAI 400 on (orphan `tool_use` / `tool_result`, duplicate\n * ids, compaction-stranded blocks).\n *\n * Observational — handlers can mirror to Sentry/PostHog/etc. for\n * postmortems on new corruption sources but cannot suppress the repair.\n * The companion `behavior.strictToolPairing` flag is the consumer escape\n * hatch for \"throw instead of repair\" (training-data collectors).\n *\n * `turnId` lets handlers correlate a repair burst with the run-loop turn\n * that observed it. Absent on repairs detected from the synthetic\n * fallback path in `agent.run()`'s error catch (the orphan-tool_use\n * fallback for crash recovery), where no turn is in flight.\n */\n 'pairing:repair': (ctx: PairingRepair & { turnId?: string }) => void\n\n // Observability seams\n /**\n * Privacy seam invoked by observability adapters (tracing, metrics,\n * logging, persistence sinks) before attaching potentially-sensitive\n * payloads as span attributes / log lines / external storage.\n *\n * Handlers mutate `ctx.redacted` — the field starts equal to `value` and\n * the final post-hook string is what consumers ship downstream. Default\n * (no handlers) is no redaction. Multiple handlers compose left-to-right;\n * the last writer wins.\n *\n * `kind` lets a single handler do shape-aware scrubbing (system prompts\n * vs. tool args vs. tool output have very different secret surfaces).\n * `meta` carries context-specific identifiers (`toolName`, `server`,\n * `turnId`, `callId`) the adapter knows at the call site — handlers use\n * it for policy decisions (\"skip redaction on internal `read_file`\n * results\"). Adapters that don't fire this hook simply attach raw values.\n *\n * Synchronous-only by design: observability stays on the hot path. Heavy\n * scrubbing (regex over MB of output) belongs in `tool:transform` instead.\n */\n 'tracing:redact': (ctx: {\n kind:\n | 'system'\n | 'prompt'\n | 'tool-input'\n | 'tool-result'\n | 'mcp-tool-input'\n | 'mcp-tool-result'\n | 'stream-text'\n | 'stream-thinking'\n value: string\n redacted: string\n meta?: Readonly<Record<string, unknown>>\n }) => void\n\n // Usage and output\n 'usage': (ctx: { turn: number, turnId: string, usage: TurnUsage, totalIn: number, totalOut: number }) => void\n 'output': (ctx: { output: Record<string, unknown>, schema: Record<string, unknown> }) => void\n /**\n * Fires when a turn's total tool-output bytes exceed `behavior.toolOutputBudget`.\n * Measured post-`tool:transform`. Loop injects a synthetic user message after\n * the tool-results turn instructing the model to summarize.\n */\n 'budget:exceeded': (ctx: { turn: number, turnId: string, bytes: number, budget: number }) => void\n /**\n * Fires when a per-tool budget configured via `behavior.toolBudgets` is\n * exceeded for a specific tool. `mode` reflects how the framework reacted:\n * `'steer'` lets the call run and queues a post-turn nudge; `'block'`\n * refuses the call outright with `Blocked: <message>`.\n *\n * `count` is the run-cumulative dispatched count just before this call.\n * Use `turnId` to correlate with `turn:after` if you need the integer turn\n * index. Distinct from `budget:exceeded` (byte-level) so consumers can\n * subscribe specifically; both can fire in the same turn.\n */\n 'tool-budget:exceeded': (ctx: {\n tool: string\n count: number\n max: number\n turnId: string\n mode: 'steer' | 'block'\n }) => void\n /**\n * Fires when the consecutive-identical {@link AgentBehavior.repeatGuard}\n * escalates on a tracked tool. `count` is the number of back-to-back\n * identical calls including the offending one. `action` reflects the\n * escalation: `'block'` refused this call via `tool:gate`; `'abort'`\n * additionally aborts the run via the agent's `AbortController` (the loop\n * then fires `agent:abort`). The `'abort'` event fires BEFORE the abort so\n * 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' | 'abort'\n }) => void\n\n // Agent lifecycle\n 'agent:abort': (ctx: object) => void\n /**\n * Run finished — fires on all exit paths (completion, maxTurns, abort).\n *\n * Since 4.0 the `AgentStats` carried here is **cumulative** across the\n * parent agent loop and every recursively-spawned sub-agent\n * (`totalIn` / `totalOut` / `cost` / `totalCacheRead` / `totalCacheCreation`).\n * For parent-loop-only counts use `ctx.turnUsage` (parent-only array);\n * for tree-wide turn counts use `flattenTurns(ctx).length`.\n */\n 'agent:done': (ctx: AgentStats) => void\n\n // Session\n 'session:start': (ctx: SessionHookContext & { runId: string, prompt: string }) => void\n 'session:end': (ctx: SessionHookContext & { runId: string, status: SessionEndStatus, turnRange: [number, number] }) => void\n 'session:turns': (ctx: SessionHookContext & { turns: SessionTurn[], count: number }) => void\n 'session:meta': (ctx: SessionHookContext & { key: string, value: unknown }) => void\n 'session:save': (ctx: SessionHookContext) => void\n}\n\n/**\n * Authoritative list of hook event names. Kept in sync with `AgentHooks` at\n * compile time: the `satisfies` assertion below rejects any drift.\n */\n/**\n * Canonical XML wire format for a background-task completion notification.\n *\n * Rendered as a leading `text` content block in the next user-turn so\n * the model can pattern-match on the outer tag. The shape uses kebab-case\n * tags (matching zidane's other XML conventions). Every field is\n * structured — `<summary>` is a derived display string, NOT the source\n * of truth for the underlying data (replay reads `<command>` /\n * `<duration-ms>` / etc. directly, never the summary, so changing the\n * summary format can't break replay).\n *\n * Field-level escaping uses {@link escapeXml} so commands containing\n * `<` / `>` / `&` round-trip cleanly through persistence + replay.\n */\nfunction renderTaskNotificationXml(info: TaskExitInfo): string {\n // Display-only summary — short, single-line, formatted by\n // `formatTaskSummary` so the wire format matches the TUI banner +\n // shell_kill tool output exactly. Replay does NOT parse this string\n // back; the structured fields below carry the canonical data.\n const summary = formatTaskSummary(info)\n const lines = [\n '<task-notification>',\n ` <task-id>${escapeXml(info.taskId)}</task-id>`,\n ` <status>${info.status}</status>`,\n ` <exit-code>${info.exitCode}</exit-code>`,\n ...(info.signal ? [` <signal>${escapeXml(info.signal)}</signal>`] : []),\n ` <command>${escapeXml(info.command)}</command>`,\n ` <output-file>${escapeXml(info.outputPath)}</output-file>`,\n ` <duration-ms>${info.durationMs}</duration-ms>`,\n ` <summary>${escapeXml(summary)}</summary>`,\n '</task-notification>',\n ]\n return lines.join('\\n')\n}\n\nconst HOOK_EVENT_NAMES = [\n 'system:before',\n 'agent:start',\n 'turn:before',\n 'turn:after',\n 'tool-results:after',\n 'stream:start',\n 'stream:text',\n 'stream:end',\n 'stream:thinking',\n 'stream:server_tool_use',\n 'stream:server_tool_result',\n 'stream:error',\n 'stream:retry',\n 'oauth:refresh',\n 'tool:gate',\n 'tool:dispatched',\n 'tool:before',\n 'tool:after',\n 'tool:error',\n 'tool:cancelled',\n 'tool:transform',\n 'tool:unknown',\n 'validation:reject',\n 'validation:coerce',\n 'context:transform',\n 'system:transform',\n 'steer:inject',\n 'spawn:before',\n 'spawn:complete',\n 'spawn:error',\n 'child:stream:text',\n 'child:stream:thinking',\n 'child:stream:end',\n 'child:stream: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:tools:list',\n 'mcp:auth:required',\n 'mcp:auth:url',\n 'mcp:auth:success',\n 'mcp:auth:error',\n 'mcp:tools:filter',\n 'mcp:tool:gate',\n 'mcp:tool:before',\n 'mcp:tool:after',\n 'mcp:tool:transform',\n 'mcp:tool:error',\n 'background:start',\n 'background:exit',\n 'background:reassign',\n 'skills:resolve',\n 'skills:catalog',\n 'skills:activate',\n 'skills:deactivate',\n 'usage',\n 'output',\n 'budget:exceeded',\n 'tool-budget:exceeded',\n '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\nexport interface AgentOptions {\n provider: Provider\n /** Display name for the agent (used in traces/logs). */\n name?: string\n /** Default system prompt injected when no system is provided at run time. */\n system?: string\n /** Tool definitions available to the agent. Defaults to no tools. */\n tools?: Record<string, ToolDef>\n /**\n * Map canonical tool names to LLM-facing (aliased) names.\n *\n * Aliasing is **LLM-boundary-only**: the alias is what the provider's tool spec\n * carries and what the model calls the tool; the canonical name is what lives in\n * `session.turns` and what the agent uses to look up the tool implementation.\n */\n toolAliases?: Record<string, string>\n /** Agent-level behavior defaults (overridden by run-level behavior) */\n behavior?: AgentBehavior\n /** Execution context: where tools run. Defaults to in-process. */\n execution?: ExecutionContext\n /** MCP servers to connect and expose as tools */\n mcpServers?: McpServerConfig[]\n /** Session for identity, turn persistence, and run tracking */\n session?: Session\n /**\n * Explicit read-state map for `read_file` dedup + `requireReadBeforeEdit`\n * tracking. When omitted, the read-state lives in a `WeakMap<Session, …>`\n * keyed by the agent's session; providing an explicit map lets a parent\n * agent share its tracking with a sessionless child (the canonical caller\n * is `spawn`'s `shareReadState: true` option). Tools resolve uniformly\n * via `resolveReadStateMap(ctx)` so the explicit map (when present)\n * wins over the session-keyed lookup.\n */\n readState?: ReadStateMap\n /** Skills configuration */\n skills?: SkillsConfig\n /**\n * Agent-lifetime hook registrations. Registered once when `createAgent`\n * returns; identical in effect to calling `agent.hooks.hook(event, fn)` for\n * each entry. Use this to bake observability / policy hooks into a\n * {@link Preset} so consumers get them automatically via `...spread`.\n *\n * Handlers may be a single function or an array — the array form is what\n * `composePresets()` produces when multiple presets register handlers for\n * the same event. Unknown event names throw at agent construction so typos\n * never silently no-op.\n *\n * For per-run handlers (auto-detached at run end) use\n * {@link AgentRunOptions.hooks}.\n */\n hooks?: AgentHookMap\n /**\n * Test seam — replaces the default MCP connector with a custom\n * implementation. Bypasses the `mcpServers` normalization layer entirely\n * and is **not** part of the supported public API. Subject to change or\n * removal in any release.\n *\n * @internal\n */\n mcpConnector?: (configs: McpServerConfig[]) => Promise<McpConnection>\n /**\n * Pre-connect MCP servers in the background as soon as `createAgent` returns,\n * instead of deferring the bootstrap to the first `agent.run()`.\n *\n * Useful when MCP latency is the dominant cost of a cold start: callers that\n * construct the agent early (e.g. at process init) can hide the bootstrap\n * behind other setup work. If bootstrap fails, the error is stored and\n * surfaced on the first `agent.run()` / `agent.warmup()`; the in-flight\n * promise is `await`ed by both paths so the error is never silently lost.\n *\n * No-op when `mcpServers` is empty. Default: `false`.\n */\n eager?: boolean\n /**\n * Time + UUID source for all journaled metadata (turn ids, `createdAt`\n * stamps, `runId`s, hook payloads consumers may persist). Defaults to\n * {@link DEFAULT_AGENT_CLOCK} (`Date.now()` / `crypto.randomUUID()`).\n *\n * Override at the run level via {@link AgentRunOptions.clock}. Live-only\n * measurements (TTFT, elapsed counters) keep `Date.now()` regardless.\n *\n * Primary use: durable-execution adapters (Restate, Temporal) inject a\n * journaled variant so replay regenerates byte-identical session\n * metadata across attempts.\n */\n clock?: AgentClock\n}\n\nexport interface Agent {\n hooks: Hookable<AgentHooks>\n run: (options: AgentRunOptions) => Promise<AgentStats>\n abort: () => void\n /**\n * Cancel a single in-flight tool call by id without aborting the rest of\n * the run. The matching call's `ctx.signal` flips and a `tool:cancelled`\n * hook fires; the call's wire result becomes the canonical\n * {@link TOOL_USE_CANCELLED_MESSAGE} with `isError: true`, while every\n * other concurrent call keeps running and the assistant turn closes\n * normally on the next batch boundary.\n *\n * Returns `true` when a live call with that id was found and the cancel\n * flag was flipped (idempotent — repeated calls return `false`).\n * `false` when no such call is currently dispatching — the lookup is\n * scoped to *currently in-flight* calls only, so a cancel issued after\n * the call has already produced a tool_result is a no-op.\n *\n * The optional `reason` is forwarded to the `tool:cancelled` hook ctx\n * and to the abort signal's `reason` field — useful for telemetry that\n * wants to distinguish \"user clicked cancel\" from a host-side policy.\n */\n cancelTool: (callId: string, reason?: string) => boolean\n /**\n * Host-side kill for a background task spawned via\n * `shell({ run_in_background: true })`. Routes through the execution\n * context's `killBackground` primitive, which SIGTERMs the whole\n * process group and awaits the output stream's flush.\n *\n * Returns `true` when a task was found and killed (or was already\n * exited — the call is idempotent on terminated tasks).\n * Returns `false` when:\n * - the task id doesn't exist in the context's registry, OR\n * - the execution context doesn't support `killBackground`\n * (some remote sandboxes), OR\n * - the agent isn't bound to an execution handle yet (no run\n * has ever fired).\n *\n * Distinct from the model-facing `shell_kill` tool — that one routes\n * through the model's turn so the kill is visible in the transcript.\n * `killBackgroundTask` is for the host's UI (\"cancel this task\" in\n * the TUI's `ctrl+k` picker); the kill is observational from the\n * model's perspective, surfacing as the usual `<task-notification>`\n * on the next turn.\n */\n killBackgroundTask: (taskId: string) => Promise<boolean>\n steer: (message: string) => void\n followUp: (message: string) => void\n waitForIdle: () => Promise<void>\n /**\n * Clear the agent's in-memory state (turns, queues, skill activations).\n * Fires `skills:deactivate` with `reason: 'reset'` for each previously active\n * skill. Awaiting lets host apps observe listener rejections.\n */\n reset: () => Promise<void>\n /**\n * Destroy the execution context and clean up resources.\n * Idempotent — safe to call from both a `finally` block and a signal handler.\n */\n destroy: () => Promise<void>\n /**\n * Explicitly activate a skill by name. Fires `skills:activate` with\n * `via: 'explicit'`. Throws if the skill isn't in the resolved catalog or\n * if the `maxActive` cap is reached. Idempotent — activating an already-active\n * skill is a no-op.\n */\n activateSkill: (name: string) => Promise<void>\n /**\n * Deactivate a skill by name. Fires `skills:deactivate` with `reason: 'explicit'`.\n * No-op when the skill wasn't active.\n */\n deactivateSkill: (name: string) => Promise<void>\n /**\n * Pre-connect MCP servers without running a turn. Idempotent and concurrency-safe:\n * - No MCP servers configured → resolves immediately.\n * - Connection already established → resolves immediately.\n * - Another `warmup()` / `run()` is bootstrapping → awaits the in-flight promise.\n *\n * Use from host code that wants to hide MCP bootstrap latency behind other\n * startup work (UI init, auth, etc.). Safe to call multiple times and from\n * multiple callers concurrently.\n */\n warmup: () => Promise<void>\n readonly isRunning: boolean\n readonly turns: SessionTurn[]\n readonly execution: ExecutionContext\n readonly handle: ExecutionHandle | null\n readonly session: Session | null\n /** Snapshot of currently active skills. */\n readonly activeSkills: readonly ActiveSkill[]\n /**\n * Frozen view of the underlying `provider.meta`. Read-only to prevent\n * accidental cross-agent contamination — writes are rejected at runtime\n * (via `Object.freeze`) and at compile time (via `Readonly`). To override\n * model / capability defaults, construct a new provider.\n */\n readonly meta: Readonly<Record<string, unknown>>\n /**\n * Categorized context-window usage — system prompt, rules, skills, MCP\n * tools/instructions, subagent defs, tool definitions, conversation, deferred\n * buckets, autocompact buffer, and free space. Computed on demand (the host\n * opens a panel / popover). Available even before the first run: when no run\n * snapshot exists yet it assembles a skills-only pre-run view (no MCP connect),\n * so it never returns `null` for a live agent — only after `destroy()`.\n *\n * Per-category counts are heuristic estimates reconciled so the live\n * categories sum to the real last-turn total; when the provider exposes\n * {@link Provider.countTokens} (Anthropic, OpenAI), the system/tools buckets\n * use exact counts and drop their `estimated` flag. One network round-trip\n * on the exact path; the host decides when to call it.\n *\n * The host supplies `effectiveWindow` (the model's `rawWindow -\n * outputReserve`, resolved via the chat model registry which the agent core\n * deliberately doesn't depend on). When omitted, free-space / fraction are\n * computed against `used` only (bar still renders, just without headroom).\n */\n getContextBreakdown: (opts?: ContextBreakdownOptions) => Promise<ContextBreakdown | null>\n /**\n * TC39 explicit-resource-management `await using` sink — alias for\n * {@link Agent.destroy}. Lets hosts write:\n *\n * ```ts\n * await using agent = createAgent({ ... })\n * await agent.run({ prompt: 'go' })\n * // agent.destroy() runs automatically at scope exit\n * ```\n *\n * Requires Node 20+ (or any runtime that ships the TC39\n * explicit-resource-management proposal). Older runtimes simply ignore\n * the symbol; nothing breaks. Idempotent because `destroy()` is.\n */\n [Symbol.asyncDispose]: () => Promise<void>\n}\n\n// ---------------------------------------------------------------------------\n// Behavior resolution\n// ---------------------------------------------------------------------------\n\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 * `surfaceMcpInstructions`, `strictToolPairing`) are always present.\n */\ntype DefaultedBehaviorKeys = 'cache' | 'compactStrategy' | 'toolDisclosure' | 'surfaceMcpInstructions' | 'strictToolPairing'\ntype ResolvedBehaviorKeys\n = | 'maxConcurrentTools' | 'maxTurns' | 'maxCostUsd' | 'retry' | 'maxTotalTokens'\n | 'maxTokens' | 'thinkingBudget' | 'modelOptions' | 'schema' | 'toolOutputBudget'\n | 'toolOutputBudgetExcludeTools' | 'compactThreshold' | 'compactKeepTurns'\n | 'thinkingDecay' | 'dedupReads' | 'dedupTools' | 'requireReadBeforeEdit'\n | 'toolBudgets' | 'repeatGuard' | 'readLineNumbers' | 'elideStaleReads' | 'toolSearch'\n | 'persistThreshold' | 'persistExcludeTools' | 'persistDir' | 'persistMaxBytes'\n | 'tasksDir' | 'disableBackgroundTasks' | 'maxConsecutivePauseTurns' | 'persistTurns'\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 maxTurns: runBehavior?.maxTurns ?? agentBehavior?.maxTurns,\n maxCostUsd: runBehavior?.maxCostUsd ?? agentBehavior?.maxCostUsd,\n retry: runBehavior?.retry ?? agentBehavior?.retry,\n maxTotalTokens: runBehavior?.maxTotalTokens ?? agentBehavior?.maxTotalTokens,\n maxTokens: runBehavior?.maxTokens ?? agentBehavior?.maxTokens,\n thinkingBudget: runBehavior?.thinkingBudget ?? agentBehavior?.thinkingBudget,\n modelOptions: runBehavior?.modelOptions ?? agentBehavior?.modelOptions,\n schema: runBehavior?.schema ?? agentBehavior?.schema,\n cache: runBehavior?.cache ?? agentBehavior?.cache ?? true,\n toolOutputBudget: runBehavior?.toolOutputBudget ?? agentBehavior?.toolOutputBudget,\n toolOutputBudgetExcludeTools: runBehavior?.toolOutputBudgetExcludeTools ?? agentBehavior?.toolOutputBudgetExcludeTools,\n compactStrategy: runBehavior?.compactStrategy ?? agentBehavior?.compactStrategy ?? 'off' as const,\n compactThreshold: runBehavior?.compactThreshold ?? agentBehavior?.compactThreshold,\n compactKeepTurns: runBehavior?.compactKeepTurns ?? agentBehavior?.compactKeepTurns,\n thinkingDecay: runBehavior?.thinkingDecay ?? agentBehavior?.thinkingDecay,\n dedupReads: runBehavior?.dedupReads ?? agentBehavior?.dedupReads,\n dedupTools: runBehavior?.dedupTools ?? agentBehavior?.dedupTools,\n requireReadBeforeEdit: runBehavior?.requireReadBeforeEdit ?? agentBehavior?.requireReadBeforeEdit,\n toolBudgets: runBehavior?.toolBudgets ?? agentBehavior?.toolBudgets,\n repeatGuard: runBehavior?.repeatGuard ?? agentBehavior?.repeatGuard,\n readLineNumbers: runBehavior?.readLineNumbers ?? agentBehavior?.readLineNumbers,\n elideStaleReads: runBehavior?.elideStaleReads ?? agentBehavior?.elideStaleReads,\n toolDisclosure: runBehavior?.toolDisclosure ?? agentBehavior?.toolDisclosure ?? 'eager' 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 disableBackgroundTasks: runBehavior?.disableBackgroundTasks ?? agentBehavior?.disableBackgroundTasks,\n strictToolPairing: runBehavior?.strictToolPairing ?? agentBehavior?.strictToolPairing ?? false,\n maxConsecutivePauseTurns: runBehavior?.maxConsecutivePauseTurns ?? agentBehavior?.maxConsecutivePauseTurns,\n persistTurns: runBehavior?.persistTurns ?? agentBehavior?.persistTurns,\n }\n}\n\n// ---------------------------------------------------------------------------\n// Tool disclosure (progressive)\n// ---------------------------------------------------------------------------\n\n/**\n * Resolve which configured MCP server a namespaced tool name belongs to.\n *\n * Tools coming through {@link connectMcpServers} are keyed `mcp_<server>_<tool>`.\n * Server names may themselves contain underscores, so a naive `split('_')[1]`\n * would mis-attribute tools — instead, we test each configured server name as\n * a prefix candidate and pick the longest match. This is O(N×M) per lookup\n * but the loop runs once at run start; not on a hot path.\n */\nfunction resolveServerForTool(\n toolName: string,\n servers: readonly McpServerConfig[] | undefined,\n): McpServerConfig | undefined {\n if (!servers || servers.length === 0)\n return undefined\n let best: McpServerConfig | undefined\n let bestLen = -1\n for (const server of servers) {\n const prefix = `mcp_${server.name}_`\n if (toolName.startsWith(prefix) && server.name.length > bestLen) {\n best = server\n bestLen = server.name.length\n }\n }\n return best\n}\n\ninterface DisclosureResult {\n /** Canonical names of tools that get full schemas in every turn's wire-level tool list. */\n eagerCanonicalNames: Set<string>\n /** Canonical names of every lazy tool — kept as a `Set` for fast O(1) gate lookup. */\n lazyCanonicalNames: Set<string>\n /** Lazy entries the catalog + `tool_search` advertise to the model (wire names). */\n lazyEntries: LazyToolEntry[]\n}\n\n/**\n * Partition a tool registry into eager and lazy buckets.\n *\n * Native + skill tools always end up in the eager bucket — only MCP tools\n * are eligible for lazy disclosure. The `tool_search` tool itself, when\n * registered, must be eager (otherwise the model has no way to discover\n * anything).\n *\n * Lazy entries carry both the wire (`name`, alias-rewritten) and the\n * canonical (`canonicalName`) so:\n * - The catalog and `tool_search` results show wire names — the only names\n * the model ever sees on the provider side.\n * - The unlock set is keyed by canonical names — the loop's `ctx.tools` map\n * and the dispatch path are alias-stable.\n */\nfunction partitionToolDisclosure(\n toolsBySpecName: Record<string, ToolDef>,\n mcpToolNames: ReadonlySet<string>,\n servers: readonly McpServerConfig[] | undefined,\n globalMode: 'eager' | 'lazy',\n toolAliases: Record<string, string> | undefined,\n): DisclosureResult {\n const eagerCanonicalNames = new Set<string>()\n const lazyCanonicalNames = new Set<string>()\n const lazyEntries: LazyToolEntry[] = []\n\n function wireFor(canonical: string): string {\n const aliased = toolAliases?.[canonical]\n return typeof aliased === 'string' && aliased.length > 0 ? aliased : canonical\n }\n\n for (const [canonicalName, def] of Object.entries(toolsBySpecName)) {\n if (!mcpToolNames.has(canonicalName)) {\n eagerCanonicalNames.add(canonicalName)\n continue\n }\n const server = resolveServerForTool(canonicalName, servers)\n const mode = server?.disclosure ?? globalMode\n if (mode === 'lazy') {\n lazyCanonicalNames.add(canonicalName)\n lazyEntries.push({\n name: wireFor(canonicalName),\n canonicalName,\n description: def.spec.description || '',\n inputSchema: (def.spec.inputSchema ?? { type: 'object', properties: {} }) as Record<string, unknown>,\n ...(server ? { server: server.name } : {}),\n })\n }\n else {\n eagerCanonicalNames.add(canonicalName)\n }\n }\n\n return { eagerCanonicalNames, lazyCanonicalNames, lazyEntries }\n}\n\ninterface BuildCatalogOptions {\n /**\n * When `'tool_search'` (or another wire name), the catalog's preface tells\n * the model to call that tool. When `null`, no call-to-action is emitted —\n * useful when the host opted out of the auto-injected `tool_search` and\n * wants the catalog text to stay non-misleading.\n */\n discoveryToolName: string | null\n}\n\nfunction buildSearchableCatalog(\n entries: readonly LazyToolEntry[],\n options: BuildCatalogOptions,\n): string {\n // Group by server so the model sees a coherent \"what's available where\"\n // view. Tools without a server attribution (shouldn't happen for MCP, but\n // defensive) land in an \"ungrouped\" bucket at the end.\n const byServer = new Map<string, LazyToolEntry[]>()\n const ungrouped: LazyToolEntry[] = []\n for (const entry of entries) {\n if (!entry.server) {\n ungrouped.push(entry)\n continue\n }\n const list = byServer.get(entry.server) ?? []\n list.push(entry)\n byServer.set(entry.server, list)\n }\n\n // Stable alphabetical group order so the catalog text doesn't shift between\n // runs based on parallel-bootstrap completion ordering. The system-prompt\n // cache breakpoint depends on byte-stability across turns/runs; without\n // this, a re-bootstrap could thrash the cache for no semantic reason.\n const serverNames = [...byServer.keys()].sort()\n\n const parts: string[] = []\n if (options.discoveryToolName) {\n // The catalog block is computed once at run start and embedded into the\n // system prompt so the system-prompt cache breakpoint stays byte-stable\n // across turns. After `tool_search` unlocks a tool, it still appears in\n // this catalog (stale by design) but its full schema is also in the\n // request's `tools` array — there's no duplicate schema definition.\n // The \"if you have not already surfaced it\" caveat keeps the model\n // from re-calling `tool_search` for tools it has already loaded.\n parts.push(\n 'The following tools are available but their input schemas are NOT loaded in your context.',\n `Call the \\`${options.discoveryToolName}\\` tool to load schemas for any tool below that you have not already surfaced. Surfaced tools persist for the rest of the run.`,\n '',\n )\n }\n parts.push('<searchable_tools>')\n for (const server of serverNames) {\n parts.push(` <server name=\"${escapeXml(server)}\">`)\n for (const entry of byServer.get(server)!)\n parts.push(` <tool name=\"${escapeXml(entry.name)}\">${escapeXml(entry.description)}</tool>`)\n parts.push(' </server>')\n }\n for (const entry of ungrouped)\n parts.push(` <tool name=\"${escapeXml(entry.name)}\">${escapeXml(entry.description)}</tool>`)\n parts.push('</searchable_tools>')\n return parts.join('\\n')\n}\n\n/**\n * Install a `tool:gate` listener that refuses dispatch on lazy tools the\n * model hasn't surfaced via `tool_search` yet. Production providers\n * (Anthropic, OpenAI) already enforce this server-side because the model\n * can only emit `tool_use` for tools in the request's `tools` list — but\n * relying on that alone leaves custom / OSS / mock providers (and any\n * future lenient validator) able to bypass the gate by quoting a name from\n * the catalog. The middleware closes the gap so lazy disclosure is a real\n * boundary, not just an advertisement filter.\n *\n * Returns an uninstall function the run-end teardown calls so handlers\n * never leak across runs.\n */\nfunction installLazyDisclosureGate(\n hooks: Hookable<AgentHooks>,\n lazyCanonicalNames: ReadonlySet<string>,\n unlocked: ReadonlySet<string>,\n discoveryToolName: string | null,\n): () => void {\n if (lazyCanonicalNames.size === 0)\n return () => {}\n return hooks.hook('tool:gate', (ctx) => {\n if (ctx.block)\n return\n if (!lazyCanonicalNames.has(ctx.name))\n return\n if (unlocked.has(ctx.name))\n return\n ctx.block = true\n ctx.reason = discoveryToolName\n ? `Tool \"${ctx.name}\" is listed in <searchable_tools> but its schema has not been loaded. Call the \\`${discoveryToolName}\\` tool with names: [\"${ctx.name}\"] first, then re-issue the call.`\n : `Tool \"${ctx.name}\" is listed in <searchable_tools> but its schema has not been loaded.`\n })\n}\n\n/**\n * Render the per-server MCP `instructions` payloads into a single\n * `# MCP Server Instructions` section. Matches the shape the Claude Code\n * SDK emits so eval traces stay comparable.\n *\n * Empty / whitespace-only entries are filtered out upstream (see\n * `bootstrapServer`); this function only renders what survived. Callers\n * that pass an empty map get an empty string back — no stub heading.\n *\n * Output shape:\n *\n * # MCP Server Instructions\n *\n * ## supabase\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): SessionTurn[] | undefined {\n const hasSessionTurns = !!session && session.turns.length > 0\n if (!prompt && !hasSessionTurns)\n throw new Error('prompt is required when no session with existing turns is provided')\n\n let resumeFilteredTurns: SessionTurn[] | undefined\n if (hasSessionTurns)\n resumeFilteredTurns = filterUnresolvedToolUses(session!.turns)\n\n if (!prompt && resumeFilteredTurns) {\n const lastTurn = resumeFilteredTurns.at(-1)\n if (lastTurn && lastTurn.role !== 'user') {\n const tail = detectTurnInterruption(resumeFilteredTurns)\n const detail = tail === 'completed'\n ? 'last turn is a completed assistant message'\n : 'last turn is mid-stream assistant content'\n throw new Error(`cannot resume without prompt: ${detail}. Pass a prompt to agent.run({ prompt: … }).`)\n }\n }\n return resumeFilteredTurns\n}\n\nexport function createAgent({ provider, name: agentName, system: agentSystem, tools: agentTools, toolAliases, behavior: agentBehavior, execution, mcpServers, session, readState: agentReadState, skills: agentSkills, mcpConnector, eager, hooks: initialHooks, clock: agentClock }: AgentOptions): Agent {\n const hooks = createHooks<AgentHooks>()\n const executionContext = execution ?? createProcessContext()\n const sourceTools = agentTools ?? {}\n\n // Agent-lifetime hooks declared on `AgentOptions.hooks` (typically supplied\n // by a preset). Registered here so they fire across every `run()` — same\n // semantics as a manual `agent.hooks.hook(...)` call right after createAgent.\n // Unknown event names throw immediately so typos in preset definitions\n // never silently no-op. Mirrors the per-run registration block in `run()`.\n if (initialHooks) {\n for (const [event, handler] of Object.entries(initialHooks)) {\n if (!isKnownHookEvent(event)) {\n throw new Error(\n `Unknown hook event \"${event}\" passed to createAgent(). See AgentHooks for valid events.`,\n )\n }\n const handlerList = Array.isArray(handler) ? handler : [handler]\n for (const fn of handlerList) {\n if (typeof fn !== 'function')\n continue\n hooks.hook(event, fn as AgentHooks[typeof event])\n }\n }\n }\n\n let abortController: AbortController | undefined\n let running = false\n let idleResolve: (() => void) | undefined\n let idlePromise: Promise<void> | undefined\n\n // 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 // Per-callId abort registry mutated by the loop's `executeSingleTool` —\n // shared with `cancelTool()` below so an external caller (TUI keybind,\n // SDK consumer) can flip a single in-flight call's signal without\n // unwinding the whole run. The loop adds the entry before gate/execute\n // and removes it in a finally, so the map only ever holds currently\n // dispatching calls. Lives on the agent (not the run scope) so the\n // same instance survives across the loop's per-batch reuses; cleared\n // when destroyed.\n const pendingToolCancels = new Map<string, AbortController>()\n let executionHandle: ExecutionHandle | null = null\n let mcpConnection: McpConnection | null = null\n // Snapshot of the last run's assembled prompt pieces (system, wire tools,\n // deferred tools, MCP groups, skills/subagent catalogs) + the model used.\n // Captured in `run()` right after the prompt is finalized; read on demand by\n // `getContextBreakdown()`. `null` until the first run assembles a prompt.\n let lastContextAssembly: ContextAssembly | null = null\n // Shared in-flight warmup promise. Guarantees that concurrent `warmup()` calls\n // and the lazy bootstrap inside `run()` observe the same outcome — we never\n // kick off the connector twice, and a failure in one caller propagates to\n // every awaiter instead of getting masked by a retry.\n let mcpWarmupPromise: Promise<void> | null = null\n const allMcpServers = mcpServers ?? []\n const steeringQueue: string[] = []\n const followUpQueue: string[] = []\n let conversationTurns: SessionTurn[] = session?.turns.slice() ?? []\n // `runCounter` mints sequential `run_${N}` ids via `++runCounter`. The\n // naive `session.runs.length` initializer is unsound when the loaded\n // session's `runs[]` is thinner than the runIds referenced by its\n // turns — which happens to forks (the fork starts with the parent's\n // turns but a fresh empty `runs[]`) and to any persistence path that\n // ever lost runs while keeping turns. In those cases starting from\n // `length` would re-mint `run_1`, colliding with a runId already\n // referenced by a turn in the same session. We instead pick the max\n // numeric `run_N` id ever observed in EITHER `runs` or `turns[].runId`\n // so the next mint is guaranteed unique within this session.\n //\n // `syncRunCounter()` scans incrementally: it remembers how far it has\n // walked `runs` and `turns` and only visits the appended tail on each\n // re-sync (P4). Append-only semantics make the tail scan sound; a\n // shrunk/replaced array (reset, fork re-seed) falls back to a full\n // rescan from offset 0. The regex matches the canonical `run_<int>`\n // shape minted by this module; caller-supplied custom id schemes are\n // ignored (they don't conflict with `run_N`).\n let runCounter = 0\n let scannedRuns = 0\n let scannedTurns = 0\n const considerRunId = (id: string | undefined) => {\n if (!id)\n return\n const m = /^run_(\\d+)$/.exec(id)\n if (!m)\n return\n const n = Number.parseInt(m[1], 10)\n if (Number.isFinite(n) && n > runCounter)\n runCounter = n\n }\n function syncRunCounter(): void {\n if (!session)\n return\n if (session.runs.length < scannedRuns)\n scannedRuns = 0\n if (session.turns.length < scannedTurns)\n scannedTurns = 0\n for (let i = scannedRuns; i < session.runs.length; i++)\n considerRunId(session.runs[i].id)\n for (let i = scannedTurns; i < session.turns.length; i++)\n considerRunId(session.turns[i].runId)\n scannedRuns = session.runs.length\n scannedTurns = session.turns.length\n }\n syncRunCounter()\n\n // Skills — resolved lazily and idempotently via `ensureSkillsResolved()`.\n // First call from `run()` / `warmup()` / `activateSkill()` wins; concurrent\n // callers converge on `skillsResolvePromise` so we never scan twice.\n const skillsConfig = agentSkills\n const skillsEnabledValue = skillsConfig?.enabled\n const skillsDisabled = skillsEnabledValue === false || (Array.isArray(skillsEnabledValue) && skillsEnabledValue.length === 0)\n let resolvedSkills: SkillConfig[] | null = null\n let skillsCatalog: string | null = null\n // Shared in-flight skills-resolution promise. Mirrors `mcpWarmupPromise`:\n // guarantees a single resolution pass even under concurrent callers, and\n // surfaces failures to every awaiter instead of masking them with a retry.\n let skillsResolvePromise: Promise<void> | null = null\n // Cleanup fn for the inline-skills temp directory (created by\n // `resolveSkills` when `skills.write` is set). Replaced with the real\n // teardown on first resolution; called from `destroy()` so the OS temp\n // doesn't fill up across many short-lived agents.\n let skillsCleanup: () => void = () => {}\n\n /**\n * Resolve skills once for the lifetime of the agent. Idempotent and\n * concurrency-safe; no-op when `skills` is disabled or omitted.\n *\n * Used by `run()` (lazy bootstrap), `warmup()` (eager bootstrap), and\n * `activateSkill()` (so the public API doesn't leak the timing of `run()`).\n * Fires `skills:resolve` (mutable `skills` array) and `skills:catalog`\n * (mutable `catalog`) in that order — exactly once per agent.\n */\n async function ensureSkillsResolved(): Promise<void> {\n if (skillsDisabled || !skillsConfig)\n return\n if (resolvedSkills)\n return\n if (skillsResolvePromise)\n return skillsResolvePromise\n\n skillsResolvePromise = (async () => {\n // Shell interpolation was previously applied here once per agent — it\n // now runs per `skills_use` invocation (Phase 3) so command output is\n // fresh on each activation rather than cached at resolution time.\n const bundle = await resolveSkills(skillsConfig)\n resolvedSkills = bundle.skills\n skillsCleanup = bundle.cleanup\n await hooks.callHook('skills:resolve', { skills: resolvedSkills })\n\n // The catalog prose branches on whether the `skills_use` tool will be\n // auto-injected into this agent's tool set. We predict the same gate\n // `run()` applies at tool-merge time: skills tool is registered iff\n // the config doesn't opt out AND the catalog is non-empty. A run-level\n // `tools` override can still suppress injection, but we don't yet know\n // `options.tools` — that's an acceptable mismatch (the model reads the\n // system prompt once; consumers doing aggressive per-run tool overrides\n // already accept that context reflects the agent default).\n const skillsToolRegistered = skillsConfig.tool !== false && resolvedSkills.length > 0\n const catalogCtx = {\n catalog: buildCatalog(resolvedSkills, { skillsToolRegistered }),\n skills: resolvedSkills,\n }\n await hooks.callHook('skills:catalog', catalogCtx)\n skillsCatalog = catalogCtx.catalog\n })()\n\n try {\n await skillsResolvePromise\n }\n catch (err) {\n // Drop the cached promise so a subsequent call gets a fresh attempt.\n // Resolution is local-FS I/O — failures (e.g. tmpdir creation) are\n // usually transient or signal a misconfigured `skills.write`.\n skillsResolvePromise = null\n throw err\n }\n }\n\n // Per-agent activation state (lives across runs; `run-end` deactivates every\n // skill at each run boundary, `reset` clears everything, `resume` rehydrates).\n const skillActivationState = createSkillActivationState({\n maxActive: skillsConfig?.maxActive,\n })\n\n async function run(options: AgentRunOptions): Promise<AgentStats> {\n if (running) {\n throw new Error('Agent is already running. Use steer() or followUp() to queue messages, or waitForIdle().')\n }\n\n // Validate the prompt/resume preconditions and compute the filtered resume\n // view once (reused below in the seed block). Throws on an unrecoverable\n // resume request; see {@link validateAndPrepareResume}.\n const resumeFilteredTurns = validateAndPrepareResume(session, options.prompt)\n\n // Resolve effective clock for this run. Run-level wins over agent-level;\n // both fall back to `DEFAULT_AGENT_CLOCK`. Captured in the run closure\n // so every journaled-metadata callsite uses the same source — under a\n // durable-execution adapter (Restate) replay regenerates byte-identical\n // session metadata.\n const clock: AgentClock = options.clock ?? agentClock ?? DEFAULT_AGENT_CLOCK\n\n // Capture the external-signal listener at outer scope so the `finally`\n // below can always remove it. Previously the listener was registered\n // with `{ once: true }`, which only fires on abort — leaving the\n // listener attached for the full lifetime of long-lived\n // `options.signal`s and pinning every `abortController` instance through\n // the closure. (C2.)\n let externalAbortListener: (() => void) | undefined\n const externalSignal = options.signal\n\n // Outer try/finally guarantees `running` always resets, idle promise\n // always resolves, and the external-signal listener is always removed —\n // even when setup below `running = true` throws (warmup failure, skills\n // resolution, alias collision, unknown hook event, seed appendTurns\n // I/O). Before this, a setup throw left `running = true`, every\n // subsequent `agent.run()` died on \"Agent is already running\", and\n // `agent.waitForIdle()` blocked forever. (C1.)\n running = true\n try {\n abortController = new AbortController()\n // Re-sync runCounter against the session BEFORE minting. Each\n // agent has its OWN counter closure — sufficient when one agent\n // owns the session, but the `spawn` tool spins up a CHILD agent\n // that ALSO mints into the SAME session. Without this re-sync\n // the child mints, say, `run_17`, then back on the parent the\n // next user prompt re-mints `run_17` because the parent's\n // closure never saw the child's increment. The resulting\n // duplicate ids confuse `eventsFromTurns` (runById collapses\n // multiple entries into one, ancestryOf returns the wrong\n // depth, the transcript renders subagent markers at the wrong\n // indent, etc — exactly the \"weird display\" rendering bug).\n // `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 // Track run in session\n // `startRun` / `session:start` both expect a string prompt — coerce PromptPart[]\n // down to its concatenated text for bookkeeping. Full multimodal content still\n // lives in the turn pushed later.\n const promptLabel = typeof options.prompt === 'string'\n ? options.prompt\n : Array.isArray(options.prompt)\n ? options.prompt\n .filter((p): p is { type: 'text', text: string } => p.type === 'text')\n .map(p => p.text)\n .join('\\n')\n : ''\n // Default depth to 0 for top-level runs so the `runs` list always has a\n // numeric depth — lets consumers group/filter by level without null-checking.\n session?.startRun(runId, promptLabel, {\n ...(options.parentRunId ? { parentRunId: options.parentRunId } : {}),\n depth: typeof options.depth === 'number' ? options.depth : 0,\n })\n if (session) {\n await session.updateStatus('running')\n await hooks.callHook('session:start', { sessionId: session.id, runId, prompt: promptLabel })\n }\n\n // Run-start observability event. Fires once per `agent.run()`,\n // symmetric with `agent:done`. Carries the tracingContext carrier\n // (W3C `traceparent`, vendor variants) propagated from the spawning\n // agent so child tracers stitch this root span as a continuation of\n // the parent's spawn span. Absent / empty on top-level runs.\n const runStartedAt = await clock.now()\n await hooks.callHook('agent:start', {\n runId,\n ...(options.parentRunId ? { parentRunId: options.parentRunId } : {}),\n depth: typeof options.depth === 'number' ? options.depth : 0,\n ...(agentName ? { agentName } : {}),\n ...(provider.name ? { providerName: provider.name } : {}),\n startedAt: runStartedAt,\n ...(options.tracingContext ? { tracingContext: Object.freeze({ ...options.tracingContext }) } : {}),\n })\n\n // If an external signal is provided, wire it to our internal controller.\n // Listener captured in `externalAbortListener` (outer scope) so the\n // outer-`finally` can detach it on every exit — completion as well as\n // abort. Otherwise `{ once: true }` leaks the listener across runs.\n if (externalSignal) {\n if (externalSignal.aborted) {\n abortController.abort()\n }\n else {\n externalAbortListener = () => abortController?.abort()\n externalSignal.addEventListener('abort', externalAbortListener, { once: true })\n }\n }\n\n idlePromise = new Promise<void>((resolve) => {\n idleResolve = resolve\n })\n\n // Collect child agent stats reported via spawn:complete hook\n const childrenStats: ChildRunStats[] = []\n const unregisterSpawnHook = hooks.hook('spawn:complete', (ctx) => {\n childrenStats.push(ctx)\n })\n\n // Per-run hook registrations. Registered before runLoop, unregistered in finally,\n // so handlers never leak across runs even on throw paths.\n //\n // A typed hook registration interface is exposed on `options.hooks` so TS\n // catches signature mismatches at the call-site; here we have to cross the\n // `Object.entries` (string-keyed) barrier, so we validate the event name\n // against the known hook surface at runtime. Unknown keys would otherwise\n // silently never fire.\n const perRunUnregisters: Array<() => void> = []\n if (options.hooks) {\n for (const [event, handler] of Object.entries(options.hooks)) {\n if (!isKnownHookEvent(event)) {\n throw new Error(\n `Unknown hook event \"${event}\" passed to run(). See AgentHooks for valid events.`,\n )\n }\n const handlerList = Array.isArray(handler) ? handler : [handler]\n for (const fn of handlerList) {\n if (typeof fn !== 'function')\n continue\n // Safe cast: event was validated via isKnownHookEvent. Handler arity\n // is host-validated via the AgentRunOptions['hooks'] surface type.\n perRunUnregisters.push(hooks.hook(event, fn as AgentHooks[typeof event]))\n }\n }\n }\n\n // Spawn execution context\n if (!executionHandle) {\n executionHandle = await executionContext.spawn()\n }\n\n // Connect MCP servers lazily on first run. Delegated to `warmup()` so the\n // fast-path (\"already connected\") and the slow-path (\"pending warmup from\n // `eager: true` or a concurrent `agent.warmup()` call\") share one promise\n // and one outcome.\n if (allMcpServers.length > 0 && !mcpConnection) {\n await warmup()\n }\n\n // Skills bootstrap. Idempotent — a no-op when `warmup()` already\n // resolved them (eager bootstrap or explicit pre-warm); pays the\n // cost here on the first run otherwise.\n await ensureSkillsResolved()\n\n // Session-resume rehydration: when resuming a session with prior turns,\n // scan for `skills_use` tool_call blocks and rebuild activation state\n // from the *most recent* mode per skill. A model-side\n // `skills_use({ mode: \"deactivate\", name })` is a load-bearing intent\n // — without honoring it, the next run would silently re-activate the\n // skill (because the catalog still lists it and an earlier `activate`\n // block is still in history) and the user's earlier \"unstuck\" gesture\n // would silently undo itself. The fix is order-aware: walk chronologically\n // and keep only the last mode per skill, then activate the skills\n // whose last mode is `'activate'`. Bad / missing `mode` is treated as\n // `'activate'` for backward compatibility with the pre-deactivate\n // schema.\n //\n // Fires `skills:activate` with `via: 'resume'` so consumers can\n // distinguish a fresh activation from a rehydrated one. Does NOT\n // fire `skills:deactivate` for skills with a trailing deactivate\n // block — there's nothing to deactivate (the state started empty),\n // and emitting a spurious `'resume'`-flavored deactivate would\n // confuse listeners expecting deactivate to follow activate.\n if (resolvedSkills && session && session.turns.length > 0 && skillActivationState.active().length === 0) {\n const skillsByName = new Map(resolvedSkills.map(s => [s.name, s]))\n const lastModeBySkill = new Map<string, 'activate' | 'deactivate'>()\n for (const turn of session.turns) {\n if (turn.role !== 'assistant')\n continue\n for (const block of turn.content) {\n if (block.type !== 'tool_call' || block.name !== 'skills_use')\n continue\n const input = block.input as { name?: string, mode?: string } | undefined\n const skillName = input?.name\n if (!skillName)\n continue\n const mode = input?.mode === 'deactivate' ? 'deactivate' : 'activate'\n lastModeBySkill.set(skillName, mode)\n }\n }\n for (const [skillName, mode] of lastModeBySkill) {\n if (mode !== 'activate')\n continue\n const skill = skillsByName.get(skillName)\n if (!skill)\n continue\n if (skillActivationState.activate(skill, 'resume') === 'ok') {\n await hooks.callHook('skills:activate', { skill, via: 'resume' })\n }\n }\n }\n\n const thinking = options.thinking ?? 'off'\n const model = options.model ?? provider.meta.defaultModel\n const resolvedBehavior = resolveBehavior(agentBehavior, options.behavior)\n const { maxConcurrentTools, maxTurns, maxCostUsd, maxTotalTokens, maxTokens, retry, thinkingBudget, modelOptions: behaviorModelOptions, schema, cache, toolOutputBudget, toolOutputBudgetExcludeTools, compactStrategy, compactThreshold, compactKeepTurns, thinkingDecay, dedupTools, toolBudgets, repeatGuard, elideStaleReads, toolDisclosure, toolSearch, surfaceMcpInstructions, persistThreshold, persistExcludeTools, persistDir, persistMaxBytes, strictToolPairing, maxConsecutivePauseTurns, persistTurns } = resolvedBehavior\n // Per-run options win over the behavior-level default outright (no merge —\n // a run that passes `modelOptions` fully replaces the agent default).\n const modelOptions = options.modelOptions ?? behaviorModelOptions\n\n // System prompt: run-time option > agent default > fallback\n let system = options.system || agentSystem || 'You are a helpful assistant.'\n // Snapshot the host-composed base (doctrine + rules + env) BEFORE the\n // agent appends skills / searchable-tools / MCP-instructions catalogs, so\n // the context breakdown can attribute each appended section separately.\n const baseSystemForBreakdown = renderSystemForWire(system)\n\n // Append skills catalog to the STATIC half of the system prompt. The\n // catalog is built once per run and stays byte-stable for the duration,\n // so it belongs in the cached prefix. When the caller embedded a\n // `SYSTEM_PROMPT_BOUNDARY` marker, the catalog lands above it; without\n // a marker, the append is a plain string concat (current behavior).\n if (skillsCatalog) {\n system = appendStaticSection(system, skillsCatalog)\n }\n\n // Tool resolution: run-level override > agent tools + MCP tools\n const runBaseTools = options.tools !== undefined\n ? options.tools\n : (mcpConnection\n ? { ...sourceTools, ...mcpConnection.tools }\n : sourceTools)\n\n // Track which tools came from MCP — only these are eligible for lazy\n // disclosure. A run-level `tools` override skips MCP entirely so the set\n // stays empty in that case.\n const mcpToolNames: ReadonlySet<string>\n = options.tools === undefined && mcpConnection\n ? new Set(Object.keys(mcpConnection.tools))\n : new Set<string>()\n\n // Auto-inject `skills_use` / `skills_read` / `skills_run_script` when the\n // resolved skills catalog is non-empty. Opt out via `SkillsConfig.tool: false`.\n //\n // Skill tools are merged at the same tier as agent/MCP — a run-level\n // `tools: {}` override still wins (consistent with \"no tools means no tools\").\n // Agent-defined tools with the same name win over auto-injected ones.\n const shouldInjectSkillTools\n = options.tools === undefined\n && !!resolvedSkills\n && resolvedSkills.length > 0\n && skillsConfig?.tool !== false\n\n const mergedWithSkills = shouldInjectSkillTools\n ? {\n // Auto-injected first so agent + MCP tools can override by name.\n skills_use: createSkillsUseTool({\n catalog: resolvedSkills!,\n state: skillActivationState,\n hooks,\n }),\n skills_read: createSkillsReadTool({\n catalog: resolvedSkills!,\n state: skillActivationState,\n }),\n skills_run_script: createSkillsRunScriptTool({\n catalog: resolvedSkills!,\n state: skillActivationState,\n scriptTimeoutMs: skillsConfig?.scriptTimeoutMs,\n }),\n ...runBaseTools,\n }\n : runBaseTools\n\n // Build a spec-name-indexed map for the loop (models call tools by spec name, not map key)\n const toolsPreSearch: Record<string, typeof mergedWithSkills[string]> = {}\n for (const tool of Object.values(mergedWithSkills)) {\n toolsPreSearch[tool.spec.name] = tool\n }\n\n // Per-run rewrite of the built-in `shell` tool. Two adjustments\n // ride on the same identity check:\n //\n // 1. Background-mode gate. Strips `run_in_background` from the\n // schema and the background-related paragraphs from the\n // description when:\n // - `behavior.tasksDir` is unset (no log dir wired), OR\n // - `behavior.disableBackgroundTasks: true` (explicit opt-out).\n // So the model never sees a flag it can't use.\n //\n // 2. Dedicated-tool nudge. When the agent also registers siblings\n // like `read_file`, `glob`, `grep`, `list_files`, `edit`, or\n // `write_file`, the description gains a \"prefer the dedicated\n // tool\" block for each one that's present — targeting the\n // `ls`/`cat`-to-re-verify loop some models fall into when both\n // a dedicated tool and `shell` are visible. Aliases are\n // threaded through so the printed name matches the wire-level\n // name the model sees in the tool spec.\n //\n // Only the framework's identity-equal built-in `shell` is rewritten —\n // host-customized shell-named tools are left alone (the host owns\n // the spec they registered). Hosts who want explicit control can\n // register `createShellTool({ ... })` directly.\n if (toolsPreSearch.shell === builtinShell) {\n const bgAllowed = typeof resolvedBehavior?.tasksDir === 'string'\n && resolvedBehavior.tasksDir.length > 0\n && resolvedBehavior.disableBackgroundTasks !== true\n toolsPreSearch.shell = createShellTool({\n allowBackground: bgAllowed,\n registeredCanonicals: new Set(Object.keys(toolsPreSearch)),\n ...(toolAliases ? { toolAliases } : {}),\n })\n }\n\n // Progressive tool disclosure — partition the registry into eager vs lazy\n // buckets and prepare the per-run `unlocked` set the loop reads when\n // rebuilding the wire-level tool list.\n //\n // Native (non-MCP) tools are always eager; `tool_search` itself must be\n // eager too (the discovery path can't gate on its own surfacing). The\n // `unlocked` set seeds with every eager canonical tool name and grows as\n // the model surfaces lazy tools through `tool_search`.\n //\n // We pass `toolAliases` so lazy entries can carry their wire-level\n // (alias-rewritten) display name — what the catalog and `tool_search`\n // results show to the model — while keeping the canonical name on the\n // entry for unlock-set membership and dispatch-map lookup.\n const disclosure = partitionToolDisclosure(toolsPreSearch, mcpToolNames, mcpServers, toolDisclosure, toolAliases)\n const unlocked = new Set<string>(disclosure.eagerCanonicalNames)\n // Snapshot of the eager seed — never mutated. `buildFormattedTools`\n // walks the registry filtered by this set for the cache-stable prefix\n // (phase 1) and then appends entries from `dynamicUnlockOrder` (phase\n // 2). Splitting the seed from the live `unlocked` set is what lets us\n // emit dynamic unlocks at the END of the wire-level tools array\n // regardless of where they sit in registry insertion order — without\n // it, an MCP tool surfaced by `tool_search` would land at its\n // registry position and shift every subsequent tool's byte offset,\n // invalidating the provider's tool-list prefix cache.\n const initialUnlocked = new Set<string>(disclosure.eagerCanonicalNames)\n const dynamicUnlockOrder: string[] = []\n // Separate membership Set keyed on names already in the dynamic log.\n // `unlocked.has()` is NOT a reliable repeat-check because callers\n // (`tool_search.execute`, `applyToolSearchToUnlocked`) write into\n // `unlocked` BEFORE calling `addUnlock` for back-compat with hosts\n // that only pass a Set — using `unlocked.has()` here would short-\n // circuit every first call and the dynamic log would stay empty.\n const dynamicUnlockSeen = new Set<string>()\n // Centralised unlock recorder. Idempotent on repeats. Used by both\n // the `tool_search` runtime path and the resume-replay path so the\n // log reflects the EFFECTIVE unlock order regardless of source.\n function recordDynamicUnlock(canonical: string): void {\n if (initialUnlocked.has(canonical))\n return\n if (dynamicUnlockSeen.has(canonical))\n return\n dynamicUnlockSeen.add(canonical)\n unlocked.add(canonical)\n dynamicUnlockOrder.push(canonical)\n }\n const hostDefinedToolSearch = !!toolsPreSearch.tool_search\n const shouldInjectToolSearch\n = disclosure.lazyEntries.length > 0\n && toolSearch?.tool !== false\n && !hostDefinedToolSearch\n let tools = toolsPreSearch\n if (shouldInjectToolSearch) {\n const toolSearchTool = createToolSearchTool({\n catalog: disclosure.lazyEntries,\n unlocked,\n addUnlock: recordDynamicUnlock,\n ...(toolSearch?.limit !== undefined ? { defaultLimit: toolSearch.limit } : {}),\n })\n tools = { ...toolsPreSearch, [toolSearchTool.spec.name]: toolSearchTool }\n unlocked.add(toolSearchTool.spec.name)\n // `tool_search` itself is part of the cache-stable prefix — it\n // never gets unlocked dynamically and we want its position to stay\n // fixed at the end of the registry-order block, before any dynamic\n // unlocks tail.\n initialUnlocked.add(toolSearchTool.spec.name)\n }\n\n // Decide which discovery-tool name (if any) the catalog text should\n // point at. `tool_search` when we auto-injected, the host's tool when\n // they brought their own (we use the wire/alias name in both cases —\n // that's what the model is told to call), and `null` when they fully\n // opted out so the catalog stays accurate without misleading prose.\n const discoveryToolName: string | null = shouldInjectToolSearch\n ? 'tool_search'\n : (hostDefinedToolSearch\n ? (toolAliases?.tool_search ?? 'tool_search')\n : null)\n\n // Append the lazy-tool catalog AFTER the skills catalog so both use the\n // same anchor point — the catalog text is stable across turns and rides\n // the system-prompt cache breakpoint. 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(toolAliases, Object.keys(tools))\n // Auto-accept the Claude Code `mcp__server__tool` double-underscore\n // naming form as an inbound alias for every MCP tool. Models trained\n // on Anthropic's SDK routinely emit the double form even when the\n // tool catalog advertises single — without this, every such call\n // tripped `tool:unknown` and burnt a turn on a correction round-trip.\n // Inbound-only by design: outbound wire still shows the canonical\n // single form, so tool-list bytes (and the prompt cache) are\n // unaffected. See `augmentMcpDoubleUnderscoreAliases` for the rule.\n augmentMcpDoubleUnderscoreAliases(aliasMaps, Object.keys(tools))\n\n // (Lazy-disclosure gate is installed below, AFTER the skill/budget/dedup\n // gates, so its `tool:gate` handler runs last in registration order. See\n // the install call further down.)\n\n // Closure that recomputes the wire-level tool list from the current\n // unlock state. Called once up-front for the seed value and again\n // per-iteration in the loop when lazy disclosure may have grown the\n // dynamic-unlock log. Safe to call repeatedly — `provider.formatTools`\n // is a pure mapping.\n //\n // Cache-stable emission: phase 1 walks the registry in insertion\n // order, emitting tools in `initialUnlocked` (every eager native +\n // skill_* + tool_search). This block is byte-stable across the entire\n // run — no element is added or moved, so providers that cache on the\n // tools-array bytes (Anthropic ephemeral cache, OpenRouter passthrough)\n // hit cache turn after turn for the prefix.\n //\n // Phase 2 appends entries from `dynamicUnlockOrder` (tools surfaced\n // by `tool_search` or resume-replay) in the order they were unlocked.\n // The log only grows — entries are never reordered or removed — so\n // every successful `tool_search` invalidates exactly the tool-list\n // suffix from the first new schema onwards, NOT the whole array. The\n // prefix (every tool the model already had) stays cache-hit.\n //\n // The previous implementation iterated `Object.values(tools)` filtered\n // by `unlocked` — that follows registry insertion order, which puts a\n // newly-unlocked tool at its REGISTRY POSITION (somewhere in the\n // middle of the MCP block), shifting every later entry's bytes and\n // invalidating the full tool-list cache on every discovery wave. Two\n // phases fixes that without changing model-observable behavior.\n // Memoize across turns keyed by `dynamicUnlockOrder.length`. The eager\n // prefix is byte-stable for the whole run and the dynamic tail is\n // append-only (never reordered/removed), so the formatted array is fully\n // determined by the tail length. Most turns unlock nothing, so this\n // skips the full registry walk + `provider.formatTools` on the hot path\n // (P3) while still recomputing the entire array — never splicing — when\n // a `tool_search` grows the tail, which keeps providers with non-mapping\n // `formatTools` (e.g. Anthropic's `web_search` rewrite) correct.\n let formattedToolsCache: { tailLen: number, value: unknown[] } | null = null\n function buildFormattedTools(): unknown[] {\n if (formattedToolsCache && formattedToolsCache.tailLen === dynamicUnlockOrder.length)\n return formattedToolsCache.value\n const specs: ToolSpec[] = []\n for (const t of Object.values(tools)) {\n if (!initialUnlocked.has(t.spec.name))\n continue\n specs.push({\n name: aliasMaps.aliasByCanonical.get(t.spec.name) ?? t.spec.name,\n description: t.spec.description || '',\n inputSchema: t.spec.inputSchema,\n })\n }\n for (const canonical of dynamicUnlockOrder) {\n const t = tools[canonical]\n if (!t)\n continue\n specs.push({\n name: aliasMaps.aliasByCanonical.get(t.spec.name) ?? t.spec.name,\n description: t.spec.description || '',\n inputSchema: t.spec.inputSchema,\n })\n }\n const value = specs.length > 0 ? provider.formatTools(specs) : []\n formattedToolsCache = { tailLen: dynamicUnlockOrder.length, value }\n return value\n }\n const formattedTools = buildFormattedTools()\n\n // Capture a context snapshot for `getContextBreakdown()`. Split the\n // disclosed wire tools into native vs MCP (by `mcpToolNames`) and group\n // the MCP ones; the deferred (lazy, not-yet-disclosed) entries come from\n // `disclosure.lazyEntries` and carry their own `server`. Each tool is\n // sized by its JSON byte footprint — the same bytes the provider sends.\n {\n // Capture raw tool inputs only — the JSON byte-sizing is deferred to\n // `materializeAssemblyTools` inside `getContextBreakdown()`, so a normal\n // run never pays the per-tool `JSON.stringify` (P1). Disclosed-tool order\n // matches the registry walk filtered by `unlocked`; deferred order\n // matches `disclosure.lazyEntries`.\n const disclosedTools: DisclosedToolInput[] = []\n for (const t of Object.values(tools)) {\n if (!unlocked.has(t.spec.name))\n continue\n const wireName = aliasMaps.aliasByCanonical.get(t.spec.name) ?? t.spec.name\n disclosedTools.push({\n name: wireName,\n description: t.spec.description || '',\n inputSchema: t.spec.inputSchema,\n ...(mcpToolNames.has(t.spec.name) ? { mcpServer: 'mcp' } : {}),\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 // Drain pending background-task notifications. Each entry becomes\n // a `<task-notification>` text block prepended to the user turn so\n // the model sees task completions on its very next prompt, without\n // polling. The notifications survive the run-end deactivate-all\n // (they're agent-scoped, not run-scoped) and were either populated\n // by `background:exit` since the last `run()` OR suppressed by a\n // `shell_kill` / `read_file` against the matching task. See\n // `docs/RUN_IN_BACKGROUND.md` §Completion notification.\n const drainedNotifications: SessionContentBlock[] = []\n if (pendingTaskNotifications.size > 0) {\n for (const notif of pendingTaskNotifications.values()) {\n drainedNotifications.push({\n type: 'text',\n text: renderTaskNotificationXml(notif),\n })\n }\n pendingTaskNotifications.clear()\n }\n\n // Snapshot the high-water mark BEFORE pushing the seeded user\n // turn for this run. Every entry currently in `turns` came from\n // the resume path (they were in `session.turns` when we loaded),\n // so they're already durable. The seeded user turn (next) and\n // subsequent assistant / tool-results turns are NEW, and the\n // persistence loop below uses `turns.slice(lastPersistedTurnCount)`\n // to figure out what to write.\n //\n // Subtle: we must NOT use `session.turns.length` here. When the\n // session carries subagent turns from a prior run, the resume\n // path FILTERS them out of `turns` (subagent runs are top-level-\n // invisible per the `childRunIds` filter above), so `turns.length`\n // is smaller than `session.turns.length`. A previous version used\n // session.turns.length as the high-water mark — `turns.slice(N)`\n // returned empty for every subsequent persist call, and the\n // entire follow-up run silently failed to persist (the seeded\n // user turn never landed in `session.turns`, the loop produced\n // assistant turns but they never persisted either, the user's\n // transcript saw a frozen session with no apparent error). The\n // local-count high-water mark sidesteps this entirely.\n let lastPersistedTurnCount = turns.length\n\n const promptParts = canonicalizePrompt(options.prompt)\n if (promptParts || drainedNotifications.length > 0) {\n const promptMsg = promptParts ? buildPromptMessage(provider, promptParts) : null\n // Prepend the notifications BEFORE the prompt content so the\n // model reads them as ambient context before the user's actual\n // request. Multiple notifications stack in completion order\n // (Map preserves insertion order; insertion order = onExit\n // arrival order from `background:exit`).\n const content: SessionContentBlock[] = [\n ...drainedNotifications,\n ...(promptMsg ? promptMsg.content : []),\n ]\n turns.push({\n id: clock.randomUUID(),\n runId,\n role: promptMsg ? promptMsg.role : 'user',\n content,\n createdAt: await clock.now(),\n })\n }\n\n conversationTurns = turns\n\n // Persist + emit the seeded user turn before any assistant `turn:before` fires.\n // Without this, the seeded turn was persisted only after the first `turn:after`,\n // so DB rows ordered by `created_at` placed the user turn AFTER its assistant\n // response. Consumers already dedupe `session:turns` payloads, so emitting the\n // seeded turn separately is safe.\n if (session && turns.length > lastPersistedTurnCount) {\n const seededTurns = turns.slice(lastPersistedTurnCount)\n await session.appendTurns(seededTurns)\n lastPersistedTurnCount = turns.length\n await hooks.callHook('session:turns', { sessionId: session.id, turns: seededTurns, count: turns.length })\n }\n\n // Incremental turn persistence — fires after both the assistant turn\n // (`turn:after`) and the tool-results user turn (`tool-results:after`),\n // so a tool_use block is always durable alongside its matching\n // tool_result. Previously only `turn:after` was subscribed, leaving a\n // crash window where a persisted assistant turn referenced tool_use\n // IDs that had no on-disk counterpart — and Anthropic rejects orphan\n // tool_use blocks on resume.\n const persistPendingTurns = async () => {\n if (!session)\n return\n const newTurns = turns.slice(lastPersistedTurnCount)\n if (newTurns.length === 0)\n return\n await session.appendTurns(newTurns)\n lastPersistedTurnCount = turns.length\n await hooks.callHook('session:turns', { sessionId: session.id, turns: newTurns, count: turns.length })\n }\n // `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 session.generateTurnId?.()) ?? clock.randomUUID()\n await synthesizeMissingToolResults(turns, turnId, runId, provider, hooks, clock)\n }\n const remaining = turns.slice(lastPersistedTurnCount)\n if (remaining.length > 0) {\n await session.appendTurns(remaining)\n lastPersistedTurnCount = turns.length\n await hooks.callHook('session:turns', { sessionId: session.id, turns: remaining, count: turns.length })\n }\n }\n\n // Deactivate every active skill with `reason: 'run-end'`. Called at each\n // natural run boundary regardless of status (completed / aborted / error).\n async function deactivateAllSkills() {\n for (const record of skillActivationState.clear())\n await hooks.callHook('skills:deactivate', { skill: record.skill, reason: 'run-end' })\n }\n\n // Finalize session: persist run status and fire session:end\n async function finalizeSession(status: SessionEndStatus) {\n if (!session)\n return\n const run = session.runs.find(r => r.id === runId)\n if (run)\n await session.updateRun(run)\n await session.updateStatus(status === 'aborted' ? 'idle' : status)\n await hooks.callHook('session:end', { sessionId: session.id, runId, status, turnRange: [runTurnStart, turns.length - 1] })\n }\n\n // Install the skill allowed-tools gate for this run; remove it in `finally`\n // so tool:gate listeners never leak across runs.\n const uninstallAllowedToolsGate = installAllowedToolsGate(hooks, skillActivationState)\n\n // Per-tool soft call budgets. Installed BEFORE dedup so a budget block\n // wins over a dedup hit — otherwise a model spamming a dedup-eligible\n // tool would bypass the cap entirely (every dedup hit short-circuits\n // budgets that fire later in the chain). Order: allowed-tools → budgets\n // → dedup → consumer hooks. The middleware owns its own per-tool\n // approval counter so within-batch ordering is correct in parallel mode.\n const uninstallToolBudgets = installToolBudgetsGate(\n hooks,\n () => toolBudgets,\n msg => steeringQueue.push(msg),\n )\n\n // Consecutive-identical repeat guard. Installed AFTER budgets (a budget\n // block wins) and BEFORE dedup (so a streak block beats a dedup replay\n // short-circuit — otherwise a model spamming a dedup-eligible payload\n // would replay forever and never trip the abort). At the abort threshold\n // the gate calls `abortController.abort()`; the loop's existing post-turn\n // `signal.aborted` check then fires `agent:abort` and ends the run — the\n // same path a user-driven cancel takes.\n const uninstallRepeatGuard = installRepeatGuard(\n hooks,\n () => repeatGuard,\n () => abortController?.abort(),\n )\n\n // Per-tool argument-dedup middleware. No-op when `behavior.dedupTools` is\n // empty/unset or when the run is sessionless. Behavior is the field-merged\n // resolution of agent + run defaults — same precedence as every other\n // behavior knob.\n const uninstallDedupTools = installDedupToolsGate(\n hooks,\n () => dedupTools,\n () => session ?? undefined,\n )\n\n // Hard gate for lazy tools — refuses dispatch on canonical names not yet\n // in `unlocked`. Production providers already enforce this via their\n // tool-list validator, but the middleware closes the gap for custom /\n // mock / lenient providers and for any path where a model quotes a name\n // straight from the catalog.\n //\n // Registered LAST among the four gates so its handler fires after\n // allowed-tools, budgets, and dedup. Lazy gate early-returns when an\n // upstream gate already set `ctx.block` (see `installLazyDisclosureGate`\n // body), so a skill refusal or budget block always wins over a \"load\n // via tool_search\" message. The early-return turns the order into a\n // safety net rather than a hard invariant — but the order-and-the-\n // early-return together make the precedence regression-proof.\n const uninstallLazyDisclosureGate = installLazyDisclosureGate(\n hooks,\n disclosure.lazyCanonicalNames,\n unlocked,\n discoveryToolName,\n )\n\n const runStartMs = await clock.now()\n\n const runDepth = typeof options.depth === 'number' ? options.depth : 0\n\n try {\n const stats = await runLoop({\n provider,\n hooks,\n agentName,\n agentSystem,\n agentTools: sourceTools,\n agentToolAliases: toolAliases,\n agentMcpServers: mcpServers,\n agentSkills,\n // Forward the resolved view (agent + run merged) so per-run overrides\n // of `dedupReads` / `requireReadBeforeEdit` / etc. are visible to\n // tools via `ToolContext.behavior`.\n agentBehavior: resolvedBehavior,\n tools,\n formattedTools,\n rebuildFormattedTools: disclosure.lazyEntries.length > 0 ? buildFormattedTools : undefined,\n aliasMaps,\n model,\n system,\n thinking,\n ...(maxConcurrentTools !== undefined ? { maxConcurrentTools } : {}),\n signal: abortController.signal,\n execution: executionContext,\n handle: executionHandle!,\n steeringQueue,\n followUpQueue,\n turns,\n runId,\n generateTurnId: () => session?.generateTurnId() ?? clock.randomUUID(),\n clock,\n maxTurns,\n ...(maxCostUsd !== undefined ? { maxCostUsd } : {}),\n ...(maxTotalTokens !== undefined ? { maxTotalTokens } : {}),\n ...(retry !== undefined ? { retry } : {}),\n maxTokens,\n ...(session ? { session } : {}),\n ...(agentReadState ? { readState: agentReadState } : {}),\n ...(options.parentRunId ? { parentRunId: options.parentRunId } : {}),\n depth: runDepth,\n thinkingBudget,\n ...(modelOptions ? { modelOptions } : {}),\n schema,\n cache,\n toolOutputBudget,\n ...(toolOutputBudgetExcludeTools !== undefined ? { toolOutputBudgetExcludeTools } : {}),\n compactStrategy,\n compactThreshold,\n compactKeepTurns,\n ...(elideStaleReads !== undefined ? { elideStaleReads } : {}),\n ...(thinkingDecay !== undefined ? { thinkingDecay } : {}),\n ...(persistThreshold !== undefined ? { persistThreshold } : {}),\n ...(persistExcludeTools !== undefined ? { persistExcludeTools } : {}),\n ...(persistDir !== undefined ? { persistDir } : {}),\n ...(persistMaxBytes !== undefined ? { persistMaxBytes } : {}),\n ...(strictToolPairing ? { strictToolPairing: true } : {}),\n ...(maxConsecutivePauseTurns !== undefined ? { maxConsecutivePauseTurns } : {}),\n providerName: provider.name,\n runStartMs,\n runToolCounts: {},\n pendingToolCancels,\n })\n\n // `stats` is the parent-loop view returned by `runLoop` —\n // `totalIn/Out/turnUsage` cover this run's loop only. Children are\n // collected separately via `spawn:complete` and folded into the\n // returned `AgentStats` below; the loop view is preserved verbatim\n // for `session.completeRun(...)` so per-run session ledgers stay\n // additive (children are persisted as their own runs in the same\n // session — folding cumulative numbers in here would double-count).\n const parentTurnCost = stats.turnUsage\n ?.reduce((sum, t) => sum + (t.cost ?? 0), 0) ?? 0\n\n // Roll children's already-cumulative totals into the parent. Each\n // child's `stats.totalIn/Out/cost/totalCacheRead/totalCacheCreation`\n // was finalized through this same path before bubbling up via\n // `spawn:complete`, so a single-level sum lands grandchildren\n // transitively.\n let childrenIn = 0\n let childrenOut = 0\n let childrenCost = 0\n let childrenCacheRead = 0\n let childrenCacheCreation = 0\n for (const c of childrenStats) {\n childrenIn += c.stats.totalIn\n childrenOut += c.stats.totalOut\n childrenCost += c.stats.cost ?? 0\n childrenCacheRead += c.stats.totalCacheRead\n childrenCacheCreation += c.stats.totalCacheCreation\n }\n\n const cumulativeCost = parentTurnCost + childrenCost\n\n const finalStats: AgentStats = {\n ...stats,\n totalIn: stats.totalIn + childrenIn,\n totalOut: stats.totalOut + childrenOut,\n totalCacheRead: stats.totalCacheRead + childrenCacheRead,\n totalCacheCreation: stats.totalCacheCreation + childrenCacheCreation,\n ...(cumulativeCost > 0 ? { cost: cumulativeCost } : {}),\n children: childrenStats.length > 0 ? childrenStats : undefined,\n }\n\n await flushTurns()\n\n // If aborted during loop (loop broke cleanly without throwing).\n // Pass `stats` through so the persisted run record reflects real\n // token consumption instead of the historic `0 in / 0 out`.\n if (abortController.signal.aborted) {\n session?.abortRun(runId, {\n turns: stats.turns,\n tokensIn: stats.totalIn,\n tokensOut: stats.totalOut,\n turnUsage: stats.turnUsage,\n cost: parentTurnCost > 0 ? parentTurnCost : undefined,\n })\n await finalizeSession('aborted')\n await hooks.callHook('agent:done', finalStats)\n return finalStats\n }\n\n session?.completeRun(runId, {\n turns: stats.turns,\n tokensIn: stats.totalIn,\n tokensOut: stats.totalOut,\n turnUsage: stats.turnUsage,\n cost: parentTurnCost > 0 ? parentTurnCost : undefined,\n })\n await finalizeSession('completed')\n\n await hooks.callHook('agent:done', finalStats)\n return finalStats\n }\n catch (err) {\n // Synthesize tool_result fallbacks for any orphaned tool_use blocks on\n // the trailing assistant turn — see `synthesizeMissingToolResults` for\n // why this matters (resume rejects orphan tool_use).\n await flushTurns({ failureFallback: true })\n\n // If aborted, provider may throw — return gracefully\n if (abortController.signal.aborted) {\n session?.abortRun(runId)\n await finalizeSession('aborted')\n const stats: AgentStats = {\n totalIn: 0,\n totalOut: 0,\n totalCacheRead: 0,\n totalCacheCreation: 0,\n turns: 0,\n elapsed: 0,\n }\n await hooks.callHook('agent:done', stats)\n return stats\n }\n\n // Budget circuit breaker — semantically a \"clean stop\", not an\n // error. Finalize the run as `aborted` (matching the abort path\n // above) so the persisted record reflects the operator-imposed\n // cap rather than mis-attributing the trip to a runtime failure,\n // then re-throw so callers can branch on the typed class. The\n // run record's token / cost columns stay zero here because the\n // loop already threw before we could read its `stats` — a host\n // that wants the breaching spend on disk should hook\n // `turn:after` to mirror cumulative usage into its own ledger\n // (the predicate fires post-`turn:after` precisely for this).\n if (err instanceof AgentBudgetExceededError) {\n session?.abortRun(runId)\n await finalizeSession('aborted')\n await hooks.callHook('agent:done', {\n totalIn: 0,\n totalOut: 0,\n totalCacheRead: 0,\n totalCacheCreation: 0,\n turns: 0,\n elapsed: 0,\n })\n throw err\n }\n\n session?.errorRun(runId, errorMessage(err))\n await finalizeSession('error')\n throw err\n }\n finally {\n // Deactivate every skill active at run end. Runs on every exit path —\n // completed, aborted, or thrown — so activation state never leaks across\n // run boundaries. Explicit activation via `agent.activateSkill()` is\n // subject to the same rule: it lives until the next run boundary.\n await deactivateAllSkills()\n\n // Teardown run-scoped skill handlers (allowed-tools gate, etc.).\n uninstallAllowedToolsGate()\n uninstallDedupTools()\n uninstallRepeatGuard()\n uninstallToolBudgets()\n uninstallLazyDisclosureGate()\n\n unregisterSpawnHook()\n unregisterTurnSync?.()\n unregisterToolResultsSync?.()\n for (const unregister of perRunUnregisters)\n unregister()\n steeringQueue.length = 0\n 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 const controller = pendingToolCancels.get(callId)\n if (!controller || controller.signal.aborted)\n return false\n // `reason` rides the abort signal so a signal-aware tool body that\n // surfaces `signal.reason` in its error path gets the same string the\n // `tool:cancelled` hook receives — keeps observability consistent.\n controller.abort(reason ?? 'user-cancelled-tool')\n return true\n }\n\n async function killBackgroundTask(taskId: string): Promise<boolean> {\n if (!executionHandle)\n return false\n if (!executionContext.killBackground)\n return false\n const info = await executionContext.killBackground(executionHandle, taskId)\n // `null` is the context's \"no such task\" return — surface as `false`\n // so the host UI can distinguish \"kill happened\" from \"id was unknown\".\n return info !== null\n }\n\n function steer(message: string) {\n steeringQueue.push(message)\n }\n\n function followUpFn(message: string) {\n followUpQueue.push(message)\n }\n\n function waitForIdle(): Promise<void> {\n return idlePromise ?? Promise.resolve()\n }\n\n async function reset() {\n // Reject mid-run resets explicitly. The loop holds its own reference to\n // the in-flight `turns` array (built per-`run()`), so clearing\n // `conversationTurns` here would have left the loop's view untouched\n // and the next `turn:after` would re-link it back to the unflushed\n // buffer — silently making the call a no-op. Throwing forces callers\n // to `await agent.waitForIdle()` first so reset's invariant\n // (\"everything cleared\") actually holds.\n if (running) {\n throw new Error(\n 'Cannot reset() while the agent is running. Call `agent.abort()` and `await agent.waitForIdle()` first.',\n )\n }\n conversationTurns = []\n steeringQueue.length = 0\n followUpQueue.length = 0\n // 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 pendingTaskNotifications.clear()\n // Activation state is agent-scoped — a reset should clear it too.\n // We fire `skills:deactivate` with reason='reset' for each previously active\n // skill so consumer telemetry stays consistent. Previously fire-and-forget;\n // awaiting ensures a listener's rejection surfaces rather than silently\n // breaking the invariant \"reset clears activation\".\n const cleared = skillActivationState.clear()\n for (const record of cleared)\n await hooks.callHook('skills:deactivate', { skill: record.skill, reason: 'reset' })\n }\n\n async function activateSkill(name: string): Promise<void> {\n // Resolve the catalog on demand so hosts can `activateSkill` at any\n // point in the agent lifecycle — including before the first `run()`.\n // Idempotent and concurrency-safe (see `ensureSkillsResolved`).\n await ensureSkillsResolved()\n if (!resolvedSkills) {\n throw new Error(\n `Cannot activate skill \"${name}\" — skills are disabled for this agent.`,\n )\n }\n const skill = resolvedSkills.find(s => s.name === name)\n if (!skill) {\n const available = resolvedSkills.map(s => s.name).join(', ') || '<none>'\n throw new Error(`Unknown skill \"${name}\". Available skills: ${available}.`)\n }\n const outcome = skillActivationState.activate(skill, 'explicit')\n if (outcome === 'cap-reached') {\n throw new Error(\n `Cannot activate skill \"${name}\" — the maxActive cap of ${skillsConfig?.maxActive} has been reached.`,\n )\n }\n if (outcome === 'ok')\n await hooks.callHook('skills:activate', { skill, via: 'explicit' })\n }\n\n async function deactivateSkill(name: string): Promise<void> {\n const removed = skillActivationState.deactivate(name)\n if (removed)\n await hooks.callHook('skills:deactivate', { skill: removed.skill, reason: 'explicit' })\n }\n\n // Background-task plumbing.\n //\n // Two listeners, both registered for the agent's lifetime:\n //\n // 1. `background:exit` → enqueue a notification keyed by `taskId`.\n // The shell tool body fires this hook from its `execBackground`\n // `onExit` callback; the context-owned `child.on('close')` is what\n // settles the lifecycle. Map.set overwrites by id so a duplicate\n // enqueue (defensive — shouldn't happen with the context's at-most-\n // once settle latch) doesn't multiply notifications.\n //\n // 2. `tool:after` → suppress the would-be notification when the model\n // already learned about the exit through other means:\n // - `shell_kill`: the tool's own return value tells the model the\n // task is dead. Delete by `task_id` from the call's input.\n // - `read_file` / `read`: the model reading the task's output file\n // is a strong \"I'm aware of this task\" signal. Delete by\n // matching the `path` argument against any pending notification's\n // outputPath.\n //\n // Suppression fires AFTER the body settled, so the enqueue (in onExit)\n // and the delete (in tool:after) happen in that order — Map.delete is\n // idempotent against a missing key. See `docs/RUN_IN_BACKGROUND.md`\n // §\"code-quality checklist\" items 1 + 9 for the failure modes this\n // wiring guards against.\n hooks.hook('background:exit', (ctx) => {\n pendingTaskNotifications.set(ctx.taskId, {\n taskId: ctx.taskId,\n status: ctx.status,\n exitCode: ctx.exitCode,\n ...(ctx.signal ? { signal: ctx.signal } : {}),\n outputPath: ctx.outputPath,\n durationMs: ctx.durationMs,\n command: ctx.command,\n })\n })\n hooks.hook('tool:after', (ctx) => {\n if (ctx.name === 'shell_kill') {\n const taskId = ctx.input?.task_id\n if (typeof taskId === 'string')\n pendingTaskNotifications.delete(taskId)\n return\n }\n if (ctx.name === 'read_file' || ctx.name === 'read') {\n const rawPath = ctx.input?.path\n if (typeof rawPath !== 'string' || rawPath.length === 0)\n return\n // The shell tool returns absolute paths in its background-mode\n // result, so the model normally reads back with the same absolute\n // string. But a paranoid model (or a slash-command, or copy-paste\n // typo) can hand us a relative / `~/…` / non-canonical path that\n // resolves to the same file. Normalize both sides via\n // `path.resolve` so suppression survives those cases.\n //\n // `resolve` against `process.cwd()` matches the read_file tool's\n // own resolution behavior (it joins against the execution\n // handle's cwd, which equals process.cwd() for the default\n // process context). For sandbox/docker contexts the handle's\n // cwd may differ; the worst-case there is a missed suppression\n // (model gets a redundant notification it can ignore) — never a\n // false suppression.\n const requested = pathResolve(rawPath)\n for (const notif of pendingTaskNotifications.values()) {\n if (pathResolve(notif.outputPath) === requested) {\n pendingTaskNotifications.delete(notif.taskId)\n return\n }\n }\n }\n })\n\n // Wrap session methods to fire hooks\n if (session) {\n const originalSave = session.save.bind(session)\n const originalSetMeta = session.setMeta.bind(session)\n\n session.save = async () => {\n await originalSave()\n await hooks.callHook('session:save', { sessionId: session.id })\n }\n\n // `setMeta` has a synchronous surface (hosts `await setMeta` wouldn't gain\n // anything), but we must still surface listener errors — a promise\n // without a handler becomes an unhandled rejection. Route rejections\n // through a no-op `.catch` that records on the session via updateStatus\n // is overkill; logging here is sufficient because the primary side effect\n // (the metadata write) has already succeeded.\n session.setMeta = (key: string, value: unknown) => {\n originalSetMeta(key, value)\n // `callHook` returns `void | Promise<...>` depending on whether any\n // listener is async. Normalize to a promise so we can attach a rejection\n // handler without leaking an unhandled rejection. Intentionally do not\n // re-throw — the sync contract must not leak a listener failure back to\n // the caller of setMeta.\n void Promise.resolve(hooks.callHook('session:meta', { sessionId: session.id, key, value })).catch((err: unknown) => {\n console.error('[zidane] session:meta listener rejected:', err)\n })\n }\n }\n\n let destroyed = false\n\n /**\n * Pre-connect MCP servers. Returns the shared in-flight promise when a\n * bootstrap is already running, so concurrent callers converge on one\n * connection. Clears the cached promise on failure so the next caller can\n * retry — leaving a rejected promise cached would permanently poison future\n * runs on the same agent.\n */\n async function ensureMcpConnected(): Promise<void> {\n if (mcpConnection || allMcpServers.length === 0)\n return\n if (mcpWarmupPromise)\n return mcpWarmupPromise\n\n mcpWarmupPromise = (async () => {\n const connection = mcpConnector\n ? await mcpConnector(allMcpServers)\n : await connectMcpServers(allMcpServers, undefined, hooks)\n // If destroy() fired while we were bootstrapping, close the fresh\n // connection immediately rather than leak it into a destroyed agent.\n // destroy() awaits this promise before returning, so its subsequent\n // `if (mcpConnection) close()` branch would handle this too — but only\n // when assignment wins the race. Closing here unconditionally is safer.\n if (destroyed) {\n await connection.close().catch(() => {})\n return\n }\n mcpConnection = connection\n })()\n\n try {\n await mcpWarmupPromise\n }\n catch (err) {\n // Drop the cached promise so the next `run()` / `warmup()` call gets a\n // fresh attempt. MCP failures at bootstrap are typically transient\n // (network blip, slow stdio spawn) and the partial-failure path in\n // `connectMcpServers` already tolerates any server that does come up.\n mcpWarmupPromise = null\n throw err\n }\n }\n\n async function warmup(): Promise<void> {\n // Post-destroy no-op. Without this guard, a `warmup()` (or a `run()` that\n // calls warmup) after `destroy()` would open a fresh MCP connection that\n // nobody ever closes — the `mcpConnection.close()` branch of `destroy` has\n // already run and won't run again.\n if (destroyed)\n return\n // MCP + skills are independent — bootstrap them in parallel. Either side\n // is a no-op when already settled, so repeat `warmup()` calls stay cheap.\n // A rejection on one side still propagates via Promise.all; the other\n // side's promise stays cached and gets reused on the next call.\n await Promise.all([ensureMcpConnected(), ensureSkillsResolved()])\n }\n\n async function destroy() {\n // Idempotent: a host may call destroy() in a `finally` block AND a\n // process-level signal handler; racing double-frees crash the MCP SDK.\n if (destroyed)\n return\n destroyed = true\n\n // Teardown ordering rule: first the things that were HAPPENING\n // INSIDE the session (background tasks, in-flight tool cancels);\n // then the things the session DEPENDED ON (MCP, execution handle,\n // skills cache). Background tasks must flush their output streams\n // before the execution handle disappears underneath them.\n\n // ① INSIDE-the-session — work the session was producing.\n // Background tasks: walk the execution context's registry and let\n // the context's own `destroy(handle)` SIGTERM each task's process\n // group, flush its output stream, and clear the entry. We can't\n // walk it ourselves (the registry is private to the context) —\n // the context's destroy path handles all of this. We call it via\n // the executionHandle below; the ORDERING here just ensures we\n // don't tear down MCP first.\n //\n // The notification queue is local to this agent — drop it\n // unconditionally so a session swap that reconstructs the agent\n // doesn't replay stale notifications on the next run.\n pendingTaskNotifications.clear()\n\n // Per-call cancels: flip every still-pending one as part of\n // teardown. Without this, a destroy mid-tool would leave tool\n // bodies running against an execution context the loop is about to\n // tear down — the cancel signal gives them one last chance to\n // unwind cleanly. The loop's `finally` also drops these entries,\n // but it may not have run yet if destroy races a still-awaiting\n // call.\n for (const controller of pendingToolCancels.values()) {\n if (!controller.signal.aborted)\n controller.abort('agent-destroyed')\n }\n pendingToolCancels.clear()\n\n // If destroy races with a still-pending warmup, wait for it to finish\n // before closing. Otherwise `mcpConnection` could be populated after\n // destroy() returns and leak clients past the agent's lifetime.\n if (mcpWarmupPromise) {\n try {\n await mcpWarmupPromise\n }\n catch {\n // Bootstrap failure is already surfaced to the warmup caller; destroy\n // path just wants to clean up whatever partial state exists.\n }\n }\n\n // ② NEEDED-for-the-session — infra the session was sitting on.\n if (mcpConnection) {\n await mcpConnection.close()\n mcpConnection = null\n }\n if (executionHandle) {\n // `executionContext.destroy(handle)` is what walks the context's\n // own task registry, kills survivors, flushes their streams, and\n // drops the handle. The ordering here is what makes it safe for\n // tasks to call back into the handle while we're draining.\n await executionContext.destroy(executionHandle)\n executionHandle = null\n }\n // Defensive re-clear: the `background:exit` hook listener stays\n // registered for the agent's lifetime, so the SIGTERMs `destroy()`\n // just issued may have re-populated the queue while we were\n // awaiting `executionContext.destroy()`. Without this, the queue\n // ends up holding entries that will never drain (no more `run()`)\n // — purely cosmetic at runtime but confuses telemetry consumers\n // that snapshot agent state post-destroy.\n pendingTaskNotifications.clear()\n // Remove the inline-skills temp directory if one was materialized.\n // No-op when `skills.write` was unused.\n skillsCleanup()\n skillsCleanup = () => {}\n // Drop the cached context-breakdown snapshot so a post-destroy\n // `getContextBreakdown()` can't serve stale data (it's also `destroyed`-\n // guarded below as a belt-and-suspenders).\n lastContextAssembly = null\n }\n\n /**\n * Last parent-run `TurnUsage` from the in-memory conversation — mirrors\n * `lastContextSizeFromTurns` (chat/store) but reads the live agent turns and\n * returns the full usage so callers can break out cache read/write/input.\n * Skips subagent (depth > 0) and zero-usage turns.\n */\n function lastTurnUsage(): TurnUsage | null {\n const childRunIds = childRunIdSet(session)\n for (let i = conversationTurns.length - 1; i >= 0; i--) {\n const turn = conversationTurns[i]\n if (turn.role !== 'assistant' || !turn.usage)\n continue\n if (turn.runId && childRunIds.has(turn.runId))\n continue\n const size = effectiveInputFromTurn(turn.usage)\n if (size === 0)\n continue\n return turn.usage\n }\n return null\n }\n\n /**\n * Best-effort context assembly BEFORE the first run, so the breakdown is\n * available the moment the chat screen opens (no \"send a message first\").\n *\n * Resolves skills + MCP via `warmup()`, then assembles the same static pieces\n * `run()` captures — system prompt (with skills catalog + MCP instructions),\n * the wire tool specs, and MCP groupings. The disclosure subtleties don't\n * matter pre-run (nothing has been unlocked yet); we treat every native +\n * MCP tool as disclosed, matching the eager seed of a fresh run.\n */\n async function buildPreRunAssembly(modelOverride?: string): Promise<ContextAssembly | null> {\n if (destroyed)\n return null\n // Resolve SKILLS only (local, cheap) — deliberately do NOT call `warmup()`,\n // which would connect MCP servers (spawn stdio processes, possibly trigger\n // OAuth) just to render a read-only popover. MCP tools surface in the\n // breakdown once a real run (or an explicit `warmup()`) has connected them;\n // until then we reflect whatever `mcpConnection` already holds (often null).\n try {\n await ensureSkillsResolved()\n }\n catch {\n // Skills resolution failed — fall through with whatever resolved.\n }\n\n const model = modelOverride ?? provider.meta.defaultModel\n const base = agentSystem || 'You are a helpful assistant.'\n const baseSystem = renderSystemForWire(base)\n let system = base\n if (skillsCatalog)\n system = appendStaticSection(system, skillsCatalog)\n\n const baseTools = mcpConnection ? { ...sourceTools, ...mcpConnection.tools } : sourceTools\n const mcpNames = new Set(mcpConnection ? Object.keys(mcpConnection.tools) : [])\n\n // 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 for (const t of Object.values(baseTools)) {\n const spec: ToolSpec = { name: t.spec.name, description: t.spec.description || '', inputSchema: t.spec.inputSchema }\n if (mcpNames.has(t.spec.name))\n mcpSpecs.push(spec)\n else\n nativeSpecs.push(spec)\n }\n for (const spec of nativeSpecs)\n disclosedTools.push({ name: spec.name, description: spec.description || '', inputSchema: spec.inputSchema })\n for (const spec of mcpSpecs)\n disclosedTools.push({ name: spec.name, description: spec.description || '', inputSchema: spec.inputSchema, mcpServer: 'mcp' })\n\n const wireTools = (nativeSpecs.length + mcpSpecs.length) > 0\n ? provider.formatTools([...nativeSpecs, ...mcpSpecs])\n : []\n\n const mcpInstructionsText = (mcpConnection?.instructions && mcpConnection.instructions.size > 0)\n ? renderMcpInstructionsSection(mcpConnection.instructions)\n : undefined\n if (mcpInstructionsText)\n system = appendStaticSection(system, mcpInstructionsText)\n\n return assembleContextSnapshot({\n modelId: model,\n system: renderSystemForWire(system),\n baseSystem,\n disclosedTools,\n deferredEntries: [],\n mcpInstructions: mcpInstructionsText,\n skillsCatalog,\n wireTools,\n })\n }\n\n async function getContextBreakdown(opts?: ContextBreakdownOptions): Promise<ContextBreakdown | null> {\n // Post-destroy: nothing to report, and `countTokens` would run against a\n // torn-down provider. Matches `buildPreRunAssembly`'s `destroyed` guard.\n if (destroyed)\n return null\n // Use the live run snapshot when present; otherwise assemble a pre-run view\n // so `ctrl+g` works the moment the chat screen opens.\n const assembly = lastContextAssembly ?? await buildPreRunAssembly(opts?.model)\n if (!assembly)\n return null\n\n const usage = lastTurnUsage()\n const used = effectiveInputFromTurn(usage)\n const effectiveWindow = opts?.effectiveWindow ?? used\n // When an auto-compact threshold is set, the buffer is the whole tail from\n // the threshold to the end of the window (so the bar/row show where\n // compaction fires); otherwise it's the reserved-output tokens.\n const autocompactBuffer\n = opts?.compactThreshold !== undefined && opts.compactThreshold > 0 && opts.compactThreshold < 1\n ? Math.max(0, Math.round((1 - opts.compactThreshold) * effectiveWindow))\n : opts?.autocompactBuffer ?? OUTPUT_RESERVE_TOKENS\n\n // Exact path — provider counts (system-only, system+tools). Best effort:\n // any failure leaves `exact` undefined and the heuristic fills the buckets.\n let exact: ContextExactCounts | undefined\n if (typeof provider.countTokens === 'function') {\n // Pass the provider-wire tool specs (already through `formatTools`) — the\n // count endpoint validates the real wire shape, so the internal `ToolSpec`\n // form would be rejected.\n //\n // count_tokens requires at least one message, so every probe carries a\n // constant 1-token dummy user message. Differencing cancels the dummy's\n // fixed overhead from every derived bucket:\n // system = count(sys + dummy) − count(BASELINE + dummy)\n // tools = count(sys + tools + dummy) − count(sys + dummy)\n //\n // The baseline uses a 1-char placeholder system (`BASELINE`) rather than\n // an empty string so that provider-specific system scaffolding stays\n // symmetric across the two probes — notably the Anthropic OAuth path,\n // which injects a synthetic user/assistant pair ONLY when the system is\n // non-empty. An empty baseline would skip that scaffolding and inflate the\n // system bucket; a non-empty baseline keeps it on both sides so it cancels.\n const BASELINE = '.'\n const dummy: SessionMessage[] = [{ role: 'user', content: [{ type: 'text', text: '.' }] }]\n const count = (system: string, tools: unknown[]) =>\n provider.countTokens!({ model: assembly.modelId, system, tools, messages: dummy }, opts?.signal)\n try {\n const [base, sysProbe, sysToolsProbe] = await Promise.all([\n count(BASELINE, []),\n count(assembly.system, []),\n count(assembly.system, assembly.wireTools),\n ])\n // Partial success is fine: a resolved `null` from one probe (the\n // documented \"unavailable\" return) just leaves that field off `exact`,\n // and the builder falls back to the heuristic for it. A thrown error\n // (rejection) is all-or-nothing via `Promise.all` → `catch` below.\n if (base !== null && sysProbe !== null) {\n exact = {\n system: Math.max(0, sysProbe - base),\n ...(sysToolsProbe !== null\n ? { systemAndTools: Math.max(0, sysToolsProbe - base) }\n : {}),\n }\n }\n }\n catch {\n exact = undefined\n }\n }\n\n const materializedTools = materializeAssemblyTools(assembly)\n return buildContextBreakdown({\n modelId: assembly.modelId,\n system: assembly.system,\n baseSystem: assembly.baseSystem,\n ...(assembly.rulesBlock ? { rulesBlock: assembly.rulesBlock } : {}),\n ...(assembly.rulesFiles?.length ? { rulesFiles: assembly.rulesFiles } : {}),\n toolsJson: materializedTools.toolsJson,\n deferredToolsJson: materializedTools.deferredToolsJson,\n mcpGroups: materializedTools.mcpGroups,\n deferredMcpGroups: materializedTools.deferredMcpGroups,\n ...(assembly.mcpInstructions ? { mcpInstructions: assembly.mcpInstructions } : {}),\n ...(assembly.skillsCatalog ? { skillsCatalog: assembly.skillsCatalog } : {}),\n ...(assembly.subagentDefs ? { subagentDefs: assembly.subagentDefs } : {}),\n used,\n effectiveWindow,\n autocompactBuffer,\n ...(exact ? { exact } : {}),\n ...(usage\n ? {\n usage: {\n input: usage.input ?? 0,\n cacheRead: usage.cacheRead ?? 0,\n cacheCreation: usage.cacheCreation ?? 0,\n output: usage.output ?? 0,\n },\n }\n : {}),\n ...(skillActivationState.active().length > 0\n ? { activeSkills: skillActivationState.active().map(s => s.skill.name) }\n : {}),\n })\n }\n\n // Eager bootstrap: kick off MCP connection + skills resolution in the\n // background so the first `run()` doesn't pay the full bootstrap cost.\n // Rejection is captured on the shared warmup promises; the next `warmup()`\n // / `run()` caller observes it. A detached `.catch` swallows the rejection\n // here only to prevent Node's unhandled-rejection warning — the error is\n // still attached to the promise for the next awaiter.\n const eagerHasWork = allMcpServers.length > 0 || (!skillsDisabled && !!skillsConfig)\n if (eager && eagerHasWork) {\n void warmup().catch(() => {})\n }\n\n return {\n hooks,\n run,\n abort,\n cancelTool,\n killBackgroundTask,\n steer,\n followUp: followUpFn,\n waitForIdle,\n reset,\n destroy,\n warmup,\n activateSkill,\n deactivateSkill,\n get isRunning() { return running },\n get turns() { return conversationTurns },\n get execution() { return executionContext },\n get handle() { return executionHandle },\n get session() { return session ?? null },\n get activeSkills() { return skillActivationState.active() },\n // Expose a frozen view of provider.meta. Hosts previously could mutate\n // the underlying provider meta (e.g. via `agent.meta.defaultModel = …`),\n // which quietly affected every other agent sharing the same provider\n // instance. Freezing forces callers to construct a new provider when\n // they want to override model/capabilities.\n meta: Object.freeze({ ...provider.meta }),\n getContextBreakdown,\n // TC39 explicit-resource-management hook. `destroy` is already\n // idempotent + async, so aliasing it here is sufficient — no extra\n // wrapping needed.\n [Symbol.asyncDispose]: destroy,\n }\n}\n","/**\n * Path-suggestion helper for file-not-found errors.\n *\n * When `read_file` / `edit` / `multi_edit` get a missing-file error, walk the\n * parent directory in the agent's execution context for files sharing the\n * same basename (sans extension) — common cause: model picked the wrong\n * extension (`.js` vs `.ts`, `.md` vs `.mdx`). Returns the closest sibling\n * filename so the tool can append `Did you mean X?` to the error.\n *\n * Mirrors `claude-code/utils/file.ts` `findSimilarFile`. Goes through the\n * `ExecutionContext.listFiles` seam so it works in process / docker / sandbox.\n */\n\nimport type { ExecutionContext, ExecutionHandle } from '../contexts'\n\n/**\n * Find a sibling file in the same directory sharing `path`'s basename\n * (sans extension), excluding the missing path itself. Returns just the\n * filename (not the full path) when found, otherwise `null`.\n *\n * Silent on errors — a missing parent directory or a `listFiles` failure\n * means we have no suggestion, not that anything is wrong.\n */\nexport async function findSimilarFile(\n execution: ExecutionContext,\n handle: ExecutionHandle,\n path: string,\n): Promise<string | null> {\n const slash = path.lastIndexOf('/')\n const dir = slash === -1 ? '.' : (path.slice(0, slash) || '/')\n const target = slash === -1 ? path : path.slice(slash + 1)\n const dot = target.lastIndexOf('.')\n const targetBase = dot === -1 ? target : target.slice(0, dot)\n\n if (targetBase.length === 0)\n return null\n\n let entries: string[]\n try {\n entries = await execution.listFiles(handle, dir)\n }\n catch {\n return null\n }\n\n for (const entry of entries) {\n if (entry === target)\n continue\n const entryDot = entry.lastIndexOf('.')\n const entryBase = entryDot === -1 ? entry : entry.slice(0, entryDot)\n if (entryBase === targetBase)\n return entry\n }\n return null\n}\n\n/**\n * Format a `Did you mean X?` suffix for missing-file errors. Returns an empty\n * string when no suggestion is available so callers can string-concat\n * unconditionally.\n */\nexport async function suggestionFor(\n execution: ExecutionContext,\n handle: ExecutionHandle,\n path: string,\n): Promise<string> {\n const sibling = await findSimilarFile(execution, handle, path)\n return sibling ? ` Did you mean ${sibling}?` : ''\n}\n","import type { ToolContext, ToolDef } from './types'\nimport { resolveOldString, stripLineNumberPrefixes, styleReplacementForVia } from './edit-utils'\nimport { suggestionFor } from './path-suggest'\nimport { hashContent, readStateKey, resolveReadStateMap } from './read-state'\n\n/**\n * Surgical edit — replace `old_string` with `new_string` in a single file.\n *\n * Mirrors Claude Code's `Edit` semantics so models post-trained on Anthropic's\n * tool surface need no relearning. Fails clearly when `old_string` isn't unique\n * (unless `replace_all: true`) and when not found, with a nearest-match preview\n * so the model can recover without a separate `read_file` round-trip.\n */\n\nexport const edit: ToolDef = {\n spec: {\n name: 'edit',\n description: 'Replace exact `old_string` with `new_string` in a file. Fails if `old_string` is not unique unless `replace_all: true`. Prefer over `write_file` for surgical changes — preserves the rest of the file. Tolerates `read_file` line-number prefixes (`<N>\\\\t…`, `<N>|…`, or `<N>→…`) in `old_string` / `new_string` — they are stripped before matching/writing, so you can paste a numbered chunk verbatim.',\n inputSchema: {\n type: 'object',\n properties: {\n path: { type: 'string', description: '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 if (find === replacement)\n return `Edit error: old_string and new_string are identical — nothing to change in ${target}.`\n\n if (find.length === 0)\n return `Edit error: old_string is empty. Use write_file to create or fully overwrite a file.`\n\n let original: string\n try {\n original = await ctx.execution.readFile(ctx.handle, target)\n }\n catch {\n const hint = await suggestionFor(ctx.execution, ctx.handle, target)\n return `Edit error: file not found: ${target}.${hint}`\n }\n\n // Read-before-edit guard. When `behavior.requireReadBeforeEdit` is on\n // and a read-state map is available (session-keyed or forwarded via\n // `ctx.readState` from a spawn `shareReadState` parent), refuse edits\n // against files the model hasn't read this session, and against files\n // whose on-disk content has drifted from what the model last saw —\n // both produce silent corruption when an edit applies a substring\n // against bytes the model \"remembers\" but no longer reflect reality.\n //\n // Without any read-state map (no session, no explicit forward) the\n // guard is skipped: tracking has nowhere to live, so the gate has\n // nothing to check against.\n if (ctx.behavior?.requireReadBeforeEdit) {\n const readState = resolveReadStateMap(ctx)\n if (readState) {\n const absKey = readStateKey(ctx.handle.cwd, target)\n const prior = readState.get(absKey)\n if (!prior)\n return `Edit error: ${target} has not been read in this session. Call read_file first so the edit applies against the current contents.`\n if (prior.contentHash !== hashContent(original))\n return `Edit error: ${target} has changed on disk since the last read. Re-read the file before editing.`\n }\n }\n\n // Resolve `old_string` against the file content with the recovery\n // cascade in `resolveOldString` (exact → quotes → desanitize → combined\n // → line-number-strip → strip+quotes). On a non-exact hit, `match.actual`\n // is the file-side substring (preserves the file's typography) and the\n // replacement is re-styled below to match.\n const match = resolveOldString(original, find)\n\n if (!match) {\n const preview = nearestMatchPreview(original, find)\n return preview\n ? `Edit error: old_string not found in ${target}. Closest match in the file: ${preview}`\n : `Edit error: old_string not found in ${target}.`\n }\n\n const { actual, occurrences, via } = match\n\n if (occurrences > 1 && !replaceAll)\n return `Edit error: old_string appears ${occurrences} times in ${target}. Pass replace_all=true or expand old_string for uniqueness.`\n\n const styledReplacement = styleReplacementForVia(replacement, via, actual)\n\n const updated = replaceAll\n ? original.split(actual).join(styledReplacement)\n : original.replace(actual, styledReplacement)\n\n if (updated === original)\n return `Edit error: replacement produced no change in ${target}.`\n\n await ctx.execution.writeFile(ctx.handle, target, updated)\n\n // Keep read-state coherent — after a successful edit, the model\n // knows the post-edit content (it produced new_string), so we\n // record the new hash against the same slice. This lets a chain\n // of edits proceed without re-reading between each one even with\n // `requireReadBeforeEdit` on.\n const readState = resolveReadStateMap(ctx)\n if (readState) {\n const absKey = readStateKey(ctx.handle.cwd, target)\n const prior = readState.get(absKey)\n if (prior)\n readState.set(absKey, { ...prior, contentHash: hashContent(updated), mtimeMs: Date.now() })\n }\n\n return `Edited ${target}: replaced ${occurrences} occurrence${occurrences === 1 ? '' : 's'}.`\n },\n}\n\n/**\n * Find the line that shares the longest common prefix with the needle's first\n * line. Cheap heuristic — better than nothing for the common \"model has a typo\"\n * case (off-by-one indent, trailing whitespace, escape mismatch).\n *\n * Returns a \"line N: <preview>\" snippet or null when no line shares a useful\n * prefix. Strips line-number prefixes from the needle first so a model that\n * pasted a numbered `read_file` chunk verbatim still gets a useful diagnostic.\n */\nfunction nearestMatchPreview(haystack: string, needle: string): string | null {\n // If the model included `<N>\\t` (or `<N>|` / `<N>→`) prefixes from a\n // numbered read, strip them before scoring — otherwise the prefix\n // dominates and we never match real file lines.\n const normalizedNeedle = stripLineNumberPrefixes(needle)\n const needleFirstLine = normalizedNeedle.split('\\n')[0]\n if (needleFirstLine.length < 3)\n return null\n\n const lines = haystack.split('\\n')\n let bestScore = 0\n let bestIdx = -1\n for (let i = 0; i < lines.length; i++) {\n const score = sharedPrefixLength(lines[i], needleFirstLine)\n if (score > bestScore) {\n bestScore = score\n bestIdx = i\n }\n }\n if (bestIdx < 0 || bestScore < Math.min(8, Math.floor(needleFirstLine.length / 2)))\n return null\n\n const snippet = lines[bestIdx].slice(0, 80)\n return `line ${bestIdx + 1}: ${JSON.stringify(snippet)}`\n}\n\nfunction sharedPrefixLength(a: string, b: string): number {\n const max = Math.min(a.length, b.length)\n let i = 0\n while (i < max && a.charCodeAt(i) === b.charCodeAt(i))\n i++\n return i\n}\n","import type { ToolContext, ToolDef } from './types'\nimport { glob as nodeGlob, stat } from 'node:fs/promises'\nimport { resolve } from 'node:path'\nimport { errorMessage } from '../errors'\n\n/**\n * Glob-pattern file matching.\n *\n * Uses the Node `fs/promises` `glob` API in the in-process execution\n * context (Bun ≥1.3 re-implements the same module-level entry, so the\n * same code path serves both runtimes). For non-process contexts\n * (docker, sandbox), falls back to running the pattern through a shell\n * `find` invocation so the match is executed wherever the context lives.\n *\n * Behavioral note: Node's `fs.glob` yields directories alongside files\n * and Bun's port doesn't yet accept `withFileTypes`, so we post-filter\n * with `stat` to keep the historical files-only contract.\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 * By default each row carries `<path>\\t<size>\\t<mtime>` metadata so the\n * model can rank \"what changed recently\" without a follow-up `read_file`.\n * Pass `metadata: false` to fall back to plain newline-separated paths.\n * Metadata is best-effort: in-process contexts use `node:fs/promises stat`\n * (parallelized); non-process contexts fall through to the path-only shape\n * since shelling out per file would dominate the call latency.\n */\nconst DEFAULT_LIMIT = 1000\n\n// Only allow characters that are meaningful in glob patterns plus path/name chars.\n// Anything else (quotes, backticks, $, ;, |, <, >, spaces-in-weird-places, etc.) is\n// rejected so the shell fallback can't be hijacked via a crafted `pattern`.\nconst SAFE_GLOB_PATTERN_RE = /^[\\w./*?[\\]{}!,^@+-]+$/\n\nasync function globInProcess(pattern: string, cwd: string, limit: number): Promise<string[]> {\n const results: string[] = []\n for await (const rel of nodeGlob(pattern, { cwd })) {\n // node:fs/promises glob yields directories alongside files; stat-filter\n // to preserve Bun.Glob's `onlyFiles: true` historical contract. The\n // limit is applied AFTER this filter so directory entries don't eat\n // the budget and silently truncate real matches.\n try {\n const s = await stat(resolve(cwd, rel))\n if (!s.isFile())\n continue\n }\n catch {\n // Race with deletion or permission error — skip rather than abort.\n continue\n }\n results.push(rel)\n if (results.length >= limit)\n break\n }\n return results.sort()\n}\n\nasync function globViaShell(pattern: string, ctx: ToolContext, limit: number): Promise<string[]> {\n // Reject any characters that could break out of the single-quoted arg. With the\n // allowlist above, pattern is guaranteed shell-safe when single-quoted.\n if (!SAFE_GLOB_PATTERN_RE.test(pattern))\n throw new Error('Glob pattern contains unsupported characters (shell fallback only allows path/glob metacharacters)')\n\n // `find -path`'s `*` matches any character — including `/` — so `**` and `*` are\n // effectively equivalent. Using the user pattern verbatim keeps semantics close to\n // the in-process glob for recursive matches. `find -name` (no slash in pattern) matches basenames.\n const useBasename = !pattern.includes('/')\n const finder = useBasename\n ? `find . -type f -name '${pattern}'`\n : `find . -type f -path './${pattern}'`\n const searchCmd = `${finder} 2>/dev/null | sed 's|^./||' | sort | head -n ${limit}`\n const result = await ctx.execution.exec(ctx.handle, searchCmd)\n if (result.exitCode !== 0 && !result.stdout)\n return []\n return result.stdout.split('\\n').filter(line => line.length > 0)\n}\n\nexport const glob: ToolDef = {\n // Filesystem listing only — safe to fan out alongside other reads.\n isConcurrencySafe: true,\n spec: {\n name: 'glob',\n description: 'Match files by glob pattern (supports **, *, ?). Relative to the execution context cwd. By default each row is `<path>\\\\t<size-bytes>\\\\t<mtime-iso>`; set `metadata: false` for a plain newline-separated list of paths. Always sorted.',\n inputSchema: {\n type: 'object',\n properties: {\n pattern: {\n type: 'string',\n description: 'Glob pattern (e.g. \"src/**/*.ts\", \"*.md\", \"test/**/fixtures/*\").',\n },\n limit: {\n type: 'number',\n description: `Maximum number of matches to return. Default: ${DEFAULT_LIMIT}.`,\n },\n metadata: {\n type: 'boolean',\n description: 'Append size (bytes) and mtime (ISO) per row, tab-separated. Default: true. In-process only — non-process execution contexts always return paths.',\n },\n },\n required: ['pattern'],\n },\n },\n async execute({ pattern, limit, metadata }, ctx: ToolContext) {\n const pat = pattern as string\n const max = typeof limit === 'number' && limit > 0 ? limit : DEFAULT_LIMIT\n const wantMetadata = metadata !== false\n\n try {\n const entries = ctx.execution.type === 'process'\n ? await globInProcess(pat, ctx.handle.cwd, max)\n : await globViaShell(pat, ctx, max)\n if (entries.length === 0)\n return '(no matches)'\n\n // Metadata only makes sense in-process — shelling out per file would\n // dominate latency for large match sets, and `find -printf` is\n // GNU-only. Non-process contexts return paths only.\n if (!wantMetadata || ctx.execution.type !== 'process')\n return entries.join('\\n')\n\n const rows = await Promise.all(entries.map(async (rel) => {\n try {\n const s = await stat(resolve(ctx.handle.cwd, rel))\n return `${rel}\\t${s.size}\\t${new Date(s.mtimeMs).toISOString()}`\n }\n catch {\n // Stat failure (race with deletion, perms) — emit path with\n // empty columns so the row count still matches.\n return `${rel}\\t\\t`\n }\n }))\n return rows.join('\\n')\n }\n catch (err) {\n return `Glob error: ${errorMessage(err)}`\n }\n },\n}\n","import type { ToolContext, ToolDef } from './types'\nimport { stat as fsStat, glob as nodeGlob } from 'node:fs/promises'\nimport { resolve } from 'node:path'\nimport { errorMessage } from '../errors'\nimport { shellQuote } from './shell-quote'\n\n/**\n * Search file contents by regex.\n *\n * Wraps ripgrep (`rg`) when available, falls back to an in-process\n * `fs/promises` `glob` + regex implementation when running in the\n * in-process execution context. For non-process contexts without `rg`,\n * returns a clear hint rather than silently doing nothing.\n *\n * The tool surface mirrors Claude Code's `Grep` so models authored against the\n * Anthropic tool surface need no relearning. Output modes:\n * - `files_with_matches` (default) — newline-separated paths.\n * - `content` — `path:line:match` (line numbers on by default).\n * - `count` — `path:N` per matching file.\n *\n * Results are capped via `head_limit` (default 250) to keep model input\n * bounded; `offset` lets the caller page through.\n */\n\nconst DEFAULT_HEAD_LIMIT = 250\nconst DEFAULT_OUTPUT_MODE: OutputMode = 'files_with_matches'\n\ntype OutputMode = 'content' | 'files_with_matches' | 'count'\n\ninterface GrepInput {\n 'pattern': string\n 'path'?: string\n 'glob'?: string\n 'type'?: string\n 'output_mode'?: OutputMode\n '-i'?: boolean\n '-n'?: boolean\n '-A'?: number\n '-B'?: number\n '-C'?: number\n 'multiline'?: boolean\n 'head_limit'?: number\n 'offset'?: number\n}\n\nexport const grep: ToolDef = {\n // Searches files without touching them — concurrency-safe.\n isConcurrencySafe: true,\n spec: {\n name: 'grep',\n description: 'Search file contents by regex. Returns matching paths (default), match content, or per-file counts. Backed by ripgrep when available with an in-process glob fallback for in-process runs.',\n inputSchema: {\n type: 'object',\n properties: {\n 'pattern': { type: 'string', description: 'Regex (PCRE-flavored via ripgrep, JS regex via fallback).' },\n 'path': { type: 'string', description: 'File or directory to search. Default: \".\".' },\n 'glob': { type: 'string', description: 'Restrict to files matching this glob, e.g. \"**/*.ts\".' },\n 'type': { type: 'string', description: 'rg file type filter, e.g. \"ts\", \"py\", \"rust\". Ignored by the fallback.' },\n 'output_mode': { type: 'string', enum: ['content', 'files_with_matches', 'count'], description: 'Default: \"files_with_matches\".' },\n '-i': { type: 'boolean', description: 'Case-insensitive match.' },\n '-n': { type: 'boolean', description: 'Show line numbers (content mode). Default: true.' },\n '-A': { type: 'integer', description: 'Lines of trailing context (content mode).' },\n '-B': { type: 'integer', description: 'Lines of leading context (content mode).' },\n '-C': { type: 'integer', description: 'Lines of surrounding context (content mode). Overridden by -A/-B if set.' },\n 'multiline': { type: 'boolean', description: 'Allow patterns to match across line boundaries.' },\n 'head_limit': { type: 'integer', description: 'Cap output entries. Default: 250. Set 0 for unlimited.' },\n 'offset': { type: 'integer', description: 'Skip first N entries. Default: 0.' },\n },\n required: ['pattern'],\n },\n },\n async execute(rawInput, ctx: ToolContext) {\n const input = rawInput as unknown as GrepInput\n\n const useRg = await isRipgrepAvailable(ctx)\n if (useRg)\n return runViaRipgrep(input, ctx)\n\n if (ctx.execution.type === 'process')\n return runInProcess(input, ctx)\n\n return 'grep error: ripgrep is not available in the execution context. Install `rg` or use the `shell` tool with grep/awk.'\n },\n}\n\n// ---------------------------------------------------------------------------\n// ripgrep path\n// ---------------------------------------------------------------------------\n\n/**\n * Probe ripgrep availability **per call**. The probe is intentionally not\n * cached: caching at module scope would leak across execution contexts (an\n * orchestrator running an in-process agent and a docker agent in the same\n * Node process must be able to differ on whether `rg` exists), and caching\n * per handle adds bookkeeping for negligible savings — `rg --version` is\n * ~5 ms, and grep is invoked at most a few times per turn.\n */\nasync function isRipgrepAvailable(ctx: ToolContext): Promise<boolean> {\n const result = await ctx.execution.exec(ctx.handle, 'rg --version')\n return result.exitCode === 0\n}\n\nasync function runViaRipgrep(input: GrepInput, ctx: ToolContext): Promise<string> {\n const args = ['rg']\n const mode = (input.output_mode ?? DEFAULT_OUTPUT_MODE) as OutputMode\n\n if (mode === 'files_with_matches')\n args.push('--files-with-matches')\n else if (mode === 'count')\n args.push('--count')\n else\n args.push((input['-n'] ?? true) ? '--line-number' : '--no-line-number')\n\n if (input['-i'])\n args.push('-i')\n\n if (mode === 'content') {\n if (typeof input['-A'] === 'number')\n args.push('-A', String(input['-A']))\n if (typeof input['-B'] === 'number')\n args.push('-B', String(input['-B']))\n if (typeof input['-C'] === 'number' && typeof input['-A'] !== 'number' && typeof input['-B'] !== 'number')\n args.push('-C', String(input['-C']))\n }\n\n if (input.multiline)\n args.push('--multiline', '--multiline-dotall')\n\n if (input.glob)\n args.push('--glob', input.glob)\n\n if (input.type)\n args.push('--type', input.type)\n\n args.push('--', input.pattern)\n\n // Always pass a path — rg reads from stdin when invoked from a non-TTY child\n // process without a path argument, which hangs forever waiting for input.\n args.push(input.path ?? '.')\n\n const command = args.map(shellQuote).join(' ')\n const result = await ctx.execution.exec(ctx.handle, command)\n\n // ripgrep exits 1 when there are no matches — that's not an error here.\n if (result.exitCode !== 0 && result.exitCode !== 1) {\n return `grep error: ${result.stderr.trim() || `rg exited with code ${result.exitCode}`}`\n }\n\n return formatPaginated(result.stdout, input)\n}\n\n// ---------------------------------------------------------------------------\n// In-process fallback (fs/promises glob + readFile + regex)\n// ---------------------------------------------------------------------------\n\nasync function runInProcess(input: GrepInput, ctx: ToolContext): Promise<string> {\n const mode = (input.output_mode ?? DEFAULT_OUTPUT_MODE) as OutputMode\n const flags = `${input['-i'] ? 'i' : ''}${input.multiline ? 's' : ''}${mode !== 'content' ? '' : 'g'}`\n\n let regex: RegExp\n try {\n regex = new RegExp(input.pattern, flags || undefined)\n }\n catch (err) {\n return `grep error: invalid regex: ${errorMessage(err)}`\n }\n\n const files = await enumerateFiles(input, ctx)\n const showLineNumbers = input['-n'] ?? true\n const before = (input['-B'] ?? input['-C'] ?? 0)\n const after = (input['-A'] ?? input['-C'] ?? 0)\n\n const lines: string[] = []\n for (const path of files) {\n let content: string\n try {\n content = await ctx.execution.readFile(ctx.handle, path)\n }\n catch {\n continue\n }\n if (input.multiline) {\n // Multiline mode: scan the whole buffer with /g semantics.\n const allMatches = [...content.matchAll(new RegExp(regex.source, `${flags.replace(/g/, '')}g`))]\n if (allMatches.length === 0)\n continue\n if (mode === 'files_with_matches') {\n lines.push(path)\n continue\n }\n if (mode === 'count') {\n lines.push(`${path}:${allMatches.length}`)\n continue\n }\n // content mode — emit each match's surrounding line context.\n for (const m of allMatches) {\n const lineStart = content.lastIndexOf('\\n', m.index! - 1) + 1\n const lineEnd = content.indexOf('\\n', m.index!)\n const snippet = content.slice(lineStart, lineEnd === -1 ? undefined : lineEnd)\n const lineNo = content.slice(0, m.index!).split('\\n').length\n lines.push(formatContentLine(path, lineNo, snippet, showLineNumbers))\n }\n continue\n }\n\n const fileLines = content.split('\\n')\n const matched: number[] = []\n for (let i = 0; i < fileLines.length; i++) {\n regex.lastIndex = 0\n if (regex.test(fileLines[i]))\n matched.push(i)\n }\n if (matched.length === 0)\n continue\n\n if (mode === 'files_with_matches') {\n lines.push(path)\n continue\n }\n if (mode === 'count') {\n lines.push(`${path}:${matched.length}`)\n continue\n }\n\n const includeLineNos = new Set<number>()\n for (const m of matched) {\n for (let i = Math.max(0, m - before); i <= Math.min(fileLines.length - 1, m + after); i++)\n includeLineNos.add(i)\n }\n const sorted = [...includeLineNos].sort((a, b) => a - b)\n let prev = -2\n for (const lineNo of sorted) {\n if (lineNo > prev + 1 && lines.length > 0)\n lines.push('--')\n const snippet = fileLines[lineNo]\n lines.push(formatContentLine(path, lineNo + 1, snippet, showLineNumbers))\n prev = lineNo\n }\n }\n\n return formatPaginated(lines.join('\\n'), input)\n}\n\nfunction formatContentLine(path: string, lineNo: number, snippet: string, showLineNumbers: boolean): string {\n return showLineNumbers ? `${path}:${lineNo}:${snippet}` : `${path}:${snippet}`\n}\n\nasync function enumerateFiles(input: GrepInput, ctx: ToolContext): Promise<string[]> {\n const cwd = ctx.handle.cwd\n const root = input.path ?? '.'\n\n // If `path` is a single file, just return it.\n // Cheap heuristic: contains a dot in the basename and no glob wildcards.\n if (input.path && !input.path.includes('*') && !input.path.includes('?')) {\n try {\n const stat = await ctx.execution.exec(ctx.handle, `test -f ${shellQuote(input.path)} && echo file || echo dir`)\n if (stat.stdout.trim() === 'file')\n return [input.path]\n }\n catch { /* fall through to glob enumeration */ }\n }\n\n const pattern = input.glob ?? '**/*'\n const scanRoot = root === '.' ? cwd : `${cwd.replace(/\\/$/, '')}/${root.replace(/^\\.\\//, '')}`\n const prefix = root === '.' ? '' : `${root.replace(/\\/$/, '')}/`\n // node:fs/promises glob yields directories alongside files and does not yet\n // accept `withFileTypes` under Bun, so we post-filter via stat. Streaming\n // (inline await rather than collecting candidates + Promise.all) keeps\n // memory bounded on huge trees at the cost of serial stat calls; for the\n // grep fallback path (no rg installed) latency is dominated by file reads\n // downstream anyway.\n const out: string[] = []\n for await (const rel of nodeGlob(pattern, { cwd: scanRoot })) {\n try {\n const s = await fsStat(resolve(scanRoot, rel))\n if (s.isFile())\n out.push(prefix ? `${prefix}${rel}` : rel)\n }\n catch { /* race with deletion or perms — skip */ }\n }\n return out.sort()\n}\n\nfunction formatPaginated(text: string, input: GrepInput): string {\n const headLimit = typeof input.head_limit === 'number' && input.head_limit >= 0\n ? input.head_limit\n : DEFAULT_HEAD_LIMIT\n const offset = typeof input.offset === 'number' && input.offset > 0\n ? Math.floor(input.offset)\n : 0\n\n if (!text.trim())\n return '(no matches)'\n\n const lines = text.split('\\n').filter(l => l.length > 0)\n const total = lines.length\n\n const sliced = headLimit === 0\n ? lines.slice(offset)\n : lines.slice(offset, offset + headLimit)\n\n if (sliced.length === 0)\n return '(no matches in this slice)'\n\n const truncatedHead = offset > 0\n const truncatedTail = headLimit > 0 && offset + headLimit < total\n\n let out = sliced.join('\\n')\n if (truncatedHead)\n out = `…(${offset} earlier matches skipped)…\\n${out}`\n if (truncatedTail)\n out = `${out}\\n…(${total - offset - headLimit} more matches; re-run with offset=${offset + headLimit} or larger head_limit)`\n return out\n}\n","/**\n * Interaction tool — lets the agent request structured input from the outside world.\n *\n * Not included in any preset by default. Add it explicitly:\n *\n * import { createInteractionTool } from 'zidane'\n *\n * const askUser = createInteractionTool({\n * schema: { type: 'object', properties: { question: { type: 'string' } }, required: ['question'] },\n * onRequest: async (payload) => {\n * const answer = await promptUser(payload.question)\n * return { answer }\n * },\n * })\n *\n * const preset = definePreset({ name: 'interactive', tools: { ...basicTools, ask_user: askUser } })\n */\n\nimport type { ToolContext, ToolDef } from './types'\n\nexport interface InteractionToolOptions {\n /** JSON Schema for the request payload the model sends */\n schema: Record<string, unknown>\n /** Tool name (default: 'interaction') */\n name?: string\n /** Tool description shown to the model */\n description?: string\n /** Called when the model invokes this tool. Receives the validated payload and tool context, returns data for the model. */\n onRequest: (payload: Record<string, unknown>, ctx: ToolContext) => Promise<Record<string, unknown> | string>\n}\n\n/**\n * Create an interaction tool that lets the agent request structured input.\n *\n * The model calls this tool with a payload matching the schema.\n * `onRequest` is called with the payload and should return the response\n * (string or object) that gets sent back to the model as the tool result.\n */\nexport function createInteractionTool(options: InteractionToolOptions): ToolDef {\n const name = options.name ?? 'interaction'\n const description = options.description ?? 'Request structured input from the user or external system.'\n\n return {\n spec: {\n name,\n description,\n inputSchema: options.schema,\n },\n async execute(input, ctx) {\n const result = await options.onRequest(input, ctx)\n\n return typeof result === 'string' ? result : JSON.stringify(result)\n },\n }\n}\n","import type { ToolContext, ToolDef } from './types'\n\nexport const listFiles: ToolDef = {\n // Directory listing only — safe to parallelize alongside other reads.\n isConcurrencySafe: true,\n spec: {\n name: 'list_files',\n description: 'List the immediate entries (files and subdirectories) at `path`. Returns a newline-separated list, or `(empty directory)` when the directory exists but is empty. Returns `Directory not found: <path>` for a missing path. Non-recursive — use `glob` for pattern matching across nested directories.',\n inputSchema: {\n type: 'object',\n properties: {\n path: { type: 'string', description: 'Directory path (relative to the execution-context cwd, or absolute). Defaults to `.`.' },\n },\n required: [],\n },\n },\n async execute({ path }, ctx: ToolContext) {\n try {\n const entries = await ctx.execution.listFiles(ctx.handle, (path as string) || '.')\n return entries.join('\\n') || '(empty directory)'\n }\n catch {\n return `Directory not found: ${path}`\n }\n },\n}\n","import type { ToolContext, ToolDef } from './types'\nimport { resolveOldString, styleReplacementForVia } from './edit-utils'\nimport { suggestionFor } from './path-suggest'\nimport { hashContent, readStateKey, resolveReadStateMap } from './read-state'\n\n/**\n * Multi-edit on a single file — best-effort, per-hunk semantics.\n *\n * Each step replaces `old_string` with `new_string` against the file\n * contents *as left by the previous applied edit*. A per-step failure\n * (`old_string` not found, ambiguous match without `replace_all`,\n * identical strings, malformed input) is recorded against THAT step\n * only — the remaining steps still run. The file is written iff at\n * least one step applied. The result text reports per-hunk outcomes\n * inline and (when any step failed) carries an\n * `<edit-outcomes>…</edit-outcomes>` annotation block so the chat layer\n * + replay can paint per-hunk badges via `parseEditOutcomesFromResult`.\n *\n * Per-hunk approval is a TUI/host concern. When the user partially\n * denies a `multi_edit` from the file-edit modal, the gate handler\n * rebinds `ctx.input.edits` to the approved subset before this tool\n * runs — so the body sees a smaller all-approved batch and reports\n * outcomes keyed against THAT subset. The host's `tool:transform`\n * hook merges this subset-keyed result with the approval-side denied\n * entries (1:1 with the model's ORIGINAL `edits` list) before the\n * canonical `<edit-outcomes>` block lands on the wire — see\n * `mergeApprovalAndBodyOutcomes` in `chat/edit-approval.ts`.\n *\n * Wire / replay format (see `parseEditOutcomesFromResult`):\n *\n * Edited <path>: applied N of M edits (R replacements).\n * edit #2 failed: old_string not found in <path>.\n *\n * <edit-outcomes>\n * #1 applied\n * #2 failed: old_string not found in <path>\n * #3 applied\n * </edit-outcomes>\n *\n * The annotation is omitted entirely on the all-applied path — the\n * renderer's `isEditErrorResult` check then suppresses the paired\n * `tool-result` so the diff stands alone (existing behavior).\n */\n\ninterface EditStep {\n old_string: string\n new_string: string\n replace_all?: boolean\n}\n\ninterface StepOutcome {\n kind: 'applied' | 'failed'\n reason?: string\n}\n\n/**\n * Inline annotation builder — kept local to avoid importing from\n * `chat/edit-approval.ts` (that's a renderer-side module; tools live\n * one layer below). Line shape matches `parseEditOutcomesFromResult`'s\n * regex so the round-trip is lossless.\n *\n * Newlines in `reason` are folded to spaces because the parser is line-\n * scoped (`body.split('\\n')`); a multi-line reason would split into a\n * \"trailing prose\" line and trip the malformed-block guard, losing every\n * outcome below it. Static reasons in this file are single-line; the\n * sanitize is a guard against a pathological `target` (file path\n * containing a newline) leaking into `old_string not found in <target>`.\n */\nfunction annotationFor(outcomes: readonly StepOutcome[]): string {\n const lines = ['<edit-outcomes>']\n for (let i = 0; i < outcomes.length; i++) {\n const o = outcomes[i]\n const reason = o.reason ? `: ${o.reason.replace(/\\r?\\n/g, ' ')}` : ''\n lines.push(`#${i + 1} ${o.kind}${reason}`)\n }\n lines.push('</edit-outcomes>')\n return lines.join('\\n')\n}\n\nexport const multiEdit: ToolDef = {\n spec: {\n name: 'multi_edit',\n description: 'Apply a sequential list of edits to a file. Each edit operates on the result of the previous APPLIED edit. Prefer this over multiple `edit` calls when several non-overlapping changes are needed in the same file. Edits run **best-effort**: a per-step failure (`old_string` not found, ambiguous match without `replace_all`, identical strings) is reported in the result but does NOT block the remaining steps. The file is written iff at least one step applied. The result lists per-hunk outcomes (`applied` / `failed`) so the model can re-issue just the failures without resending the whole batch. Each step tolerates `read_file` line-number prefixes (`<N>\\\\t…`, `<N>|…`, or `<N>→…`) in `old_string` / `new_string`.',\n inputSchema: {\n type: 'object',\n properties: {\n path: { type: 'string', description: '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 if (!Array.isArray(steps) || steps.length === 0)\n return `multi_edit error: edits must be a non-empty array.`\n\n let current: string\n try {\n current = await ctx.execution.readFile(ctx.handle, target)\n }\n catch {\n const hint = await suggestionFor(ctx.execution, ctx.handle, target)\n return `multi_edit error: file not found: ${target}.${hint}`\n }\n\n // Read-before-edit guard — see the matching block in `src/tools/\n // edit.ts` for the rationale and the no-read-state-map fall-through.\n if (ctx.behavior?.requireReadBeforeEdit) {\n const readState = resolveReadStateMap(ctx)\n if (readState) {\n const absKey = readStateKey(ctx.handle.cwd, target)\n const prior = readState.get(absKey)\n if (!prior)\n return `multi_edit error: ${target} has not been read in this session. Call read_file first so the edits apply against the current contents.`\n if (prior.contentHash !== hashContent(current))\n return `multi_edit error: ${target} has changed on disk since the last read. Re-read the file before editing.`\n }\n }\n\n const outcomes: StepOutcome[] = []\n let totalReplacements = 0\n\n for (let i = 0; i < steps.length; i++) {\n const step = steps[i]\n const find = step.old_string\n const replacement = step.new_string\n const replaceAll = step.replace_all === true\n\n if (typeof find !== 'string' || typeof replacement !== 'string') {\n outcomes.push({ kind: 'failed', reason: 'missing old_string or new_string' })\n continue\n }\n\n if (find.length === 0) {\n outcomes.push({ kind: 'failed', reason: 'empty old_string (use write_file to fully replace a file)' })\n continue\n }\n\n if (find === replacement) {\n outcomes.push({ kind: 'failed', reason: 'old_string and new_string are identical' })\n continue\n }\n\n const match = resolveOldString(current, find)\n if (!match) {\n outcomes.push({ kind: 'failed', reason: `old_string not found in ${target}` })\n continue\n }\n\n const { actual, occurrences, via } = match\n if (occurrences > 1 && !replaceAll) {\n outcomes.push({\n kind: 'failed',\n reason: `old_string appears ${occurrences} times — pass replace_all=true on this edit or expand old_string for uniqueness`,\n })\n continue\n }\n\n const styledReplacement = styleReplacementForVia(replacement, via, actual)\n current = replaceAll\n ? current.split(actual).join(styledReplacement)\n : current.replace(actual, styledReplacement)\n totalReplacements += occurrences\n outcomes.push({ kind: 'applied' })\n }\n\n const appliedCount = outcomes.reduce((n, o) => o.kind === 'applied' ? n + 1 : n, 0)\n const failedCount = outcomes.length - appliedCount\n\n // Skip the write when nothing changed. Preserves mtime so a partner\n // process watching the file doesn't see a phantom touch on a fully-\n // failed batch.\n if (appliedCount > 0) {\n await ctx.execution.writeFile(ctx.handle, target, current)\n\n // Bring the tracked hash forward so a follow-up edit on the same\n // file doesn't trip the drift branch on freshly-rewritten bytes.\n const readState = resolveReadStateMap(ctx)\n if (readState) {\n const absKey = readStateKey(ctx.handle.cwd, target)\n const prior = readState.get(absKey)\n if (prior)\n readState.set(absKey, { ...prior, contentHash: hashContent(current), mtimeMs: Date.now() })\n }\n }\n\n const n = steps.length\n\n // Header shape — three branches, each chosen for renderer + model\n // legibility:\n //\n // - All applied → legacy \"Edited X: applied N edits (R replacements).\"\n // so the renderer's `isEditErrorResult` keeps suppressing the\n // paired tool-result (the diff stands alone on a clean success).\n // - Mixed → \"Edited X: applied N of M edits (R replacements).\"\n // plus per-failure lines + `<edit-outcomes>` block.\n // - All failed → \"multi_edit error: no edits applied to X (M attempted).\"\n // keeps the legacy \"multi_edit error:\" prefix so anything that\n // pattern-matched on it (renderer visibility branch, status\n // ribbons, log filters) keeps working.\n let header: string\n if (appliedCount === n) {\n header = `Edited ${target}: applied ${n} edit${n === 1 ? '' : 's'} (${totalReplacements} replacement${totalReplacements === 1 ? '' : 's'}).`\n }\n else if (appliedCount > 0) {\n header = `Edited ${target}: applied ${appliedCount} of ${n} edits (${totalReplacements} replacement${totalReplacements === 1 ? '' : 's'}).`\n }\n else {\n header = `multi_edit error: no edits applied to ${target} (${n} attempted).`\n }\n\n const failureLines: string[] = []\n for (let i = 0; i < outcomes.length; i++) {\n const o = outcomes[i]\n if (o.kind === 'failed')\n failureLines.push(`edit #${i + 1} failed: ${o.reason}`)\n }\n\n // Annotation is the body's side-channel for replay + chat-layer\n // merge. Omit on the all-applied happy path so the renderer can\n // continue to hide the paired tool-result; emit whenever any step\n // failed so the chat layer + replay can paint per-hunk badges.\n const parts = [header]\n if (failureLines.length > 0)\n parts.push(failureLines.join('\\n'))\n if (failedCount > 0)\n parts.push(annotationFor(outcomes))\n return parts.join('\\n\\n')\n },\n}\n","/**\n * Binary-aware file read for the `read_file` tool.\n *\n * Dispatches to `ExecutionContext.readFileBinary` when implemented; otherwise\n * shells out via `base64 < path` so docker / sandbox contexts don't have to\n * implement a custom primitive. The shell fallback is portable (busybox /\n * coreutils / macOS all ship `base64`) and only fires when the native path\n * is unavailable.\n *\n * Returns a base64 string ready to drop into a `ToolResultContent` image\n * block. Raises on read failure — caller is responsible for catching and\n * formatting the error message.\n */\n\nimport type { ExecutionContext, ExecutionHandle } from '../contexts'\nimport { Buffer } from 'node:buffer'\nimport { alwaysQuote } from './shell-quote'\n\n/**\n * Best-effort guess at IANA media type from a file extension. Covers the\n * extensions Zidane's `read_file` actually dispatches to image blocks\n * (png/jpg/jpeg/gif/webp). Other extensions return `undefined` and the\n * caller short-circuits the binary route.\n */\nexport function imageMediaTypeFor(path: string): string | undefined {\n const dot = path.lastIndexOf('.')\n if (dot === -1)\n return undefined\n const ext = path.slice(dot + 1).toLowerCase()\n switch (ext) {\n case 'png':\n return 'image/png'\n case 'jpg':\n case 'jpeg':\n return 'image/jpeg'\n case 'gif':\n return 'image/gif'\n case 'webp':\n return 'image/webp'\n default:\n return undefined\n }\n}\n\n/**\n * Read a file as base64. Prefers `ExecutionContext.readFileBinary` (zero\n * subprocess overhead in-process) and falls back to `base64 < path` via\n * the shell seam — works on docker / sandbox without an interface change.\n *\n * Returns `{ base64, byteLength }`. `byteLength` is the *decoded* byte count\n * so callers can size-budget against the original file, not the inflated\n * base64 representation (which is ~4/3× larger).\n */\nexport async function readFileAsBase64(\n execution: ExecutionContext,\n handle: ExecutionHandle,\n path: string,\n): Promise<{ base64: string, byteLength: number }> {\n if (execution.readFileBinary) {\n const bytes = await execution.readFileBinary(handle, path)\n const b64 = Buffer.from(bytes.buffer, bytes.byteOffset, bytes.byteLength).toString('base64')\n return { base64: b64, byteLength: bytes.byteLength }\n }\n\n // Shell fallback. Use `base64 -i` on macOS-style implementations; the\n // GNU/coreutils form just takes the path positionally. Try both.\n // `2>/dev/null` swallows the macOS warning when the GNU form is used on\n // a system without `-i`.\n const cmd = `base64 < ${alwaysQuote(path)}`\n const result = await execution.exec(handle, cmd)\n if (result.exitCode !== 0)\n throw new Error(`base64 read failed: ${result.stderr || `exit ${result.exitCode}`}`)\n // `base64` may emit line-wrapped output (76-col default); strip whitespace.\n const b64 = result.stdout.replace(/\\s+/g, '')\n return { base64: b64, byteLength: decodedBase64ByteLength(b64) }\n}\n\n/**\n * Decoded byte length of a (whitespace-stripped) base64 string. Accounts for\n * `=` padding so the value matches the original file size to the byte —\n * `Math.floor(len * 3 / 4)` over-reports by 1–2 bytes on padded payloads.\n */\nfunction decodedBase64ByteLength(b64: string): number {\n if (b64.length === 0)\n return 0\n let pad = 0\n if (b64.endsWith('=='))\n pad = 2\n else if (b64.endsWith('='))\n pad = 1\n return Math.max(0, (b64.length * 3) / 4 - pad)\n}\n","import type { ToolResultContent } from '../types'\nimport type { ToolContext, ToolDef } from './types'\nimport { Buffer } from 'node:buffer'\nimport { errorMessage } from '../errors'\nimport { looksBinary } from './binary-detect'\nimport { imageMediaTypeFor, readFileAsBase64 } from './binary-read'\nimport { reconcileImageMediaType } from './image-sniff'\nimport { suggestionFor } from './path-suggest'\nimport { hashContent, readStateKey, resolveReadStateMap } from './read-state'\n\n/**\n * Read a file with line-based offset/limit and a hard byte cap.\n *\n * Defaults are tuned for source code: 2000 lines / 256 KiB. A typical source\n * file, lockfile, or large config fits in one read; logs and very large\n * fixtures get truncated with a footer that documents how to fetch the\n * remainder.\n *\n * Binary files are detected on the leading bytes — if the buffer contains a\n * NUL or has an unreasonable proportion of non-printable bytes, we skip text\n * decoding and return a marker so the model doesn't drown in mojibake.\n */\n\nconst DEFAULT_LINE_LIMIT = 2000\nconst DEFAULT_BYTE_CAP = 262_144\n\n/**\n * Hard upper bound on raw image bytes we'll inline as a base64 image block.\n * Above this, we return a marker instead — the model won't get useful\n * information from a 10 MB+ screenshot rendered as one tool result, and\n * the wire bill gets ugly. Override via the `maxBytes` parameter on the\n * tool call.\n */\nconst DEFAULT_IMAGE_BYTE_CAP = 5 * 1024 * 1024\n\nexport const readFile: ToolDef = {\n // Pure read — fans out in parallel with other concurrency-safe siblings\n // up to `behavior.maxConcurrentTools`.\n isConcurrencySafe: true,\n spec: {\n name: 'read_file',\n description: 'Read a file by path. Returns lines [offset..offset+limit). Default offset=1, limit=2000. Each line is prefixed with its 1-indexed line number followed by a tab (e.g. `42\\\\tconst foo = bar`); the prefix is metadata, not part of the file. Mirrors Claude Code\\'s `cat -n`-style compact output for token efficiency. A trailing footer explains how to read the rest when truncated. Binary files return a short marker rather than mojibake.',\n inputSchema: {\n type: 'object',\n properties: {\n path: { type: 'string', description: '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. Set 0 for unlimited. The rendered output may be slightly larger than this cap when `lineNumbers` is on (each line carries a `<N>\\\\t` prefix).' },\n lineNumbers: { type: 'boolean', description: 'Prefix each line with its 1-indexed line number. Default: true. Override the agent-wide `behavior.readLineNumbers` for this call.' },\n },\n required: ['path'],\n },\n },\n async execute({ path, offset, limit, maxBytes, lineNumbers }, ctx: ToolContext) {\n // Image dispatch — vision-aware models can answer questions directly\n // against the image bytes; non-vision models get the marker substitution\n // applied later in the loop (`stripImagesForNonVision`). Skips the line-\n // based read entirely. Gated on extension first (cheap), then reconciled\n // against magic bytes — Anthropic rejects the whole request when the\n // declared `media_type` disagrees with the actual bytes (e.g. a JPEG\n // saved with a `.webp` extension).\n const extMedia = imageMediaTypeFor(path as string)\n if (extMedia) {\n const sizeCap = maxBytes !== undefined ? normalizeInteger(maxBytes, DEFAULT_IMAGE_BYTE_CAP) : DEFAULT_IMAGE_BYTE_CAP\n try {\n const { base64, byteLength } = await readFileAsBase64(ctx.execution, ctx.handle, path as string)\n if (sizeCap > 0 && byteLength > sizeCap) {\n return `[image too large to inline: ${path}, ${byteLength} bytes (cap ${sizeCap}). Raise maxBytes, or use shell to inspect.]`\n }\n const 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 let raw: string\n try {\n raw = await ctx.execution.readFile(ctx.handle, path as string)\n }\n catch {\n const hint = await suggestionFor(ctx.execution, ctx.handle, path as string)\n return `File not found: ${path}.${hint}`\n }\n\n const totalBytes = Buffer.byteLength(raw)\n\n // Read-state tracking + per-session dedup. Tracking populates the\n // map so `requireReadBeforeEdit` can confirm the model has seen\n // the file; dedup decides whether identical re-reads should\n // short-circuit to a \"still current\" stub. The two are\n // INDEPENDENT — turning off dedup must NOT also break the gate.\n //\n // Tracking runs whenever either feature is enabled (or by default,\n // since dedup defaults on); dedup short-circuit only fires when\n // `dedupReads` is explicitly on. Costs nothing when no session is\n // bound (direct/standalone tool invocations).\n //\n // The hash is taken on the *raw* file before any truncation so a\n // future read with a wider slice still invalidates the prior\n // stub-eligible entry — re-emitting the freshly-included bytes.\n const dedupEnabled = ctx.behavior?.dedupReads !== false\n const gateEnabled = ctx.behavior?.requireReadBeforeEdit === true\n const trackingEnabled = dedupEnabled || gateEnabled\n const readState = trackingEnabled ? resolveReadStateMap(ctx) : undefined\n const absKey = readStateKey(ctx.handle.cwd, path as string)\n const offsetForKey = normalizeInteger(offset, 1)\n const limitForKey = normalizeInteger(limit, DEFAULT_LINE_LIMIT)\n const maxBytesForKey = normalizeInteger(maxBytes, DEFAULT_BYTE_CAP)\n const showLineNumbers = typeof lineNumbers === 'boolean'\n ? lineNumbers\n : (ctx.behavior?.readLineNumbers ?? true)\n const currentHash = readState ? hashContent(raw) : ''\n if (dedupEnabled && readState) {\n const prior = readState.get(absKey)\n if (\n prior\n && prior.contentHash === currentHash\n && prior.offset === offsetForKey\n && prior.limit === limitForKey\n && prior.maxBytes === maxBytesForKey\n // Toggling `lineNumbers` between reads must miss dedup: the stub\n // claims \"the prior result is still current\", but the prior\n // output was a different shape — the model would draw bogus\n // conclusions if we replayed it.\n && prior.lineNumbers === showLineNumbers\n ) {\n return `File ${path} unchanged since the previous read in this session — the prior result is still current.`\n }\n }\n\n // Cheap binary detection on a leading sample. ExecutionContext.readFile\n // returns UTF-8-decoded text already — but if the underlying file was\n // binary, the decode replaces invalid sequences with U+FFFD. Use a NUL\n // byte and replacement-char ratio in the sample as a heuristic.\n if (looksBinary(raw)) {\n return `[binary file: ${path}, ${totalBytes} bytes; use shell with hexdump | xxd | od to inspect]`\n }\n\n const offsetN = offsetForKey\n const limitN = limitForKey\n const maxBytesN = maxBytesForKey\n\n const lines = raw.split('\\n')\n const totalLines = lines.length\n\n // 1-indexed offset → 0-indexed slice start.\n const startIdx = Math.max(0, offsetN - 1)\n const endIdx = limitN > 0 ? Math.min(totalLines, startIdx + limitN) : totalLines\n let slice = lines.slice(startIdx, endIdx)\n\n // Apply byte cap at the line boundary. Walk lines, accumulating bytes; cut\n // just below the cap. The \"truncatedSlice.length > 0\" guard ensures we\n // always emit at least one line — files with a single oversized line would\n // otherwise return empty. The single-line overflow gets handled by the\n // hard-cap pass below.\n let bytesCut = false\n if (maxBytesN > 0) {\n const truncatedSlice: string[] = []\n let bytesUsed = 0\n for (const line of slice) {\n const lineBytes = Buffer.byteLength(line) + 1 // +1 for the '\\n' rejoin\n if (bytesUsed + lineBytes > maxBytesN && truncatedSlice.length > 0) {\n bytesCut = true\n break\n }\n truncatedSlice.push(line)\n bytesUsed += lineBytes\n if (bytesUsed >= maxBytesN) {\n // Budget consumed — keep this line, stop here.\n break\n }\n }\n if (truncatedSlice.length < slice.length)\n bytesCut = true\n slice = truncatedSlice\n }\n\n // Hard-cap pass: when the line-boundary loop had to admit an oversized line\n // (single huge line, or first line ≥ maxBytes), the body still exceeds the\n // budget. Truncate the last line at a UTF-8-safe boundary and flag it as a\n // mid-line cut so the footer can warn the reader that offset+1 won't pick\n // up where this read left off.\n let midLineCut = false\n if (maxBytesN > 0 && slice.length > 0) {\n const bodyBytes = Buffer.byteLength(slice.join('\\n'))\n if (bodyBytes > maxBytesN) {\n const lastIdx = slice.length - 1\n const lastLine = slice[lastIdx]\n const otherBytes = lastIdx > 0\n ? Buffer.byteLength(slice.slice(0, lastIdx).join('\\n')) + 1 // +1 for the joining '\\n'\n : 0\n const budgetForLast = Math.max(0, maxBytesN - otherBytes)\n // Estimate cut at char count = byte budget (correct for ASCII), then\n // walk back if the resulting prefix still overflows because of multi-\n // byte codepoints. Worst case ~3 iterations (max UTF-8 width is 4).\n let cut = Math.min(lastLine.length, budgetForLast)\n while (cut > 0 && Buffer.byteLength(lastLine.slice(0, cut)) > budgetForLast)\n cut--\n slice[lastIdx] = lastLine.slice(0, cut)\n midLineCut = true\n bytesCut = true\n }\n }\n\n const linesReturned = slice.length\n const lastLineRead = startIdx + linesReturned\n\n // Line numbers default on. Format: `<line-no>\\t<content>` — bare digit,\n // tab separator, no padding. Matches Claude Code's compact `cat -n`\n // output for token efficiency (~5 chars saved per line vs. padded\n // forms). Models treat the prefix as metadata; `edit` strips it from\n // `old_string` when the model pastes a numbered chunk back, and also\n // accepts `<N>|<content>` / `<N>→<content>` for cross-stack tolerance.\n const body = showLineNumbers\n ? slice.map((line, i) => `${startIdx + i + 1}\\t${line}`).join('\\n')\n : slice.join('\\n')\n\n if (readState) {\n readState.set(absKey, {\n contentHash: currentHash,\n offset: offsetN,\n limit: limitN,\n maxBytes: maxBytesN,\n lineNumbers: showLineNumbers,\n mtimeMs: Date.now(),\n })\n }\n\n const linesTruncated = endIdx < totalLines || bytesCut\n if (!linesTruncated && offsetN === 1)\n return body\n\n if (!linesTruncated) {\n // Reading from a non-1 offset; tell the model where the read started.\n return `${body}\\n\\n…read lines ${offsetN}-${lastLineRead} of ${totalLines}.`\n }\n\n if (midLineCut) {\n // Line N was only partially read, so offset=N+1 would skip the unread\n // portion — we don't suggest it. Raising maxBytes is the only lossless\n // way forward. Avoid suggesting shell+sed/awk slicing here: that primes\n // a debugging anti-pattern where the model loops on narrow shell probes\n // instead of using paginated read_file calls.\n return `${body}\\n\\n…truncated mid-line at line ${lastLineRead} (byte cap ${maxBytesN} reached). File has ${totalLines} lines, ${totalBytes} bytes total. Raise maxBytes to read the full line.`\n }\n\n const reason = bytesCut ? `byte cap (${maxBytesN}) reached` : `line limit (${limitN}) reached`\n return `${body}\\n\\n…truncated at line ${lastLineRead} (${reason}). File has ${totalLines} lines, ${totalBytes} bytes total — re-read with offset=${lastLineRead + 1} to continue.`\n },\n}\n\nfunction normalizeInteger(value: unknown, fallback: number): number {\n if (typeof value !== 'number' || !Number.isFinite(value))\n return fallback\n // Validation auto-coerces strings to numbers, so by the time we get here\n // a non-finite value means the caller passed something garbled.\n if (value < 0)\n return fallback\n return Math.floor(value)\n}\n","/**\n * `shell_kill` — terminate a running background task by id.\n *\n * Companion to the `shell` tool's `run_in_background: true` mode. The\n * model receives a `task_id` when it spawns a background task; this\n * tool routes a kill request through `ExecutionContext.killBackground`,\n * which SIGTERMs the whole process group (the foreground `exec`\n * implementation already documents the kill-tree rationale).\n *\n * The successful kill ALSO suppresses the would-be `<task-notification>`\n * on the next turn — the agent's `tool:after` listener (see\n * `src/agent.ts`'s notification-suppression block) deletes the pending\n * entry by `task_id`. Without that, the model would get the kill result\n * inline AND a redundant notification on its next prompt.\n */\n\nimport type { ToolContext, ToolDef } from './types'\nimport { formatDuration, formatTaskStatus, previewLine } from '../chat/format'\n\nexport const shellKill: ToolDef = {\n // Concurrency-safe — pure SIGTERM dispatch, no shared mutable state\n // beyond the per-context registry which is atomic.\n isConcurrencySafe: true,\n spec: {\n name: 'shell_kill',\n description: [\n 'Terminate a running background task started by `shell({ run_in_background: true })`.',\n 'Sends SIGTERM to the whole process group so the shell wrapper AND its child commands die together.',\n 'Returns the final exit info (status, exit code, output path) or a \"no such task\" message when the id is unknown / already cleaned up.',\n 'Idempotent — calling on a task that has already terminated returns the cached exit info without re-killing.',\n ].join('\\n'),\n inputSchema: {\n type: 'object',\n properties: {\n task_id: { type: 'string', description: 'The task id returned by a prior `shell({ run_in_background: true })` call.' },\n },\n required: ['task_id'],\n additionalProperties: false,\n },\n },\n async execute(input, ctx: ToolContext): Promise<string> {\n const taskId = input.task_id as string\n\n if (!ctx.execution.killBackground) {\n return `shell_kill error: the active execution context (${ctx.execution.type}) does not support background tasks.`\n }\n\n const info = await ctx.execution.killBackground(ctx.handle, taskId)\n if (!info) {\n return `shell_kill: no such task \"${taskId}\". It may have already exited and been cleaned up, or it was never started in this session.`\n }\n\n return [\n `Killed ${info.taskId} — ${formatTaskStatus(info)} after ${formatDuration(info.durationMs)}.`,\n ` command: ${previewLine(info.command, 60)}`,\n ` output: ${info.outputPath}`,\n ].join('\\n')\n },\n}\n","/**\n * Spawn tool — create sub-agents from a parent agent.\n *\n * A configurable factory that reads the parent's preset-y fields from ToolContext.\n *\n * Usage:\n * ```ts\n * import { createSpawnTool } from 'zidane'\n * import { definePreset, basicTools } from 'zidane/presets'\n *\n * const preset = definePreset({\n * name: 'orchestrator',\n * tools: { ...basicTools, spawn: createSpawnTool({ maxConcurrent: 5 }) },\n * })\n * ```\n *\n * Each `createSpawnTool()` call returns a fresh instance with its own\n * concurrency counter, depth cap, and child-stats accumulator — never\n * share an instance across unrelated parent agents.\n *\n * Key guarantees:\n * - **Depth-capped** to `maxDepth` (default 3) to prevent infinite recursion.\n * - **Concurrency slot is reserved synchronously** before any `await`, so a\n * parent running tools in parallel cannot exceed `maxConcurrent`.\n * - **Pre-aborted signals short-circuit** without paying agent-spawn cost.\n * - **Abort / timeout / error are surfaced distinctly** in the returned text\n * and via `ChildRunStats.status` on `spawn:complete`.\n * - **`agent.destroy()` errors never mask the original run error** (captured\n * and emitted via `spawn:error` but the primary error wins).\n * - **Child hooks bubble** to the parent as `child:*` events when\n * `forwardHooks` is set (default `true`).\n * - **Persistence** via `persist: true` — child runs get appended to the\n * parent's session with `parentRunId` wired, so the run tree is\n * reconstructible from a stored `SessionData`.\n */\n\nimport type { Hookable } from 'hookable'\nimport type { AgentHooks } from '../agent'\nimport type { Preset } from '../presets'\nimport type { Session } from '../session'\nimport type { AgentStats, ChildRunStats } from '../types'\nimport type { ToolContext, ToolDef } from './types'\nimport { createAgent } from '../agent'\nimport { flattenTurns, formatTokenUsage } from '../stats'\nimport { resolveReadStateMap } from './read-state'\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport interface ChildAgent {\n id: string\n task: string\n startedAt: number\n /** Subagent depth — 1 for a direct child of a top-level agent. */\n depth: number\n}\n\nexport interface SpawnToolState {\n /** Currently running children. */\n readonly children: ReadonlyMap<string, ChildAgent>\n /**\n * Cumulative stats across every completed direct child of this spawn-tool\n * instance (returns a copy). Each child's contribution is the cumulative\n * `AgentStats` returned by its `agent.run()` — so\n * `totalIn`/`totalOut`/`totalCacheRead`/`totalCacheCreation` cover the\n * entire subtree (children + grandchildren + …), while `turns` and\n * `elapsed` stay parent-loop-only per child and are summed across direct\n * children. `elapsed` over-counts when children ran in parallel.\n *\n * Lives across multiple parent runs that share this instance.\n */\n readonly totalChildStats: Readonly<AgentStats>\n}\n\n// ---------------------------------------------------------------------------\n// Events to bubble from child → parent. Keep these lists small: the goal is\n// to give the parent a usable live-tail (and a place to gate child calls)\n// without drowning it in telemetry.\n//\n// Two kinds:\n// - {@link BUBBLED_EVENTS} — observational. Forwarded as a spread copy so\n// the parent's listeners can't accidentally mutate the child's ctx.\n// - {@link BUBBLED_GATE_EVENTS} — gates. Forwarded with the SAME ctx\n// reference so parent listeners can set `block` / `reason` / `result`\n// and have those mutations land on the gate the child's loop is\n// awaiting on.\n// ---------------------------------------------------------------------------\n\nconst BUBBLED_EVENTS = [\n 'stream:text',\n 'stream:thinking',\n 'stream:end',\n 'stream:error',\n '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. Gate hooks (`block` / `result`) and `tool:transform`\n// (`result` rewrite) need this so a parent's per-edit annotation /\n// approval lands on the child's wire-level tool result.\nconst BUBBLED_MUTABLE_EVENTS = [\n 'tool:gate',\n 'mcp:tool:gate',\n 'tool:transform',\n] as const\n\ntype BubbledEvent = typeof BUBBLED_EVENTS[number]\ntype BubbledMutableEvent = typeof BUBBLED_MUTABLE_EVENTS[number]\n\n// Mapping from a bubbled event to its corresponding `child:*` event. Kept as\n// an explicit table (rather than a computed `child:${evt}` template literal)\n// so AgentHooks can type each entry independently.\nconst CHILD_EVENT_NAME: Record<BubbledEvent, keyof AgentHooks> = {\n 'stream:text': 'child:stream:text',\n 'stream:thinking': 'child:stream:thinking',\n 'stream:end': 'child:stream:end',\n 'stream:error': 'child:stream:error',\n '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 'tool:gate': 'child:tool:gate',\n 'mcp:tool:gate': 'child:mcp:tool:gate',\n 'tool:transform': 'child:tool:transform',\n}\n\n// ---------------------------------------------------------------------------\n// Session-scoped child label counter\n//\n// The `child-N` label that surfaces in transcripts (and in the spawn tool's\n// return string) is allocated per-session — *seeded* from the persisted\n// run tree — rather than from a closure-local counter on the spawn-tool\n// instance. The reason: closure counters don't survive process restarts,\n// so a fresh CLI launch that reopens a session with two prior children\n// (`child-1`, `child-2`) would start its next spawn at `child-1` and\n// collide with the persisted labels. Anchoring on `session.runs.filter(\n// depth > 0).length` makes the live counter pick up exactly where the\n// reloaded transcript's labels left off — matching `eventsFromTurns`'s\n// chronological numbering on the read side.\n//\n// The WeakMap caches the counter for the lifetime of the `Session` object\n// after the first call so the read of `session.runs.length` only happens\n// once per session, even though spawns mutate `session.runs` mid-run.\n//\n// Sessionless agents (no `ctx.session`) fall back to `localCounter` — the\n// closure is the best we can do without a shared anchor.\n// ---------------------------------------------------------------------------\n\nconst sessionChildCounters = new WeakMap<Session, number>()\n\nfunction reserveChildLabel(session: Session): string {\n let counter = sessionChildCounters.get(session)\n if (counter === undefined)\n counter = session.runs.filter(r => (r.depth ?? 0) > 0).length\n counter += 1\n sessionChildCounters.set(session, counter)\n return `child-${counter}`\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nfunction extractText(message: unknown): string {\n if (!message || typeof message !== 'object')\n return ''\n\n const msg = message as Record<string, unknown>\n\n if (typeof msg.content === 'string')\n return msg.content\n\n if (Array.isArray(msg.content)) {\n return msg.content\n .filter((block): block is Record<string, unknown> => !!block && typeof block === 'object' && (block as Record<string, unknown>).type === 'text')\n .map(block => block.text)\n .join('\\n')\n }\n\n return ''\n}\n\n/**\n * Read-only tool whitelist applied when a subagent preset has\n * `readonly: true` and no explicit `tools` list. Intentionally narrow —\n * only obviously non-mutating built-ins. Hosts wanting a different\n * read-only profile (e.g. include `glob` for a code-search agent) should\n * pass an explicit `tools` array on the subagent def.\n */\nconst READONLY_TOOL_DEFAULTS = ['read_file', 'grep', 'glob', 'list_files'] as const\n\n/**\n * Apply a subagent preset's tool filter to the parent's tool registry.\n * Always a strict subset of the input — the child agent never gains\n * tools the parent doesn't have. Names that don't match a parent tool\n * are silently dropped (matches MCP's lenient `enabledTools` behavior).\n *\n * Precedence: `def.tools` (explicit list) > `def.readonly: true`\n * (built-in read-only set) > unfiltered.\n */\nfunction filterToolsForSubagent(\n parentTools: Record<string, ToolDef> | undefined,\n def: SubagentDef,\n): Record<string, ToolDef> | undefined {\n if (!parentTools)\n return parentTools\n const explicit = def.tools\n // Match on `tool.spec.name` rather than the parent registry's object\n // key — registry keys are an implementation detail of the host\n // (`basicTools = { readFile, listFiles, ... }` uses camelCase JS\n // keys), but `spec.name` (`'read_file'`, `'list_files'`) is the\n // stable, documented, model-visible identifier hosts reason about.\n if (explicit && explicit.length > 0) {\n const wanted = new Set(explicit)\n const filtered: Record<string, ToolDef> = {}\n for (const [registryKey, t] of Object.entries(parentTools)) {\n if (wanted.has(t.spec.name))\n filtered[registryKey] = t\n }\n return filtered\n }\n if (def.readonly) {\n const wanted = new Set<string>(READONLY_TOOL_DEFAULTS)\n const filtered: Record<string, ToolDef> = {}\n for (const [registryKey, t] of Object.entries(parentTools)) {\n if (wanted.has(t.spec.name))\n filtered[registryKey] = t\n }\n return filtered\n }\n return parentTools\n}\n\n/**\n * Render the per-type descriptions into a single schema-field\n * description string the model reads when choosing a `subagent_type`.\n * Falls back to a generic line when the host didn't supply any per-type\n * descriptions.\n */\nfunction buildSubagentTypeDescription(registry: SubagentRegistry): string {\n const lines: string[] = []\n let hasAny = false\n for (const [key, def] of Object.entries(registry)) {\n if (def.description) {\n lines.push(`- \"${key}\": ${def.description}`)\n hasAny = true\n }\n else {\n lines.push(`- \"${key}\"`)\n }\n }\n lines.push('- \"general-purpose\": no specialization; uses the spawn tool\\'s default config.')\n if (!hasAny) {\n return `Optional subagent preset. One of: ${lines.map(l => l.replace(/^- /, '').replace(/:.*$/, '')).join(', ')}.`\n }\n return `Optional subagent preset that overlays the spawn tool's defaults.\\n${lines.join('\\n')}`\n}\n\n/**\n * Race `task` (an already-running child `agent.run()` promise) against a\n * timer. Does NOT race against the parent abort signal — the child agent\n * already observes the same signal internally and handles its own aborted\n * bookkeeping, so racing here would detach the spawn from the child's\n * session-persisting finally block.\n *\n * On timeout: rejects with `SpawnTimeoutError`; caller is expected to call\n * `agent.abort()` and subsequently `await` the original `task` so the\n * child's session state (runs, turns, status) gets flushed before the\n * parent moves on.\n */\nasync function raceWithTimeout<T>(\n task: Promise<T>,\n timeoutMs: number | undefined,\n): Promise<T> {\n if (!timeoutMs || timeoutMs <= 0)\n return task\n\n let timer: ReturnType<typeof setTimeout> | undefined\n try {\n return await new Promise<T>((resolve, reject) => {\n timer = setTimeout(() => reject(new SpawnTimeoutError(timeoutMs)), timeoutMs)\n task.then(resolve, reject)\n })\n }\n finally {\n if (timer)\n clearTimeout(timer)\n }\n}\n\nclass SpawnTimeoutError extends Error {\n readonly timeoutMs: number\n constructor(timeoutMs: number) {\n super(`Child agent timed out after ${timeoutMs}ms`)\n this.name = 'SpawnTimeoutError'\n this.timeoutMs = timeoutMs\n }\n}\n\n/**\n * Wire child's hooks to bubble into `parentHooks` as `child:*` events.\n *\n * Three kinds of forwarding:\n *\n * 1. **Originating observational events** (`stream:text`, `tool:before`, …)\n * → rewrite to the matching `child:*` event, inject `{ childId, depth }`,\n * fire on the parent's hook bus as a **spread copy** (so parent listeners\n * can't accidentally mutate the child's ctx).\n * 2. **Originating gate events** (`tool:gate`, `mcp:tool:gate`) → forward\n * the **same ctx reference**, augmented with `childId` / `depth`, so a\n * parent listener writing `ctx.block = true` lands on the gate the\n * child's loop is awaiting on. The bubble itself `await`s the parent's\n * callHook so any async approval (e.g. a TUI picker) completes before\n * the child's loop sees the decision.\n * 3. **Re-bubbled `child:*` events** from a grandchild already carry the\n * originating `childId` + `depth`. Forward verbatim so a top-level\n * listener sees true ancestry, not the immediate parent's.\n *\n * Returns a function that unregisters every listener registered here.\n * Called before `agent.run()` starts, torn down in a finally block — so\n * nothing leaks even if the child throws mid-run.\n */\n/**\n * Surface a thrown observational-bubble listener without killing the\n * process. Observational events (`child:stream:text`, `child:tool:after`,\n * `child:turn:after`, …) are fire-and-forget — a rejected promise here\n * has no caller. Without this handler, a buggy parent listener bubbles\n * up to an unhandled-rejection, and Node/Bun under\n * `--unhandled-rejections=strict` terminates the host.\n *\n * Gated on `ZIDANE_DEBUG` so production logs stay quiet; debug builds\n * still get the tagged line.\n */\nfunction swallowBubbleError(eventName: string, err: unknown): void {\n if (!process.env.ZIDANE_DEBUG)\n return\n const message = err instanceof Error ? (err.stack ?? err.message) : String(err)\n process.stderr.write(`[zidane/spawn] parent listener for \"${eventName}\" rejected: ${message}\\n`)\n}\n\nfunction bubbleHooks(\n childHooks: Hookable<AgentHooks>,\n parentHooks: Hookable<AgentHooks>,\n childId: string,\n depth: number,\n): () => void {\n const unregisters: Array<() => void> = []\n\n // Cast the callHook surface once — it accepts any (name, ctx) at runtime\n // but the typed overload is too narrow to satisfy across the map.\n const fire = parentHooks.callHook as unknown as (\n name: keyof AgentHooks,\n ctx: Record<string, unknown>,\n ) => Promise<unknown>\n\n // Observational events — fire-and-forget so a slow parent listener never\n // backpressures child emission (the child has already moved on by then).\n // `Promise.resolve(...).catch(swallowBubbleError)` instead of `void` so a\n // rejected listener surfaces as a tagged stderr line rather than an\n // unhandled rejection — which would terminate the process under\n // `--unhandled-rejections=strict`. `Promise.resolve` because hookable's\n // `callHook` returns `undefined` when no listeners are registered (rather\n // than a resolved promise), and we still want a uniform `.catch` surface.\n for (const evt of BUBBLED_EVENTS) {\n const parentEvt = CHILD_EVENT_NAME[evt]\n const unregister = childHooks.hook(evt, (ctx: object) => {\n Promise.resolve(fire(parentEvt, { ...(ctx as Record<string, unknown>), childId, depth }))\n .catch(err => swallowBubbleError(parentEvt, err))\n })\n unregisters.push(unregister)\n }\n\n // Same-ref bubble — `await` so the parent's decision lands before the\n // child's loop reads back the mutable slot (`ctx.block` / `ctx.result`\n // for gates, `ctx.result` for `tool:transform`). The bubble only adds\n // `childId` + `depth` for routing; the ctx reference itself is the\n // one the child's loop is awaiting on.\n const tagOnCtx = (ctx: Record<string, unknown>) => {\n ctx.childId = childId\n ctx.depth = depth\n }\n for (const evt of BUBBLED_MUTABLE_EVENTS) {\n const parentEvt = CHILD_MUTABLE_EVENT_NAME[evt]\n const unregister = childHooks.hook(evt, async (ctx: object) => {\n tagOnCtx(ctx as Record<string, unknown>)\n await fire(parentEvt, ctx as Record<string, unknown>)\n })\n unregisters.push(unregister)\n }\n\n // Chain bubbling: forward already-tagged `child:*` events from a grandchild\n // through to the parent, preserving the originating spawn's `childId` +\n // `depth`. Without this, only direct children would surface.\n const chainHook = childHooks.hook as unknown as (\n name: keyof AgentHooks,\n handler: (ctx: object) => void | Promise<void>,\n ) => () => void\n for (const evt of BUBBLED_EVENTS) {\n const parentEvt = CHILD_EVENT_NAME[evt]\n unregisters.push(chainHook(parentEvt, (ctx) => {\n Promise.resolve(fire(parentEvt, ctx as Record<string, unknown>))\n .catch(err => swallowBubbleError(parentEvt, err))\n }))\n }\n for (const evt of BUBBLED_MUTABLE_EVENTS) {\n const parentEvt = CHILD_MUTABLE_EVENT_NAME[evt]\n unregisters.push(chainHook(parentEvt, async (ctx) => {\n // Same-ref forwards need the await chain to remain serial up to\n // the top-level listener — otherwise the deepest child's loop\n // resumes before the root's decision / result mutation lands.\n await fire(parentEvt, ctx as Record<string, unknown>)\n }))\n }\n\n return () => {\n for (const u of unregisters) u()\n }\n}\n\n// ---------------------------------------------------------------------------\n// Factory\n// ---------------------------------------------------------------------------\n\nexport interface SpawnToolOptions {\n /** Maximum concurrent sub-agents (default: 3). */\n maxConcurrent?: number\n /**\n * Maximum subagent depth. 0 disables spawning entirely; 1 allows top-level\n * spawns but forbids grandchildren; 3 (default) allows three levels of\n * recursion — enough for most orchestration patterns, a sharp ceiling\n * against runaway loops.\n */\n maxDepth?: number\n /** Child model override. */\n model?: string\n /** Child system prompt override. Per-spawn `input.system` takes precedence. */\n system?: string\n /** Child thinking level. */\n thinking?: 'off' | 'minimal' | 'low' | 'medium' | 'high'\n /** Preset override for children. Shallow-merged over the parent's preset (parent fields still win for anything left unset). */\n preset?: Preset\n /**\n * Per-child timeout, in milliseconds. When the child exceeds it the spawn\n * tool returns a timeout marker, fires `spawn:error`, and destroys the\n * child agent. Default: none.\n */\n timeoutMs?: number\n /**\n * When `true` and the parent has a session, the child reuses the parent's\n * session — child turns are appended with the child's own `runId`, and the\n * resulting `SessionRun` carries `parentRunId` so the tree is\n * reconstructible. Default: `false` (child is in-memory only).\n *\n * **Read-state isolation.** Sharing the session also shares the\n * `read_file` / `requireReadBeforeEdit` tracking map (it's keyed\n * by `Session`). With `persist: false` the child gets no session,\n * so reads inside the subagent populate nothing the parent can see —\n * a follow-up `edit` / `multi_edit` in the parent will trip the\n * gate with `\"has not been read\"` even though the model just\n * read the file in the child. Use {@link shareReadState} when\n * you want the parent's gate to honor the child's reads WITHOUT\n * also persisting child turns to the parent's session.\n */\n persist?: boolean\n /**\n * Forward the parent's read-state map to the child agent so the\n * `requireReadBeforeEdit` gate and `dedupReads` cache see reads\n * across the parent/child boundary. Orthogonal to {@link persist} —\n * use this when you want shared read tracking without sharing the\n * session's turn history. Default: `false`.\n *\n * Has no effect when the parent has no read-state to share (no\n * session and no explicit `readState` on the parent agent's\n * options). Implementation: passes the parent's resolved\n * `ReadStateMap` to the child via `AgentOptions.readState`, which\n * tools resolve via `ctx.readState ?? getReadState(ctx.session)`.\n */\n shareReadState?: boolean\n /**\n * Forward a curated subset of child hook events (`stream:*`, `tool:*`,\n * `turn:after`) onto the parent's hook bus as `child:*` events. Default:\n * `true`. Grandchildren bubble through their child transparently.\n */\n forwardHooks?: boolean\n /** Called when a child agent starts. */\n onSpawn?: (child: ChildAgent) => void\n /** Called when a child agent completes (success, abort, timeout, or error). */\n onComplete?: (child: ChildAgent, stats: AgentStats, status: NonNullable<ChildRunStats['status']>) => void\n /**\n * Named subagent presets the model can select via the `subagent_type`\n * input field. Mirrors the Claude Code SDK's surface — models trained\n * on it routinely emit `subagent_type: 'Explore' | 'Plan' |\n * 'Verification' | 'general-purpose'`, and without a registry the\n * field is silently dropped, so hosts wanting type-specialized\n * subagents have to invent their own dispatch layer.\n *\n * Each entry overlays the base spawn config for that particular\n * dispatch. Per-call `input.system` still wins over `subagents[type].system`\n * so the model can always specialize further.\n *\n * When the registry is non-empty:\n * - `subagent_type` appears in the spawn input schema as a `string`\n * enum of the registered keys (plus the always-available\n * `'general-purpose'` fallback).\n * - Models that pass an unregistered type are routed to\n * `'general-purpose'` (no error — degrade gracefully so trained\n * models keep working even on hosts that haven't wired every\n * type Claude Code uses).\n *\n * When the registry is empty / unset, the field is omitted entirely\n * (preserves the historical schema for hosts that never use this).\n *\n * Default: `undefined` (no subagent types; `subagent_type` schema\n * field hidden).\n */\n subagents?: SubagentRegistry\n}\n\n/**\n * Per-type subagent override applied when the model calls\n * `spawn({ subagent_type: '…' })`. All fields are optional; absent\n * fields fall back to the parent's resolved configuration (see\n * {@link SpawnToolOptions} comments for the merge order).\n */\nexport interface SubagentDef {\n /**\n * System prompt override for this subagent type. Per-call\n * `input.system` still wins — model-supplied specialization beats\n * preset defaults.\n */\n system?: string\n /**\n * Restrict the child agent's tool registry to this list of canonical\n * tool names. Operates as a filter over the parent's tools (the\n * parent's selection is the upper bound — a subagent can never gain\n * tools the parent doesn't have). When unset, the child inherits the\n * parent's full tool list.\n *\n * Tool names are canonical (registry-key) not wire/alias names — the\n * filter runs before aliases are applied. Names that don't match a\n * parent tool are silently dropped (matches the lenient behaviour of\n * `enabledTools` on MCP configs).\n */\n tools?: readonly string[]\n /**\n * Mark this subagent as read-only — equivalent to listing only\n * obviously-non-mutating tools (`read_file`, `grep`, `glob`,\n * `list_files`) in {@link SubagentDef.tools}. Convenience for the\n * common \"Plan\" / \"Explore\" subagent shape Claude Code ships.\n *\n * When both `readonly: true` and `tools` are set, `tools` wins\n * (explicit beats implicit).\n */\n readonly?: boolean\n /**\n * Short description rendered into the spawn tool's schema (the\n * `subagent_type` field's description) so the model can pick a type\n * that matches the task without round-tripping through docs.\n */\n description?: string\n}\n\n/**\n * Map of subagent-type key → preset. Keys are case-sensitive and\n * appear verbatim in the spawn input schema's enum so the model emits\n * them with the same casing. Common conventions: `'Explore'`,\n * `'Plan'`, `'Verification'`, `'general-purpose'` (Claude Code SDK's\n * built-in set).\n */\nexport type SubagentRegistry = Record<string, SubagentDef>\n\n/**\n * Create a configured spawn tool.\n *\n * State (`children`, `totalChildStats`, counters, active count) is scoped to\n * the returned instance. Multiple parent agents using the same instance will\n * share counters + stats + concurrency slots — call `createSpawnTool()` per\n * agent (or use the stateless default `spawn`) to keep them isolated.\n */\nexport function createSpawnTool(options: SpawnToolOptions = {}): ToolDef & SpawnToolState {\n const localChildren = new Map<string, ChildAgent>()\n let localCounter = 0\n let localActiveCount = 0\n const maxConcurrent = options.maxConcurrent ?? 3\n const maxDepth = options.maxDepth ?? 3\n const forwardHooks = options.forwardHooks ?? true\n\n const localStats: AgentStats = {\n totalIn: 0,\n totalOut: 0,\n totalCacheRead: 0,\n totalCacheCreation: 0,\n turns: 0,\n elapsed: 0,\n }\n\n // Pre-compute the subagent_type schema piece once. Including the\n // enum keys (plus the canonical `general-purpose` fallback) saves\n // the host from re-typing them in their preset description. Empty\n // registry — or one passed as `{}` with no entries — drops the field\n // entirely, preserving the historical schema. We don't surface\n // `subagent_type` with only `general-purpose` in the enum (a\n // single-value enum is noise to the model with no decision to make).\n const subagentRegistry = options.subagents\n const hasSubagentEntries = !!subagentRegistry && Object.keys(subagentRegistry).length > 0\n const subagentTypeKeys = hasSubagentEntries\n ? [...new Set([...Object.keys(subagentRegistry!), 'general-purpose'])]\n : []\n const subagentTypeProperty = subagentTypeKeys.length > 0\n ? {\n subagent_type: {\n type: 'string' as const,\n enum: subagentTypeKeys,\n description: buildSubagentTypeDescription(subagentRegistry!),\n },\n }\n : {}\n\n return {\n get children() { return localChildren },\n get totalChildStats() { return { ...localStats } },\n\n // Sub-agents fan out by design — multiple spawn calls in a single\n // batch run in parallel (each child has its own context window,\n // its own retry budget, and its own session linkage). The internal\n // `maxConcurrent` here is the spawn-tool-level cap on simultaneous\n // children; `behavior.maxConcurrentTools` is the loop-level cap on\n // all concurrent tools regardless of kind.\n isConcurrencySafe: true,\n\n spec: {\n name: 'spawn',\n description: 'Spawn a sub-agent for a self-contained task that benefits from isolation (separate context window, separate retries) — for example, a deep research dive or a long codegen pass on a specific file. The sub-agent runs independently with its own tool access and returns its final response. Do NOT spawn for sequential steps you could do yourself.',\n inputSchema: {\n type: 'object',\n properties: {\n task: {\n type: 'string',\n description: 'The task prompt for the sub-agent. Be specific about what you want it to accomplish.',\n },\n system: {\n type: 'string',\n description: 'Optional system prompt override for this specific sub-agent.',\n },\n ...subagentTypeProperty,\n },\n required: ['task'],\n },\n },\n\n async execute(input: Record<string, unknown>, ctx: ToolContext): Promise<string> {\n const task = input.task as string\n const systemOverride = input.system as string | undefined\n const requestedSubagentType = typeof input.subagent_type === 'string'\n ? input.subagent_type\n : undefined\n // Resolve the subagent preset. Unknown types degrade to\n // `general-purpose` (i.e. no overlay) so Claude-Code-trained\n // models that emit `subagent_type: 'Verification'` against hosts\n // that never wired that type don't get silently broken — the\n // call still runs, just with the parent's default config.\n const subagentDef = requestedSubagentType && subagentRegistry\n ? (subagentRegistry[requestedSubagentType] ?? undefined)\n : undefined\n const parentDepth = ctx.depth ?? 0\n const childDepth = parentDepth + 1\n\n // Reject before any await so the parent-level LLM sees the failure\n // immediately, and the concurrency slot below is reserved atomically\n // with the cap check.\n if (childDepth > maxDepth) {\n return `Cannot spawn: maxDepth=${maxDepth} reached (parent depth=${parentDepth}). Deepen the cap with createSpawnTool({ maxDepth }).`\n }\n\n if (localActiveCount >= maxConcurrent) {\n return `Cannot spawn: ${localActiveCount}/${maxConcurrent} sub-agents already running. Wait for one to complete.`\n }\n\n // Signal short-circuit: a pre-aborted parent means the child would\n // immediately no-op on its first `signal.aborted` check. Skip the\n // agent/handle/session setup cost entirely.\n if (ctx.signal.aborted) {\n return `[sub-agent pre-aborted] Parent signal was already aborted — skipped \"${task.slice(0, 80)}\"`\n }\n\n // Reserve the slot + id SYNCHRONOUSLY — this block must not contain\n // any `await`, so (a) two parallel spawns can't both pass the cap\n // check, and (b) the session-scoped child counter (see\n // `reserveChildLabel`) sees a consistent `session.runs` snapshot\n // between concurrent spawns sharing this session.\n const id = ctx.session\n ? reserveChildLabel(ctx.session)\n : `child-${++localCounter}`\n localActiveCount++\n const child: ChildAgent = { id, task, startedAt: Date.now(), depth: childDepth }\n localChildren.set(id, child)\n\n let destroyError: Error | undefined\n let childRunStatus: NonNullable<ChildRunStats['status']> = 'completed'\n let finalStats: AgentStats | undefined\n let result = ''\n\n // Bubble child hook events to parent if requested. Wired BEFORE\n // `agent.run()` so even the very first `turn:before` is observable.\n let unbubble: (() => void) | undefined\n\n try {\n // Apply the subagent-type tool filter on top of the parent's\n // tool registry. `tools` wins over `readonly` when both are\n // set (explicit beats implicit). The filter is a strict subset\n // — a subagent can never gain tools the parent doesn't have.\n const filteredTools = subagentDef\n ? filterToolsForSubagent(ctx.tools, subagentDef)\n : ctx.tools\n\n const parentPreset: Preset = {\n ...(ctx.name !== undefined ? { name: ctx.name } : {}),\n ...(ctx.system !== undefined ? { system: ctx.system } : {}),\n tools: filteredTools,\n ...(ctx.toolAliases !== undefined ? { toolAliases: ctx.toolAliases } : {}),\n ...(ctx.mcpServers !== undefined ? { mcpServers: ctx.mcpServers } : {}),\n ...(ctx.skills !== undefined ? { skills: ctx.skills } : {}),\n ...(ctx.behavior !== undefined ? { behavior: ctx.behavior } : {}),\n }\n // Resolve the parent's read-state map for cross-context\n // sharing. `shareReadState: true` forwards it to the child via\n // `AgentOptions.readState`, which tools resolve through\n // `resolveReadStateMap(ctx)`. Skips when the parent has\n // nothing to share (no session, no explicit map). Orthogonal\n // to `persist` — turn-history and read-state are independent.\n // Reading via `resolveReadStateMap` here ensures we forward\n // the same map a grandparent might already have forwarded\n // into us (chained `shareReadState` across the tree).\n const sharedReadState = options.shareReadState\n ? resolveReadStateMap(ctx)\n : undefined\n const agent = createAgent({\n ...parentPreset,\n ...options.preset,\n provider: ctx.provider,\n execution: ctx.execution,\n // Share the parent's session on opt-in. Child turns get appended to\n // the same session.turns stream with the child's runId; the child\n // run itself is tagged with parentRunId below, via AgentRunOptions.\n ...(options.persist && ctx.session ? { session: ctx.session } : {}),\n ...(sharedReadState ? { readState: sharedReadState } : {}),\n })\n\n if (forwardHooks) {\n // Enrich `tool:before` ctx with `priorContent` for the three\n // edit tools BEFORE bubbleHooks fires the parent's\n // `child:tool:before`. Hookable runs listeners serially in\n // registration order, so registering this enricher first\n // guarantees the bubble's ctx-spread captures the field.\n // Hosts that listen on `child:tool:before` (the TUI's\n // edit-diff feature) read `priorContent` to:\n // - render a true `old → new` diff for `write_file` instead\n // of an all-add view, and\n // - paint real-file line numbers in the gutter / compact\n // summary for all three edit tools (via the renderer's\n // `buildContextualDiff` path).\n //\n // Note: `tool:before` is awaited by the child's loop, so this\n // pre-read blocks the child's tool execution by the read\n // latency. Acceptable — edit calls are cheap relative to the\n // model round-trip, and the read is skipped entirely for\n // non-edit tools.\n const unregisterEnricher = agent.hooks.hook('tool:before', async (toolCtx) => {\n if (toolCtx.name !== 'write_file' && toolCtx.name !== 'edit' && toolCtx.name !== 'multi_edit')\n return\n if (!agent.handle)\n return\n const inputPath = toolCtx.input?.path\n if (typeof inputPath !== 'string')\n return\n try {\n toolCtx.priorContent = await agent.execution.readFile(agent.handle, inputPath)\n }\n catch {\n // File doesn't exist (fresh create for `write_file`, or\n // a model-side mistake for `edit` / `multi_edit`) — leave\n // priorContent unset; downstream renderers treat the\n // absence as \"no real-file positions available\" and fall\n // back to synthetic line numbers.\n }\n })\n const unbubbleInner = bubbleHooks(agent.hooks, ctx.hooks, id, childDepth)\n unbubble = () => {\n unregisterEnricher()\n unbubbleInner()\n }\n }\n\n options.onSpawn?.(child)\n // Mutable tracingContext carrier — empty by default; parent tracer\n // listeners on `spawn:before` write a W3C `traceparent` (plus\n // optional `tracestate`) into it. We forward whatever it contains\n // to the child's `agent.run()` so the child tracer can re-parent\n // its root span. Empty / absent for parents that don't run a\n // tracer (no-op, no key gets set, child sees `undefined`).\n const spawnHookCtx: { id: string, task: string, depth: number, tracingContext: Record<string, string> } = {\n id,\n task,\n depth: childDepth,\n tracingContext: {},\n }\n await ctx.hooks.callHook('spawn:before', spawnHookCtx)\n\n // `agent.run()` is started here and we hold onto its promise so we\n // can always `await` it before destroying. Even on a timeout we\n // re-await the run — that lets the child's internal `finally`\n // (session.abortRun, finalizeSession, hooks) flush its state to the\n // store before the parent hands the outcome back to the LLM.\n const propagatedTracing = Object.keys(spawnHookCtx.tracingContext).length > 0\n ? Object.freeze({ ...spawnHookCtx.tracingContext })\n : undefined\n // System prompt precedence (highest first):\n // 1. per-call `input.system` — model-supplied specialization.\n // 2. `subagents[type].system` — host preset for this type.\n // 3. `options.system` — spawn-tool default.\n // The previous logic skipped (2). Subagent-type system\n // overrides matter for Plan/Explore-style presets where the\n // type IS the contract (e.g. \"you are a research subagent…\").\n const effectiveSystem\n = systemOverride\n ?? subagentDef?.system\n ?? options.system\n const runPromise = agent.run({\n prompt: task,\n model: options.model,\n system: effectiveSystem,\n thinking: options.thinking,\n signal: ctx.signal,\n depth: childDepth,\n ...(options.persist && ctx.runId ? { parentRunId: ctx.runId } : {}),\n ...(propagatedTracing ? { tracingContext: propagatedTracing } : {}),\n })\n\n try {\n finalStats = await raceWithTimeout(runPromise, options.timeoutMs)\n\n // Tree-wide turn count keeps the model-facing line internally\n // consistent with the cumulative token numbers — otherwise a\n // 2-turn child that spawned a 50-turn grandchild would print\n // \"2 turns ... 5000 tokens\" and look broken. Equals\n // `finalStats.turns` when the child has no descendants of its own.\n const treeTurns = flattenTurns(finalStats).length\n // `formatTokenUsage` reports the effective input (including\n // cached reads / cache creations) with the cached portion broken\n // out — see the helper's docstring.\n const usage = formatTokenUsage(finalStats)\n if (ctx.signal.aborted) {\n childRunStatus = 'aborted'\n result = [\n `[sub-agent ${id}] Aborted after ${treeTurns} turns (${finalStats.elapsed}ms)`,\n `Tokens: ${usage}`,\n ].join('\\n')\n }\n else {\n const response = extractText(agent.turns.at(-1))\n result = [\n `[sub-agent ${id}] Completed in ${treeTurns} turns (${finalStats.elapsed}ms)`,\n `Tokens: ${usage}`,\n '',\n response || '(no text response)',\n ].join('\\n')\n }\n }\n catch (err) {\n if (err instanceof SpawnTimeoutError) {\n childRunStatus = 'timeout'\n agent.abort() // triggers child's internal abort path\n // Await the run so its finally block persists aborted state\n // before we move on. Swallow any error from the awaited promise\n // — we've already classified the outcome as 'timeout'.\n try {\n finalStats = await runPromise\n }\n catch {\n finalStats = {\n totalIn: 0,\n totalOut: 0,\n totalCacheRead: 0,\n totalCacheCreation: 0,\n turns: 0,\n elapsed: err.timeoutMs,\n }\n }\n result = `[sub-agent ${id}] Timed out after ${err.timeoutMs}ms`\n }\n else {\n const error = err instanceof Error ? err : new Error(String(err))\n childRunStatus = 'error'\n finalStats = {\n totalIn: 0,\n totalOut: 0,\n totalCacheRead: 0,\n totalCacheCreation: 0,\n turns: 0,\n elapsed: 0,\n }\n result = `[sub-agent ${id}] Error: ${error.message}`\n await ctx.hooks.callHook('spawn:error', { id, task, depth: childDepth, error })\n }\n }\n finally {\n // Promote the subagent's still-running background tasks up\n // to the parent's handle BEFORE destroying the child.\n // Without this, `agent.destroy()` would SIGTERM every task\n // the subagent started (per-handle scoping is what makes\n // sibling subagents isolated, but it also kills `&`-shell\n // semantics where backgrounded work outlives the spawning\n // process). With it, the user can `ctrl+k` a long-running\n // task the subagent started even after the subagent returns,\n // and `killBackgroundTask` from the parent agent actually\n // finds it.\n //\n // The reassignment also REPLACES the task's `onExit`\n // callback — the original closed over the child's hook bus,\n // which is about to be torn down. The new callback fires\n // `background:exit` on the PARENT's bus so the TUI's\n // existing listener drains the entry when the task\n // eventually terminates.\n //\n // Optional in the contract — sandboxes that don't expose\n // `reassignBackgroundTasks` fall through to the previous\n // behavior (destroy kills the subagent's tasks). Same for\n // a child agent that minted no execution handle (no runs\n // ever executed).\n const childHandle = agent.handle\n if (childHandle && ctx.execution.reassignBackgroundTasks) {\n try {\n const reassigned = await ctx.execution.reassignBackgroundTasks(\n childHandle,\n ctx.handle,\n (info) => {\n void ctx.hooks.callHook('background:exit', info)\n },\n )\n for (const entry of reassigned) {\n await ctx.hooks.callHook('background:reassign', {\n taskId: entry.taskId,\n fromHandleId: childHandle.id,\n toHandleId: ctx.handle.id,\n childId: id,\n pid: entry.pid,\n command: entry.command,\n outputPath: entry.outputPath,\n startedAt: entry.startedAt,\n })\n }\n }\n catch (err) {\n // Non-fatal — destroy will still kill the tasks. Log\n // under ZIDANE_DEBUG so a misconfigured sandbox surfaces\n // visibly. We don't promote this into `spawn:error`\n // because it doesn't affect the subagent's own outcome.\n if (process.env.ZIDANE_DEBUG)\n process.stderr.write(`[zidane/spawn] reassignBackgroundTasks failed: ${err instanceof Error ? err.message : String(err)}\\n`)\n }\n }\n\n // Always attempt to destroy. Capture any destroy error so it can be\n // surfaced via spawn:error without masking the primary outcome.\n try {\n await agent.destroy()\n }\n catch (err) {\n destroyError = err instanceof Error ? err : new Error(String(err))\n }\n }\n\n // Aggregate after run finalization so in-flight totalIn/Out/turns\n // counters are stable.\n if (finalStats) {\n localStats.totalIn += finalStats.totalIn\n localStats.totalOut += finalStats.totalOut\n localStats.totalCacheRead += finalStats.totalCacheRead\n localStats.totalCacheCreation += finalStats.totalCacheCreation\n localStats.turns += finalStats.turns\n // `elapsed` is a cumulative sum across children — documented as such.\n // Over-counts when children run in parallel; reflects \"total CPU-ish\n // time burned on children\" rather than wall-clock parallelism.\n localStats.elapsed += finalStats.elapsed\n }\n\n // Build ChildRunStats + emit completion hook. Emitted for every\n // terminal state (success/abort/timeout/error) so consumers can\n // reconcile local state without also listening on spawn:error.\n const childRunStats: ChildRunStats = {\n id,\n task,\n stats: finalStats!,\n depth: childDepth,\n status: childRunStatus,\n ...(finalStats!.output ? { output: finalStats!.output } : {}),\n }\n options.onComplete?.(child, finalStats!, childRunStatus)\n await ctx.hooks.callHook('spawn:complete', childRunStats)\n\n if (destroyError) {\n // Non-fatal but worth surfacing: a destroy failure leaves an\n // execution-handle / MCP connection in a weird state.\n await ctx.hooks.callHook('spawn:error', {\n id,\n task,\n depth: childDepth,\n error: destroyError,\n })\n }\n\n return result\n }\n finally {\n unbubble?.()\n localActiveCount--\n localChildren.delete(id)\n }\n },\n }\n}\n","import type { ToolContext, ToolDef } from './types'\nimport { Buffer } from 'node:buffer'\nimport { hashContent, readStateKey, resolveReadStateMap } from './read-state'\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 let existing: string | undefined\n try {\n existing = await ctx.execution.readFile(ctx.handle, targetPath)\n }\n catch {\n // File does not exist (or read failed for unrelated reasons — we'll let\n // writeFile surface that error). Either way: treat as a fresh write.\n }\n\n const bytes = Buffer.byteLength(targetContent)\n\n if (existing === targetContent)\n return `No change needed: ${targetPath} already at target state (${bytes} bytes).`\n\n await ctx.execution.writeFile(ctx.handle, targetPath, targetContent)\n\n // 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 return existing === undefined\n ? `Created ${targetPath} (${bytes} bytes).`\n : `Updated ${targetPath} (${bytes} bytes).`\n },\n}\n"],"mappings":"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AAG,EAAE,QAAQ,CAAC,EAAE,KAAK,CAAC,QAAQ,WAAW;EAAE;EAAQ,OAAO;CAAK,EAAE;CAEpE,OAAO;EACL;EACA;EACA,WAAW,SAAS,YAAY;EAChC,mBAAmB,SAAS,WAAW;CACzC;AACF;;;;;;;;;;;;;;;;;;;AAw9BA,SAAS,0BAA0B,MAA4B;CAK7D,MAAM,UAAU,kBAAkB,IAAI;CAatC,OAAO;EAXL;EACA,cAAc,UAAU,KAAK,MAAM,EAAE;EACrC,aAAa,KAAK,OAAO;EACzB,gBAAgB,KAAK,SAAS;EAC9B,GAAI,KAAK,SAAS,CAAC,aAAa,UAAU,KAAK,MAAM,EAAE,UAAU,IAAI,CAAC;EACtE,cAAc,UAAU,KAAK,OAAO,EAAE;EACtC,kBAAkB,UAAU,KAAK,UAAU,EAAE;EAC7C,kBAAkB,KAAK,WAAW;EAClC,cAAc,UAAU,OAAO,EAAE;EACjC;CAES,EAAE,KAAK,IAAI;AACxB;AA2FA,MAAM,iBAAsC,IAAI,IAAI;CAxFlD;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;AAGiE,CAAC;AAEpE,SAAS,iBAAiB,OAA0C;CAClE,OAAO,eAAe,IAAI,KAAK;AACjC;;;;;;;;;;;;;;;;;;;;;;;;;;AA0CA,eAAe,6BACb,OACA,iBACA,OACA,UACA,OACA,OACe;CACf,IAAI,MAAM,WAAW,GACnB;CACF,MAAM,OAAO,MAAM,MAAM,SAAS;CAClC,IAAI,KAAK,SAAS,aAChB;C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"}