zidane 6.5.5 → 6.5.7

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 (93) hide show
  1. package/dist/{acp-BPJbXS2J.js → acp-Dm95NwNM.js} +4 -4
  2. package/dist/{acp-BPJbXS2J.js.map → acp-Dm95NwNM.js.map} +1 -1
  3. package/dist/acp-cli.js +5 -5
  4. package/dist/acp.d.ts +1 -1
  5. package/dist/acp.js +1 -1
  6. package/dist/{agent-gZjHsUft.js → agent-CcTWL-Z6.js} +59 -5
  7. package/dist/agent-CcTWL-Z6.js.map +1 -0
  8. package/dist/agent.d.ts +1 -1
  9. package/dist/agent.js +1 -1
  10. package/dist/{anthropic-D3le2d-s.js → anthropic-BomWPIYw.js} +392 -30
  11. package/dist/anthropic-BomWPIYw.js.map +1 -0
  12. package/dist/{auth-DCiXCwGa.js → auth-C-Zx5zBo.js} +127 -35
  13. package/dist/auth-C-Zx5zBo.js.map +1 -0
  14. package/dist/chat/pure.d.ts +3 -3
  15. package/dist/chat.d.ts +91 -6
  16. package/dist/chat.d.ts.map +1 -1
  17. package/dist/chat.js +4 -4
  18. package/dist/contexts/daytona.d.ts +1 -1
  19. package/dist/contexts/e2b.d.ts +1 -1
  20. package/dist/eval.d.ts +1 -1
  21. package/dist/eval.js +1 -1
  22. package/dist/extensions.d.ts +1 -1
  23. package/dist/extensions.js +1 -1
  24. package/dist/headless.d.ts +1 -1
  25. package/dist/headless.js +3 -3
  26. package/dist/{index-BYKzzYzi.d.ts → index-ZWibV230.d.ts} +33 -7
  27. package/dist/index-ZWibV230.d.ts.map +1 -0
  28. package/dist/index.d.ts +1 -1
  29. package/dist/index.js +6 -6
  30. package/dist/mcp.d.ts +1 -1
  31. package/dist/{messages-CVVg5jUL.js → messages-CZMUuKzo.js} +2 -2
  32. package/dist/{messages-CVVg5jUL.js.map → messages-CZMUuKzo.js.map} +1 -1
  33. package/dist/{openai-compat-hmZzpDrB.js → openai-compat-BoTi-9fd.js} +8 -3
  34. package/dist/openai-compat-BoTi-9fd.js.map +1 -0
  35. package/dist/output/stream-json.d.ts +1 -1
  36. package/dist/output/terminal.d.ts +1 -1
  37. package/dist/plugins.d.ts +1 -1
  38. package/dist/presets.d.ts +1 -1
  39. package/dist/presets.js +1 -1
  40. package/dist/providers/anthropic.d.ts +1 -1
  41. package/dist/providers/anthropic.js +1 -1
  42. package/dist/providers/arcee.d.ts +1 -1
  43. package/dist/providers/arcee.js +1 -1
  44. package/dist/providers/baseten.d.ts +1 -1
  45. package/dist/providers/baseten.js +1 -1
  46. package/dist/providers/cerebras.d.ts +1 -1
  47. package/dist/providers/cerebras.js +2 -2
  48. package/dist/providers/cerebras.js.map +1 -1
  49. package/dist/providers/local.d.ts +1 -1
  50. package/dist/providers/local.js +1 -1
  51. package/dist/providers/openai-compat.d.ts +1 -1
  52. package/dist/providers/openai-compat.js +1 -1
  53. package/dist/providers/openai.d.ts +1 -1
  54. package/dist/providers/openai.js +2 -2
  55. package/dist/providers/openrouter.d.ts +1 -1
  56. package/dist/providers/openrouter.js +1 -1
  57. package/dist/providers/xai.d.ts +1 -1
  58. package/dist/providers/xai.js +1 -1
  59. package/dist/{providers-Cf0AXIIZ.js → providers-CmhdtD7w.js} +2 -2
  60. package/dist/{providers-Cf0AXIIZ.js.map → providers-CmhdtD7w.js.map} +1 -1
  61. package/dist/providers.d.ts +1 -1
  62. package/dist/providers.js +3 -3
  63. package/dist/restate.d.ts +1 -1
  64. package/dist/restate.js +2 -2
  65. package/dist/session/sqlite.d.ts +1 -1
  66. package/dist/{session-cqN5NbO-.js → session-CyRLSsVa.js} +2 -2
  67. package/dist/{session-cqN5NbO-.js.map → session-CyRLSsVa.js.map} +1 -1
  68. package/dist/session.d.ts +1 -1
  69. package/dist/session.js +2 -2
  70. package/dist/skills.d.ts +1 -1
  71. package/dist/{tool-formatters-B5WSHMCU.d.ts → tool-formatters-D3wSS2zz.d.ts} +2 -2
  72. package/dist/{tool-formatters-B5WSHMCU.d.ts.map → tool-formatters-D3wSS2zz.d.ts.map} +1 -1
  73. package/dist/tools/fetch-url.d.ts +1 -1
  74. package/dist/tools/web-search.d.ts +1 -1
  75. package/dist/tools.d.ts +1 -1
  76. package/dist/tools.js +1 -1
  77. package/dist/{transcript-anchors-BvC8t7dt.js → transcript-anchors-DGUqw92H.js} +3 -3
  78. package/dist/{transcript-anchors-BvC8t7dt.js.map → transcript-anchors-DGUqw92H.js.map} +1 -1
  79. package/dist/{transcript-anchors-BPnWHLyr.d.ts → transcript-anchors-llwMrLRG.d.ts} +2 -2
  80. package/dist/{transcript-anchors-BPnWHLyr.d.ts.map → transcript-anchors-llwMrLRG.d.ts.map} +1 -1
  81. package/dist/tui.d.ts +9 -3
  82. package/dist/tui.d.ts.map +1 -1
  83. package/dist/tui.js +232 -13
  84. package/dist/tui.js.map +1 -1
  85. package/dist/{turn-operations-BvP3_HsU.d.ts → turn-operations-C_XvfBU3.d.ts} +3 -3
  86. package/dist/{turn-operations-BvP3_HsU.d.ts.map → turn-operations-C_XvfBU3.d.ts.map} +1 -1
  87. package/dist/types.d.ts +1 -1
  88. package/package.json +2 -2
  89. package/dist/agent-gZjHsUft.js.map +0 -1
  90. package/dist/anthropic-D3le2d-s.js.map +0 -1
  91. package/dist/auth-DCiXCwGa.js.map +0 -1
  92. package/dist/index-BYKzzYzi.d.ts.map +0 -1
  93. package/dist/openai-compat-hmZzpDrB.js.map +0 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"openai-compat-BoTi-9fd.js","names":[],"sources":["../src/hash.ts","../src/aliasing.ts","../src/prompt.ts","../src/providers/model-catalog.ts","../src/providers/cost.ts","../src/providers/prompt-cache-key.ts","../src/providers/schema-sanitize.ts","../src/providers/tool-args.ts","../src/providers/tool-name-boundary.ts","../src/providers/openai-compat.ts"],"sourcesContent":["/**\n * FNV-1a (32-bit) rendered in base36 — a tiny, dependency-free, deterministic\n * hash.\n *\n * Used to disambiguate sanitized identifiers: distinct inputs that would\n * otherwise collapse to the same sanitized string (e.g. `a.b` and `a/b` both\n * → `a_b`, or two over-long names truncated to the same prefix) keep a stable,\n * unique suffix. Determinism matters because the suffix must be identical\n * across runs and across a `tool_use` / `tool_result` pair derived from the\n * same original value.\n */\nexport function fnv1aBase36(value: string): string {\n let hash = 0x811C9DC5\n for (let i = 0; i < value.length; i++) {\n hash ^= value.charCodeAt(i)\n hash = Math.imul(hash, 0x01000193)\n }\n return (hash >>> 0).toString(36)\n}\n","/**\n * Tool-name aliasing helpers.\n *\n * Aliasing is applied at the LLM boundary only: the provider sees the aliased\n * name, but hooks and persisted turns use canonical names. See `AgentOptions.toolAliases`.\n */\n\nimport type { SessionContentBlock, SessionMessage } from './types'\nimport { fnv1aBase36 } from './hash'\n\n/** Forward + reverse lookup between canonical and aliased tool names */\nexport interface AliasMaps {\n /** canonical → alias (only entries where alias !== canonical) */\n aliasByCanonical: Map<string, string>\n /** alias → canonical (only entries where alias !== canonical) */\n canonicalByAlias: Map<string, string>\n}\n\n/**\n * Build alias lookup maps from a `toolAliases` record.\n *\n * Validates:\n * - No two canonical names map to the same alias (collision).\n * - No alias collides with another canonical tool name (would shadow).\n *\n * Silently ignores alias entries whose canonical name isn't in `canonicalNames` —\n * preset/agent authors can declare aliases for tools that may be added later via MCP.\n *\n * @param aliases - The `toolAliases` map from the agent options.\n * @param canonicalNames - All tool canonical names currently in scope (agent + MCP).\n */\nexport function buildAliasMaps(\n aliases: Record<string, string> | undefined,\n canonicalNames: Iterable<string>,\n): AliasMaps {\n const aliasByCanonical = new Map<string, string>()\n const canonicalByAlias = new Map<string, string>()\n\n if (!aliases) {\n return { aliasByCanonical, canonicalByAlias }\n }\n\n const canonicalSet = new Set(canonicalNames)\n\n // A canonical name is \"genuinely remapped\" when it has an alias entry that is\n // both non-empty and different from itself. Identity aliases or missing keys\n // mean the tool still answers to its canonical name on the wire.\n function isRemappedAway(canonical: string): boolean {\n const mapped = aliases![canonical]\n return typeof mapped === 'string' && mapped.length > 0 && mapped !== canonical\n }\n\n for (const [canonical, alias] of Object.entries(aliases)) {\n if (typeof alias !== 'string' || alias.length === 0)\n throw new Error(`Tool alias for \"${canonical}\" must be a non-empty string`)\n\n if (alias === canonical)\n continue\n\n if (!canonicalSet.has(canonical))\n continue\n\n // When the alias matches an existing canonical tool name, only allow it if\n // that other canonical is itself being remapped away (i.e. a name swap).\n // Otherwise the wire name would shadow a real tool.\n if (canonicalSet.has(alias) && !isRemappedAway(alias)) {\n throw new Error(`Tool alias \"${canonical}\" -> \"${alias}\" collides with an existing canonical tool name`)\n }\n\n const existingCanonical = canonicalByAlias.get(alias)\n if (existingCanonical && existingCanonical !== canonical) {\n throw new Error(\n `Tool alias collision: both \"${existingCanonical}\" and \"${canonical}\" map to alias \"${alias}\"`,\n )\n }\n\n aliasByCanonical.set(canonical, alias)\n canonicalByAlias.set(alias, canonical)\n }\n\n return { aliasByCanonical, canonicalByAlias }\n}\n\n/** Return the alias for a canonical name, falling back to the canonical name itself. */\nexport function toWireName(canonical: string, maps: AliasMaps): string {\n return maps.aliasByCanonical.get(canonical) ?? canonical\n}\n\n/** Return the canonical name for a wire name, falling back to the wire name itself. */\nexport function toCanonicalName(wire: string, maps: AliasMaps): string {\n return maps.canonicalByAlias.get(wire) ?? wire\n}\n\n/**\n * Tool names must match `^[a-zA-Z0-9_-]{1,N}$`. The charset is universal; the\n * length cap is taken as the STRICTEST across providers (OpenAI/-compat: 64;\n * Anthropic allows 128) so a sanitized name is valid on EVERY provider — and so\n * a mid-session model switch can never make the provider-level drop guard\n * (`sanitizeToolSpecs`) discard a tool we could have kept.\n *\n * MCP tool names are minted verbatim as `mcp_<server>_<tool>` (see\n * `connectMcpServers`), so a server configured as `SSH Ipseity` yields\n * `mcp_SSH Ipseity_exec` — the space (and `.`/`:`/`/`, unicode, or a >64-char\n * join) 400s the request the instant that tool lands in the `tools` array:\n * `tools.N.custom.name: String should match pattern ...`.\n */\nconst WIRE_NAME_MAX = 64\nconst WIRE_NAME_OK = /^[\\w-]{1,64}$/\nconst WIRE_NAME_BAD_CHAR = /[^\\w-]/g\n\n/**\n * Build OUTBOUND wire aliases that coerce any tool name violating the provider\n * name constraint into a valid one. Only offenders get an entry; clean setups\n * return `undefined` (no alias map churn, prompt cache untouched).\n *\n * Returned as a `Record` so it merges into `effectiveToolAliases` upstream of\n * BOTH wire-name computations — the `tool_search` catalog and the `tools` array\n * — keeping them in lockstep. Canonical names (dispatch, hooks, persisted\n * turns) never change; only the bytes the model sees do.\n *\n * Scoped to MCP names (the only third-party-controlled, thus only realistically\n * invalid, source). The hash suffix keeps distinct originals distinct even when\n * they collapse to the same replacement; `taken` guards the residual collision.\n */\nexport function buildSanitizedWireAliases(\n names: Iterable<string>,\n existing: Record<string, string> | undefined,\n): Record<string, string> | undefined {\n const wireOf = (name: string): string => {\n const alias = existing?.[name]\n return typeof alias === 'string' && alias.length > 0 ? alias : name\n }\n\n // Already-valid wire names stay occupied so a freshly minted one can never\n // shadow another tool.\n const taken = new Set<string>()\n const offenders: string[] = []\n for (const name of names) {\n const wire = wireOf(name)\n if (WIRE_NAME_OK.test(wire))\n taken.add(wire)\n else\n offenders.push(name)\n }\n if (offenders.length === 0)\n return undefined\n\n const out: Record<string, string> = {}\n for (const name of offenders) {\n const sanitized = sanitizeWireName(wireOf(name), taken)\n out[name] = sanitized\n taken.add(sanitized)\n }\n return out\n}\n\nfunction sanitizeWireName(name: string, taken: ReadonlySet<string>): string {\n let base = name.replace(WIRE_NAME_BAD_CHAR, '_')\n if (base.length === 0)\n base = 'tool'\n // Hash the ORIGINAL so `a.b` vs `a/b` (both → `a_b`) and truncated long\n // names stay distinct and deterministic across runs.\n const hash = fnv1aBase36(name)\n const withSuffix = (suffix: string): string =>\n `${base.slice(0, Math.max(1, WIRE_NAME_MAX - suffix.length))}${suffix}`\n let candidate = withSuffix(`_${hash}`)\n for (let n = 1; taken.has(candidate); n++)\n candidate = withSuffix(`_${hash}_${n}`)\n return candidate\n}\n\n/**\n * Augment {@link AliasMaps} with INBOUND-ONLY aliases for the Claude Code\n * `mcp__server__tool` double-underscore naming convention. For every MCP\n * tool registered with the canonical `mcp_<server>_<tool>` form, add a\n * `canonicalByAlias` entry mapping the double-underscore variant to the\n * same canonical name. Outbound (`aliasByCanonical`) is left untouched —\n * the model still sees the single-underscore canonical on the wire, so\n * the tools-array byte count (and the provider prompt cache) is\n * unaffected.\n *\n * Why: SDK consumers and tool descriptions across the ecosystem\n * inconsistently reference one form or the other (Anthropic's docs use\n * double, zidane uses single). A Claude-Code-trained model emitting\n * `mcp__acme__apply_migration` against a server zidane registered as\n * `mcp_acme_apply_migration` would otherwise trip the `tool:unknown`\n * path and waste a turn on the correction.\n *\n * Idempotent: a canonical that already has a double-underscore alias\n * mapped (host's explicit `toolAliases` got there first) is left alone.\n * Tools whose canonical name DOESN'T start with `mcp_` (or that contain\n * no inner separator) are skipped — we don't want to invent aliases for\n * non-MCP tools or malformed names.\n *\n * Mutates `maps.canonicalByAlias` in place and returns the same object\n * for chaining.\n */\nexport function augmentMcpDoubleUnderscoreAliases(\n maps: AliasMaps,\n canonicalNames: Iterable<string>,\n): AliasMaps {\n for (const canonical of canonicalNames) {\n if (!canonical.startsWith('mcp_'))\n continue\n // Strip leading `mcp_`, then require AT LEAST one more `_` so the\n // remainder is `<server>_<tool>` (a degenerate `mcp_foo` with no\n // tool segment has nothing meaningful to double-underscore).\n const tail = canonical.slice(4)\n const sep = tail.indexOf('_')\n if (sep <= 0 || sep >= tail.length - 1)\n continue\n const server = tail.slice(0, sep)\n const tool = tail.slice(sep + 1)\n const doubleForm = `mcp__${server}__${tool}`\n if (doubleForm === canonical)\n continue // shouldn't happen but defensive\n if (maps.canonicalByAlias.has(doubleForm))\n continue // host alias or earlier pass got there first\n maps.canonicalByAlias.set(doubleForm, canonical)\n }\n return maps\n}\n\n/**\n * Rewrite `tool_call` block names in a content array from canonical → wire for outbound\n * messages sent to the provider. Mutation is non-destructive (returns a new array).\n */\nexport function rewriteContentToWire(\n content: SessionContentBlock[],\n maps: AliasMaps,\n): SessionContentBlock[] {\n if (maps.aliasByCanonical.size === 0)\n return content\n return content.map((block) => {\n if (block.type !== 'tool_call')\n return block\n const wire = maps.aliasByCanonical.get(block.name)\n if (!wire || wire === block.name)\n return block\n return { ...block, name: wire }\n })\n}\n\n/**\n * Rewrite `tool_call` block names in a content array from wire → canonical for inbound\n * messages received from the provider. Non-destructive.\n */\nexport function rewriteContentToCanonical(\n content: SessionContentBlock[],\n maps: AliasMaps,\n): SessionContentBlock[] {\n if (maps.canonicalByAlias.size === 0)\n return content\n return content.map((block) => {\n if (block.type !== 'tool_call')\n return block\n const canonical = maps.canonicalByAlias.get(block.name)\n if (!canonical || canonical === block.name)\n return block\n return { ...block, name: canonical }\n })\n}\n\n/**\n * Rewrite every `SessionMessage.content` in an array from canonical → wire.\n * Returns a new array of new message objects — input messages are not mutated.\n * When the alias map is empty, returns the input by reference (no allocation).\n */\nexport function rewriteMessagesToWire(\n messages: SessionMessage[],\n maps: AliasMaps,\n): SessionMessage[] {\n if (maps.aliasByCanonical.size === 0)\n return messages\n return messages.map(msg => ({ ...msg, content: rewriteContentToWire(msg.content, maps) }))\n}\n","/**\n * 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 !== 'audio' && type !== 'video' && type !== 'document') {\n throw new Error(`Invalid PromptPart type \"${type}\". Expected \"text\" | \"image\" | \"audio\" | \"video\" | \"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 === 'audio'\n || part.type === 'video'\n || part.type === 'document'\n ))\n if (!hasMeaningfulPart)\n return undefined\n\n return prompt\n}\n\n/**\n * Standard error for a media kind the active provider/model can't accept.\n *\n * Per the audio/video design, unsupported media is a hard error (not a silent\n * marker) so the caller switches to a capable model rather than paying for a\n * request the provider would 400 — or worse, one that succeeds with the bytes\n * stripped and the model confabulating over what it can't see.\n */\nexport function unsupportedMediaError(kind: 'audio' | 'video', provider: string): Error {\n return new Error(\n `Provider \"${provider}\" does not support ${kind} input. `\n + `Route to a ${kind}-capable model (e.g. an OpenAI-compat / OpenRouter model `\n + `with capabilities.${kind} = true) to send ${kind} attachments.`,\n )\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 * - `audio` / `video` parts map to their canonical blocks verbatim. Providers\n * that cannot accept them throw later (in `promptMessage` / loop gating); the\n * default builder stays lossless so capable providers receive the bytes.\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 if (part.type === 'audio') {\n content.push({ type: 'audio', mediaType: part.mediaType, data: part.data, ...(part.name ? { name: part.name } : {}) })\n continue\n }\n\n if (part.type === 'video') {\n content.push({ type: 'video', 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 for (const part of parts) {\n if (part.type === 'audio' && provider.meta.capabilities?.audio !== true)\n throw unsupportedMediaError('audio', provider.name)\n if (part.type === 'video' && provider.meta.capabilities?.video !== true)\n throw unsupportedMediaError('video', provider.name)\n }\n\n if (provider.promptMessage)\n return provider.promptMessage(parts)\n return defaultPromptMessage(parts)\n}\n","/**\n * Model catalog override — the one seam for replacing model metadata.\n *\n * zidane resolves model metadata (output ceiling, pricing, context window,\n * reasoning support) from pi-ai's bundled registry. That registry is a\n * snapshot: it can't know a gateway alias, a private deployment, a model\n * released after our dependency pin, or a non-Anthropic model served over the\n * Anthropic wire. Without a seam, consumers either patch our source (the\n * `EXTRA_COST_RATES` table in `cost.ts` exists because of exactly that) or\n * mutate pi-ai's unfrozen catalog behind our back.\n *\n * Two ways to supply one, both first-class:\n *\n * - `behavior.modelCatalog` — per agent, and per run like every other\n * behavior field. No process-wide state, so two agents can carry\n * different catalogs.\n * - {@link setModelCatalog} — process-wide default. Also the only source\n * the chat/TUI layer can see (the model picker resolves metadata before\n * any agent exists).\n *\n * Resolution, first hit wins: run behavior → agent behavior → global →\n * pi-ai's bundled registry → local fallback tables.\n *\n * Everything stays keyed by `(provider, modelId)`: nothing is flattened\n * across providers, and overriding one provider leaves the others fully\n * bundled. The provider key is the pi-ai registry id, which is usually the\n * factory name (`anthropic`, `cerebras`, `openrouter`, …) — with one\n * exception: the Codex provider stores model records under `openai-codex` and\n * prices them under `openai`, and accepts an entry keyed EITHER way — on the\n * wire via {@link aliasCatalogProviders} and in the host layer via the\n * descriptor's key set, so an override can't half-apply.\n *\n * Two operational notes:\n *\n * - **Serialization.** The table form is plain JSON; the `{ get, list }`\n * form holds functions. A durable / replay runtime that snapshots\n * `behavior` can only round-trip the table form — build the source form\n * inside the process that runs the agent, or install it with\n * {@link setModelCatalog} at boot.\n * - **Multi-tenancy.** {@link setModelCatalog} is process-wide, so in a\n * shared server it applies to every tenant. Pass `behavior.modelCatalog`\n * per agent / per run instead; the global is for a deployment-wide\n * correction and for the host's model picker.\n */\n\n/**\n * Metadata for one model. Every field is optional and merges FIELD-LEVEL over\n * the bundled entry, so `{ maxTokens: 128000 }` corrects the ceiling and\n * leaves pricing and context window to the registry.\n */\nexport interface ModelCatalogEntry {\n /** Model id. Required only for entries surfaced through {@link ModelCatalogSource.list}. */\n id?: string\n /** Display name for pickers. */\n name?: string\n /** Max output tokens — the provider's `max_tokens` default and hard clamp. */\n maxTokens?: number\n /** Total context window, in tokens (drives the host-layer context gauge). */\n contextWindow?: number\n /** Whether the model supports reasoning/thinking. */\n reasoning?: boolean\n /** Accepted input modalities. */\n input?: readonly ('text' | 'image')[]\n /**\n * Per-MTok rates used for cost estimation. Merges PER-RATE over the bundled\n * rates, so `{ cost: { output: 30 } }` corrects the output rate and keeps the\n * registry's input / cache rates rather than zeroing them.\n */\n cost?: {\n input?: number\n output?: number\n cacheRead?: number\n cacheWrite?: number\n }\n}\n\n/**\n * Programmatic catalog. Both members are optional and both fall through on\n * `undefined`, which is what keeps overrides scoped to the providers you\n * actually own.\n */\nexport interface ModelCatalogSource {\n /**\n * Resolve one model. Return `undefined` to fall through to the bundled\n * entry for that `(provider, modelId)`.\n */\n get?: (provider: string, modelId: string) => ModelCatalogEntry | undefined\n /**\n * Enumerate a provider's models (model pickers, `modelsForDescriptor`).\n *\n * - `undefined` → use the bundled list for that provider (the default).\n * - `[…]` → you own that provider's list; bundled entries are not\n * consulted, so this both adds and hides.\n * - `[]` → you own it and it has no models (hides the picker).\n */\n list?: (provider: string) => readonly ModelCatalogEntry[] | undefined\n}\n\n/**\n * A catalog: either a {@link ModelCatalogSource} or the table shorthand —\n * a plain record keyed `\"<provider>:<modelId>\"`.\n *\n * The table form is a pure OVERLAY: entries patch or add models, and each\n * provider keeps every bundled model the table doesn't name. Hiding bundled\n * models requires the `list` function, so the easy path can't accidentally\n * blank a provider's picker.\n *\n * ```ts\n * setModelCatalog({ 'openai:gpt-5.6': { maxTokens: 128000 } })\n * ```\n */\nexport type ModelCatalog = ModelCatalogSource | Readonly<Record<string, ModelCatalogEntry>>\n\n/**\n * Normalized internal form. The table (when the shorthand was used) is kept\n * alongside the accessors so list-overlay can enumerate it — a `get`-only\n * closure would be opaque to enumeration.\n */\ninterface NormalizedCatalog extends ModelCatalogSource {\n table?: Readonly<Record<string, ModelCatalogEntry>>\n}\n\nconst holder: { current: NormalizedCatalog | undefined } = { current: undefined }\n\n/**\n * Normalization cache. Per-call catalogs arrive on every turn; without this a\n * table form would allocate a fresh closure per lookup. Weak so a discarded\n * catalog doesn't pin memory.\n */\nconst normalized = new WeakMap<object, NormalizedCatalog>()\n\n/**\n * Install (or clear, with `null`) the process-wide catalog. Latest call wins.\n *\n * Prefer `behavior.modelCatalog` when you can: it needs no global state and\n * scopes to one agent or one run. Use this for a deployment-wide correction,\n * or when the host's model picker must see the override too.\n */\nexport function setModelCatalog(catalog: ModelCatalog | null): void {\n holder.current = catalog ? normalize(catalog) : undefined\n}\n\n/** The process-wide catalog, normalized to source form. */\nexport function getModelCatalog(): ModelCatalogSource | undefined {\n return holder.current\n}\n\nfunction normalize(catalog: ModelCatalog): NormalizedCatalog {\n const cached = normalized.get(catalog as object)\n if (cached)\n return cached\n const source = isSource(catalog)\n ? catalog\n : ((): NormalizedCatalog => {\n const table = catalog as Readonly<Record<string, ModelCatalogEntry>>\n // `list` stays absent: the table form is an overlay, so callers keep\n // the bundled list and fold these entries in (`listModelOverlay`).\n return { table, get: (provider, modelId) => table[`${provider}:${modelId}`] }\n })()\n normalized.set(catalog as object, source)\n return source\n}\n\n/** A `ModelCatalog` is a source when it declares either accessor. */\nfunction isSource(catalog: ModelCatalog): catalog is ModelCatalogSource {\n const candidate = catalog as ModelCatalogSource\n return typeof candidate.get === 'function' || typeof candidate.list === 'function'\n}\n\n/**\n * Resolve one model through the catalog chain, WITHOUT consulting the bundled\n * registry — callers own that fallback because each reader has a different\n * bundled shape (pi-ai `Model`, `CodexModel`, `ModelInfo`).\n *\n * `perCall` (an agent's / run's `behavior.modelCatalog`) shadows the global.\n */\nexport function lookupModel(\n provider: string,\n modelId: string,\n perCall?: ModelCatalog,\n): ModelCatalogEntry | undefined {\n for (const source of sourcesFor(perCall)) {\n const entry = source.get?.(provider, modelId)\n if (entry)\n return entry\n }\n return undefined\n}\n\n/**\n * Authoritative model list for a provider, or `undefined` when no catalog\n * claims ownership (the caller then uses the bundled list).\n *\n * Only the function form can claim ownership; table entries are overlaid via\n * {@link listModelOverlay} instead.\n */\nexport function listModels(\n provider: string,\n perCall?: ModelCatalog,\n): readonly ModelCatalogEntry[] | undefined {\n for (const source of sourcesFor(perCall)) {\n const list = source.list?.(provider)\n if (list)\n return list\n }\n return undefined\n}\n\n/**\n * Table-form entries for one provider, to merge on top of a bundled list\n * (these win on id collision). Empty when no table-form catalog is active.\n */\nexport function listModelOverlay(\n provider: string,\n perCall?: ModelCatalog,\n): readonly ModelCatalogEntry[] {\n const prefix = `${provider}:`\n const out: ModelCatalogEntry[] = []\n const seen = new Set<string>()\n for (const source of sourcesFor(perCall)) {\n if (!source.table)\n continue\n for (const [key, entry] of Object.entries(source.table)) {\n if (!key.startsWith(prefix))\n continue\n const id = entry.id ?? key.slice(prefix.length)\n // Earlier sources (per-call before global) win on id collision.\n if (seen.has(id))\n continue\n seen.add(id)\n out.push(entry.id ? entry : { ...entry, id })\n }\n }\n return out\n}\n\n/**\n * View of `catalog` that resolves a model under several provider ids, in\n * order — for providers whose metadata and pricing live under DIFFERENT\n * registry keys.\n *\n * The Codex provider is the case: model records come from `openai-codex`\n * while cost estimation runs under `openai`. Without this, one catalog entry\n * fixes the ceiling or the price but not both, depending on which key the\n * consumer guessed — and neither key is guessable.\n *\n * Returns `undefined` for an absent catalog so callers can pass it straight\n * through.\n */\nexport function aliasCatalogProviders(\n catalog: ModelCatalog | undefined,\n providers: readonly string[],\n): ModelCatalog | undefined {\n if (!catalog)\n return undefined\n return {\n // The queried provider is ignored on purpose: the point is to accept the\n // entry under any of this provider's aliases.\n get: (_provider, modelId) => {\n for (const id of providers) {\n const entry = lookupModel(id, modelId, catalog)\n if (entry)\n return entry\n }\n return undefined\n },\n }\n}\n\n/**\n * Overlay a catalog entry onto a bundled model record (pi-ai `Model`, the\n * chat layer's `ModelInfo`, …). Only fields the entry actually defines are\n * replaced, so a one-field override corrects that field and inherits the rest.\n *\n * `cost` merges per-rate rather than wholesale, so `{ cost: { output: 30 } }`\n * keeps the bundled input/cache rates instead of zeroing them.\n *\n * Kept here rather than at each reader so the merge semantics can't drift\n * between the wire path and the host layer.\n */\nexport function mergeCatalogEntry<T extends object>(base: T, entry: ModelCatalogEntry | undefined): T {\n if (!entry)\n return base\n const patch: Record<string, unknown> = {}\n if (entry.id !== undefined)\n patch.id = entry.id\n if (entry.name !== undefined)\n patch.name = entry.name\n if (entry.maxTokens !== undefined)\n patch.maxTokens = entry.maxTokens\n if (entry.contextWindow !== undefined)\n patch.contextWindow = entry.contextWindow\n if (entry.reasoning !== undefined)\n patch.reasoning = entry.reasoning\n if (entry.input !== undefined)\n patch.input = entry.input\n if (entry.cost !== undefined) {\n const baseCost = (base as { cost?: Record<string, unknown> }).cost\n patch.cost = { ...baseCost, ...entry.cost }\n }\n return { ...base, ...patch }\n}\n\n/** Per-call source first (it shadows), then the process-wide one. */\nfunction sourcesFor(perCall?: ModelCatalog): NormalizedCatalog[] {\n const out: NormalizedCatalog[] = []\n if (perCall)\n out.push(normalize(perCall))\n if (holder.current)\n out.push(holder.current)\n return out\n}\n","import type { TurnUsage } from '../types'\nimport type { ModelCatalog } from './model-catalog'\nimport { lookupModel } from './model-catalog'\n\ninterface ModelCostRates {\n input?: number\n output?: number\n cacheRead?: number\n cacheWrite?: number\n}\n\ntype GetModel = (provider: string, model: string) => { cost?: ModelCostRates } | undefined\n\n// pi-ai ships ESM-only (no `require` export condition), so it can only be pulled\n// in via an async dynamic import — there is no synchronous load path. Loading it\n// lazily (instead of a static import) also keeps its multi-MB price registry out\n// of the static bundle, so edge consumers can externalize `@earendil-works/pi-ai`;\n// the `.catch` then degrades to `EXTRA_COST_RATES`. The promise is cached, so the\n// import runs at most once per process.\nlet getModelPromise: Promise<GetModel | null> | undefined\nfunction loadGetModel(): Promise<GetModel | null> {\n getModelPromise ??= import('@earendil-works/pi-ai/providers/all')\n .then(mod => mod.getBuiltinModel as unknown as GetModel)\n .catch(() => null)\n return getModelPromise\n}\n\n/**\n * Per-MTok rates for models not yet in pi-ai's bundled registry. Keyed by\n * `provider:modelId`. Consulted only when pi-ai's `getModel` misses, so a\n * later dep bump that bundles the model transparently takes over.\n *\n * MAINTAINER-side stopgap only. Consumers with custom / gateway / private\n * models should install a catalog instead of sending PRs against this table:\n * `behavior.modelCatalog` per agent, or `setModelCatalog()` process-wide\n * (see `./model-catalog`) — both outrank the registry AND this table.\n */\nconst EXTRA_COST_RATES: Readonly<Record<string, ModelCostRates>> = {\n // Claude Opus 5 — same pricing as 4.8 ($5/$25 per MTok).\n 'anthropic:claude-opus-5': { input: 5, output: 25, cacheRead: 0.5, cacheWrite: 6.25 },\n // Claude Fable 5 — $10/$50 per MTok (cache rates at the standard 0.1×/1.25×).\n 'anthropic:claude-fable-5': { input: 10, output: 50, cacheRead: 1, cacheWrite: 12.5 },\n // Claude Opus 4.8 — same pricing as 4.7 ($5/$25 per MTok).\n 'anthropic:claude-opus-4-8': { input: 5, output: 25, cacheRead: 0.5, cacheWrite: 6.25 },\n // `gpt-5.6` is OpenAI's shorthand alias for the Sol flagship. pi-ai's\n // registry bundles the concrete ids (sol/terra/luna) but not the bare\n // alias, so usage recorded against the shorthand needs this local entry\n // (Sol's short-context standard-tier rates).\n 'openai:gpt-5.6': { input: 5, output: 30, cacheRead: 0.5, cacheWrite: 6.25 },\n // Arcee Trinity — platform pricing per MTok.\n 'arcee:trinity-large-thinking': { input: 0.25, output: 0.8 },\n 'arcee:trinity-large-preview': { input: 0.45, output: 0.15 },\n 'arcee:trinity-mini': { input: 0.045, output: 0.15 },\n // Baseten Model APIs — published per-MTok rates (cached-input discount as\n // cacheRead). GLM 5.1 / Nemotron Ultra omitted: no published rate yet.\n 'baseten:zai-org/GLM-5.2': { input: 1.4, output: 4.4, cacheRead: 0.14 },\n 'baseten:zai-org/GLM-5': { input: 0.95, output: 3.15, cacheRead: 0.2 },\n 'baseten:zai-org/GLM-4.7': { input: 0.6, output: 2.2, cacheRead: 0.12 },\n 'baseten:moonshotai/Kimi-K2.6': { input: 1, output: 3.9, cacheRead: 0.2 },\n 'baseten:moonshotai/Kimi-K2.5': { input: 0.6, output: 3, cacheRead: 0.12 },\n 'baseten:deepseek-ai/DeepSeek-V4-Pro': { input: 1.74, output: 3.48, cacheRead: 0.145 },\n 'baseten:nvidia/Nemotron-120B-A12B': { input: 0.3, output: 0.75, cacheRead: 0.06 },\n 'baseten:openai/gpt-oss-120b': { input: 0.1, output: 0.5 },\n}\n\n/**\n * Per-rate multipliers applied to the looked-up base rates before computing the\n * total. Used for premium-priced modes (e.g. Anthropic fast mode at 2×/6×). A\n * missing key defaults to `1` (no change).\n */\nexport interface CostMultiplier {\n input?: number\n output?: number\n cacheRead?: number\n cacheWrite?: number\n}\n\n/**\n * Fill in `usage.cost` from pi-ai's bundled price registry when the\n * provider didn't report one. Provider-reported cost (e.g. OpenRouter's\n * `total_cost`, OpenAI's pi-ai-computed total) always wins — we only\n * estimate when `usage.cost` is undefined.\n *\n * Lookup is `(provider, usage.modelId)`. On a miss (unknown / unbundled\n * model), `cost` stays undefined and the footer indicator hides — better\n * than fabricating a $0.\n *\n * The number is an estimate: token counts come from the API, rates from\n * the locally-bundled registry that refreshes with `@earendil-works/pi-ai`\n * version bumps. If a provider changes prices between bumps the figure\n * will drift until the dep updates. An optional `multiplier` scales the\n * base rates for premium modes (e.g. fast mode) before totaling.\n */\n/**\n * Dollar cost of a prompt-cache break's re-billed prefix.\n *\n * The `rebilledTokens` were WRITTEN at the cache-write rate when a healthy\n * turn would have READ them at the cache-read rate, so the waste is\n * `(write − read) × tokens`. Same registry + fallback-table resolution and\n * the same doctrine as {@link fillEstimatedCost}: returns `undefined` on a\n * rate miss (unknown provider/model, or no published cache-write rate) —\n * better than fabricating a $0. An optional `multiplier` scales the base\n * rates for premium modes (e.g. Anthropic fast mode, 1h-TTL writes at 2x).\n *\n * `catalog` is caller-supplied because this helper has no run context to read\n * `behavior.modelCatalog` from: cache-break telemetry is computed by the host\n * that owns the `cache:break` hook, not by the loop. A host using a model\n * catalog must pass the same one here, or a re-bill estimate for an\n * override-only model comes back `undefined` while the per-turn cost is\n * priced correctly.\n */\n/**\n * Rates for one model: catalog over pi-ai registry over local table.\n *\n * A catalog entry wins because it's an explicit claim about a model we may not\n * know (gateway alias, private deployment, post-pin release) — but it merges\n * PER-RATE over whatever the registry knows, matching `mergeCatalogEntry` and\n * the documented contract. Replacing the whole rate object would zero the rates\n * a partial entry doesn't mention, so `{ cost: { output: 30 } }` would bill\n * input and cache traffic at $0 — and the picker (which merges per-rate) would\n * disagree with the invoice.\n */\nasync function resolveRates(\n provider: string,\n modelId: string,\n catalog?: ModelCatalog,\n): Promise<ModelCostRates | undefined> {\n const override = lookupModel(provider, modelId, catalog)?.cost\n\n // Await the cached pi-ai load (resolves once; instant thereafter). `getModel`\n // accepts arbitrary strings here (the openai-compat adapter routes both known\n // and user-defined endpoint names through this helper) and throws on a miss,\n // so the lookup is guarded.\n const getModel = await loadGetModel()\n let base: ModelCostRates | undefined\n if (getModel) {\n try {\n base = getModel(provider, modelId)?.cost\n }\n catch {\n base = undefined\n }\n }\n base ??= EXTRA_COST_RATES[`${provider}:${modelId}`]\n\n if (!override)\n return base\n return base ? { ...base, ...override } : override\n}\n\nexport async function estimateRebilledCostUsd(\n provider: string,\n modelId: string | undefined,\n rebilledTokens: number,\n multiplier?: CostMultiplier,\n catalog?: ModelCatalog,\n): Promise<number | undefined> {\n if (!modelId || rebilledTokens <= 0)\n return undefined\n\n const rates = await resolveRates(provider, modelId, catalog)\n if (!rates || rates.cacheWrite === undefined)\n return undefined\n\n const write = rates.cacheWrite * (multiplier?.cacheWrite ?? 1)\n const read = (rates.cacheRead ?? 0) * (multiplier?.cacheRead ?? 1)\n return Math.max(0, ((write - read) * rebilledTokens) / 1_000_000)\n}\n\nexport async function fillEstimatedCost(\n usage: TurnUsage,\n provider: string,\n multiplier?: CostMultiplier,\n catalog?: ModelCatalog,\n): Promise<TurnUsage> {\n if (usage.cost !== undefined)\n return usage\n if (!usage.modelId)\n return usage\n\n const rates = await resolveRates(provider, usage.modelId, catalog)\n if (!rates)\n return usage\n\n const mul = multiplier ?? {}\n const total\n = ((rates.input ?? 0) * (mul.input ?? 1) * (usage.input ?? 0)\n + (rates.output ?? 0) * (mul.output ?? 1) * (usage.output ?? 0)\n + (rates.cacheRead ?? 0) * (mul.cacheRead ?? 1) * (usage.cacheRead ?? 0)\n + (rates.cacheWrite ?? 0) * (mul.cacheWrite ?? 1) * (usage.cacheCreation ?? 0)) / 1_000_000\n\n if (total <= 0)\n return usage\n\n return { ...usage, cost: total }\n}\n","/**\n * Prompt-cache affinity keys — the opt-in routing hint that keeps a session's\n * requests landing where its KV cache is already warm.\n *\n * ## Why a key exists at all\n *\n * A prefix cache only pays off when two requests share a prefix AND reach the\n * same machine. Providers pick that machine by hashing the head of the prompt\n * (OpenAI documents ~the first 256 tokens), which is a good guess and no more:\n * under load, traffic for one prefix spills across replicas and a session's\n * cache goes cold for reasons the client never sees. A stable per-session key,\n * combined server-side with that prefix hash, makes the routing sticky.\n *\n * The field is not a zidane invention and not a cache handle — it cannot make\n * the server keep or fetch an entry. It is a routing hint, so the honest\n * expectation differs per route:\n *\n * - OpenAI / Azure — real effect; `prompt_cache_key` is combined with the\n * prefix hash, and newer model families require it for the more reliable\n * matching path.\n * - OpenRouter — real effect; it reads the same field as its sticky-routing\n * key, so clients already sending one get session-pinned provider routing.\n * - Baseten Model APIs — effectively a no-op; their load balancer is already\n * KV-cache-aware (it routes on replica cache state) and caching is\n * unconditional.\n * - 1P Anthropic — effectively a no-op; cache entries are scoped per\n * organization/workspace and matched purely by prefix, with no client-side\n * routing knob. `metadata.user_id` is what we carry it in, and that field's\n * documented purpose is abuse detection.\n * - Any proxy or AI gateway in front of either provider (`baseURL` overrides,\n * Cloudflare AI Gateway, LiteLLM, …) — real effect, and the reason this is\n * worth plumbing on both providers: it hands the intermediary a stable\n * session key to route on.\n *\n * Keeping the semantics identical across providers means a host sets one flag\n * and stops caring which provider is behind it.\n *\n * ## Why the default is derived, not injected\n *\n * `StreamOptions` carries no session identity, and adding one would touch the\n * loop plus every provider. It is also the wrong shape: the GUI/daemon shares a\n * single provider instance across sessions, so a construction-time constant\n * would bucket unrelated sessions onto one replica. Deriving the key from the\n * cacheable prefix instead is stable for a session's lifetime, distinct between\n * sessions, and needs no plumbing — the same property the servers key on.\n *\n * A hash rather than a raw id also keeps prompt text and user identifiers out\n * of a field that leaves our process (Anthropic explicitly asks that\n * `metadata.user_id` not carry PII).\n *\n * Off by default everywhere: when unset, requests stay byte-identical to what\n * they were before this module existed.\n */\n\nimport type { SessionMessage } from '../types'\nimport { createHash } from 'node:crypto'\n\n/** What a request needs to expose for a key to be derived from it. */\nexport interface PromptCacheKeyContext {\n model: string\n system: string\n messages: readonly SessionMessage[]\n}\n\n/**\n * How to obtain the affinity key for a request.\n *\n * - `undefined` / `false` — send nothing (default).\n * - `true` — derive from the cacheable prefix via {@link derivePromptCacheKey}.\n * - `string` — use verbatim. For hosts that already have a stable session id\n * and would rather route on that.\n * - function — compute per request; returning `undefined` omits the field for\n * that request, so a host can key some traffic and not the rest.\n */\nexport type PromptCacheKeyOption\n = | boolean\n | string\n | ((ctx: PromptCacheKeyContext) => string | undefined)\n\n/**\n * A stable key for everything a session shares: the system prompt plus its\n * first user message.\n *\n * Deliberately NOT the whole transcript — that grows every turn, which would\n * hand out a new key per request and defeat the point. The head of the\n * conversation is what stays byte-identical for a session's life, and it is\n * also what the servers hash for routing, so the key moves exactly when the\n * routable prefix does (a new session, a different agent profile, or a\n * compaction that rewrites the head — all cases where the old replica's cache\n * is worthless anyway).\n *\n * The message contribution is capped at its first 4 KiB of JSON: this runs on\n * every stream call, and a first message carrying inline media (multi-MB\n * base64) would pay a full stringify + hash per turn for no routing benefit —\n * the first few hundred bytes already distinguish sessions, and the servers\n * themselves only hash ~the first 256 tokens for routing. Two sessions that\n * agree on system + 4 KiB of opener share a routing bucket, which is exactly\n * when sharing a replica helps rather than hurts.\n *\n * 32 hex chars of sha256, matching the digest width used in\n * `src/attachment-handle.ts`.\n */\nexport function derivePromptCacheKey(system: string, messages: readonly SessionMessage[]): string {\n const firstUser = messages.find(m => m.role === 'user')\n const head = firstUser === undefined ? '' : JSON.stringify(firstUser.content).slice(0, 4096)\n return createHash('sha256').update(`${system}\\u0000${head}`).digest('hex').slice(0, 32)\n}\n\n/**\n * Resolve a configured option into the key to send, or `undefined` to send\n * nothing. Empty strings are treated as \"send nothing\" so a host threading\n * through a possibly-unset session id can pass it straight in.\n */\nexport function resolvePromptCacheKey(\n option: PromptCacheKeyOption | undefined,\n ctx: PromptCacheKeyContext,\n): string | undefined {\n if (option === undefined || option === false)\n return undefined\n if (option === true)\n return derivePromptCacheKey(ctx.system, ctx.messages)\n const resolved = typeof option === 'function' ? option(ctx) : option\n return resolved !== undefined && resolved.length > 0 ? resolved : undefined\n}\n","/**\n * JSON Schema sanitizer for tool `inputSchema` forwarded to LLM providers.\n *\n * Why this exists: MCP servers ship arbitrary JSON Schema in `tools/list`.\n * Anthropic and (to a lesser extent) OpenAI reject specific keyword\n * combinations with a 400 on `messages.create`, and the error message\n * lands deep inside an SDK exception that consumers blame on their own\n * code. The most common failure modes in the wild:\n *\n * - Root `$ref` (server-published schema lives under `#/$defs/X`).\n * - Root `oneOf` / `anyOf` / `allOf` (Anthropic allows neither at the\n * top level; expects `type: 'object'`).\n * - `type: ['object', 'null']` at the root (nullable variants of an\n * otherwise valid object schema).\n * - Missing `properties` on an object root.\n * - `nullable: true` (Swagger / OpenAPI 3.0 idiom — not standard JSON\n * Schema, ignored by every modern provider but Anthropic warns).\n * - Nested `$ref` pointing at `#/definitions/X` without the schema\n * actually inlining the referenced definition.\n *\n * Pure rewrite. Returns a new schema (or the same reference when nothing\n * needed to change — the hot path on stable tool registries) and a list\n * of `warnings` describing each rewrite. Providers call\n * `sanitizeToolSpecs` from `formatTools` so every tool — eager MCP, lazy\n * MCP, native, skill — gets the same treatment, and warnings surface\n * through `console.warn` once per batch (the seen-set in\n * `sanitizeToolSpecs` is local to one call, so a stable bad schema logs\n * once per request rather than every turn).\n *\n * Scope:\n * - Pure function. No I/O, no globals.\n * - Bounded recursion (`MAX_DEPTH = 32`) so a malicious server can't\n * wedge the agent with a deeply self-referential schema.\n * - Bounded `$ref` resolution (`MAX_REF_HOPS = 16`) for the same\n * reason. Cycles fall back to `{}` (permissive) rather than\n * throwing — the request still succeeds, the schema is just looser.\n *\n * Non-goals:\n * - Validating that the schema is internally consistent.\n * - Cleaning up tool *descriptions* (handled elsewhere).\n * - Provider-side feature detection (handled by the provider's own\n * `formatTools`; this module is a syntactic guard, not a semantic one).\n */\n\n/** Strictness profile — picks which transformations to apply. */\nexport type SchemaSanitizeProfile = 'anthropic' | 'openai' | 'permissive'\n\nexport interface SchemaSanitizeOptions {\n /** Strictness profile. Defaults to `'permissive'`. */\n profile?: SchemaSanitizeProfile\n /**\n * Tool name for context in warnings. Optional — when set, every warning\n * is prefixed with `[tool:<name>] ` so log lines correlate to the tool.\n */\n toolName?: string\n}\n\nexport interface SchemaSanitizeResult {\n /** Sanitized schema, safe to forward to the provider. */\n schema: Record<string, unknown>\n /**\n * Human-readable strings describing each rewrite the sanitizer performed.\n * Empty on a clean schema. Consumers may log these (recommended) or\n * thread them through a hook for observability.\n */\n warnings: string[]\n}\n\n/**\n * Provider tool-name constraint. The charset (`[A-Za-z0-9_-]`) is universal\n * across Anthropic / OpenAI / OpenAI-compat; only the length cap differs —\n * Anthropic allows 128, OpenAI/-compat cap at 64. A name violating this 400s\n * the WHOLE request (`tools.N.custom.name: String should match pattern ...`),\n * not just the one tool — so it's a request-killer, not a per-tool nuisance.\n */\nconst TOOL_NAME_CHARSET = /^[\\w-]+$/\n\nfunction maxToolNameLen(profile: SchemaSanitizeProfile): number {\n return profile === 'anthropic' ? 128 : 64\n}\n\nfunction isValidToolName(name: string, profile: SchemaSanitizeProfile): boolean {\n return name.length >= 1 && name.length <= maxToolNameLen(profile) && TOOL_NAME_CHARSET.test(name)\n}\n\n/** Max nesting depth before sub-schemas are replaced with `{}`. */\nconst MAX_DEPTH = 32\n\n/** Max `$ref` hop count before resolution gives up. */\nconst MAX_REF_HOPS = 16\n\n/** Keys that hold a single sub-schema. */\nconst SUBSCHEMA_KEYS = ['items', 'additionalProperties', 'contains', 'not', 'if', 'then', 'else', 'propertyNames'] as const\n\n/**\n * Keys that hold a record of sub-schemas.\n *\n * `$defs` / `definitions` are intentionally NOT included — they're\n * metaschema storage, only reached via `$ref` resolution (which has its\n * own walker in `resolveRef`), and `enforceRoot` strips them at the\n * wire layer once inlining is done. Recursing into them would cost\n * cycles and produce warnings about sub-trees the provider never sees.\n */\nconst SUBSCHEMA_RECORD_KEYS = ['properties', 'patternProperties', 'dependentSchemas'] as const\n\n/** Keys that hold an array of sub-schemas. */\nconst SUBSCHEMA_ARRAY_KEYS = ['oneOf', 'anyOf', 'allOf', 'prefixItems'] as const\n\nfunction isPlainObject(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value)\n}\n\n/**\n * Resolve a JSON pointer like `#/$defs/Foo` against `root`. Returns\n * `undefined` when the pointer doesn't resolve or hops past `MAX_REF_HOPS`.\n * The caller treats `undefined` as \"give up, leave as a permissive schema\".\n */\nfunction resolveRef(root: Record<string, unknown>, ref: string, hops = 0): Record<string, unknown> | undefined {\n if (hops > MAX_REF_HOPS)\n return undefined\n if (!ref.startsWith('#/'))\n return undefined\n\n const parts = ref.slice(2).split('/').map(decodeRefSegment)\n let cursor: unknown = root\n for (const part of parts) {\n if (!isPlainObject(cursor))\n return undefined\n cursor = cursor[part]\n }\n if (!isPlainObject(cursor))\n return undefined\n\n if (typeof cursor.$ref === 'string')\n return resolveRef(root, cursor.$ref, hops + 1)\n return cursor\n}\n\nfunction decodeRefSegment(seg: string): string {\n return seg.replace(/~1/g, '/').replace(/~0/g, '~')\n}\n\ninterface SanitizeCtx {\n root: Record<string, unknown>\n warnings: string[]\n profile: SchemaSanitizeProfile\n prefix: string\n}\n\n/**\n * Recursively sanitize a sub-schema. Returns the **original** reference\n * when nothing needed to change — every turn's `formatTools` re-runs the\n * sanitizer, and the no-alloc fast path keeps the hot loop cheap when\n * the registered tool set is already clean. Returns a fresh object when\n * any rewrite happened; never mutates the input.\n *\n * The dirty-tracking is local to each frame so a clean sub-tree under a\n * dirty parent doesn't get cloned uselessly.\n */\nfunction sanitizeNode(node: unknown, ctx: SanitizeCtx, depth: number, path: string): Record<string, unknown> {\n if (depth > MAX_DEPTH) {\n ctx.warnings.push(`${ctx.prefix}schema nested deeper than ${MAX_DEPTH} levels at ${path || '$'} — replaced with permissive {}`)\n return {}\n }\n if (!isPlainObject(node))\n return {}\n\n let dirty = false\n let working: Record<string, unknown> = node\n\n // Inline `$ref` first so subsequent passes see the resolved shape.\n // Sibling keys (per JSON Schema 2019-09+ semantics) win over the\n // referenced definition. Unresolvable refs collapse to whatever\n // siblings remained so the surrounding schema still validates.\n if (typeof working.$ref === 'string') {\n const ref = working.$ref\n const resolved = resolveRef(ctx.root, ref)\n const { $ref: _drop, ...rest } = working\n if (resolved) {\n ctx.warnings.push(`${ctx.prefix}inlined $ref \"${ref}\" at ${path || '$'}`)\n working = { ...resolved, ...rest }\n }\n else {\n ctx.warnings.push(`${ctx.prefix}dropped unresolvable $ref \"${ref}\" at ${path || '$'}`)\n working = rest\n }\n dirty = true\n }\n\n // OpenAPI 3.0 `nullable` → JSON-Schema `type: [..., 'null']`. Modern\n // providers don't recognise `nullable`; Anthropic in particular ignores\n // it silently, which lets the model emit `null` and then the validator\n // complains the value isn't the declared type. Translation keeps the\n // intent explicit on the wire.\n if (working.nullable === true) {\n if (!dirty) {\n working = { ...working }\n dirty = true\n }\n const t = working.type\n if (typeof t === 'string') {\n working.type = [t, 'null']\n ctx.warnings.push(`${ctx.prefix}converted nullable:true → type:[${t},null] at ${path || '$'}`)\n }\n else if (Array.isArray(t) && !t.includes('null')) {\n working.type = [...t, 'null']\n ctx.warnings.push(`${ctx.prefix}converted nullable:true → type:[…,null] at ${path || '$'}`)\n }\n else if (Array.isArray(t)) {\n // Redundant — type already includes 'null'. Silent strip keeps the\n // wire shape clean without surfacing a warning the caller can't act on.\n }\n else {\n ctx.warnings.push(`${ctx.prefix}stripped nullable:true at ${path || '$'} (no base type)`)\n }\n delete working.nullable\n }\n\n // Sub-schema recursion. Sweep every key that JSON Schema 2020-12\n // reserves for nested schemas; leave annotation keywords (`title`,\n // `description`, `examples`, `default`, ...) and validation keywords\n // (`minimum`, `pattern`, ...) alone.\n for (const key of SUBSCHEMA_KEYS) {\n const child = working[key]\n if (isPlainObject(child)) {\n const sanitized = sanitizeNode(child, ctx, depth + 1, `${path}/${key}`)\n if (sanitized !== child) {\n if (!dirty) {\n working = { ...working }\n dirty = true\n }\n working[key] = sanitized\n }\n }\n }\n\n for (const key of SUBSCHEMA_RECORD_KEYS) {\n const rec = working[key]\n if (isPlainObject(rec)) {\n let recDirty = false\n let out: Record<string, unknown> = rec\n for (const [k, v] of Object.entries(rec)) {\n const sanitized = sanitizeNode(v, ctx, depth + 1, `${path}/${key}/${k}`)\n if (sanitized !== v) {\n if (!recDirty) {\n out = { ...rec }\n recDirty = true\n }\n out[k] = sanitized\n }\n }\n if (recDirty) {\n if (!dirty) {\n working = { ...working }\n dirty = true\n }\n working[key] = out\n }\n }\n }\n\n for (const key of SUBSCHEMA_ARRAY_KEYS) {\n const arr = working[key]\n if (Array.isArray(arr)) {\n let arrDirty = false\n let out: unknown[] = arr\n for (let i = 0; i < arr.length; i++) {\n const sanitized = sanitizeNode(arr[i], ctx, depth + 1, `${path}/${key}/${i}`)\n if (sanitized !== arr[i]) {\n if (!arrDirty) {\n out = [...arr]\n arrDirty = true\n }\n out[i] = sanitized\n }\n }\n if (arrDirty) {\n if (!dirty) {\n working = { ...working }\n dirty = true\n }\n working[key] = out\n }\n }\n }\n\n return working\n}\n\n/**\n * Apply root-level coercions every supported provider expects:\n *\n * - `type === 'object'` (Anthropic requires it; OpenAI tolerates more\n * but rejects unions at root in strict mode).\n * - `properties` is a plain object (Anthropic 400s when missing).\n * - No root-level `$ref` / `oneOf` / `anyOf` / `allOf` (Anthropic rejects;\n * OpenAI behaviour varies by endpoint).\n * - `$defs` / `definitions` stripped (recursive pass already inlined\n * refs; the metaschema storage is dead weight at the wire layer).\n *\n * Applied for every profile because portability matters more than the\n * marginal permissiveness of any one host. Profile-specific extras\n * (currently `$schema` stripping on `anthropic`) are gated below.\n */\nfunction enforceRoot(schema: Record<string, unknown>, ctx: SanitizeCtx): Record<string, unknown> {\n // Decide what the final shape needs first, then materialise a clone\n // only when at least one change is required. The recursive pass calls\n // this last, so a fully-clean schema reaches the wire as the exact\n // reference the consumer passed in.\n let out: Record<string, unknown> = schema\n let dirty = false\n const dirtyOnce = (): void => {\n if (!dirty) {\n out = { ...schema }\n dirty = true\n }\n }\n\n const t = out.type\n if (Array.isArray(t)) {\n if (t.includes('object')) {\n dirtyOnce()\n out.type = 'object'\n if (t.length > 1)\n ctx.warnings.push(`${ctx.prefix}collapsed root type:[${t.join(',')}] to 'object'`)\n }\n else {\n dirtyOnce()\n ctx.warnings.push(`${ctx.prefix}root type:[${t.join(',')}] does not include 'object' — coerced to 'object'`)\n out.type = 'object'\n }\n }\n else if (typeof t === 'string' && t !== 'object') {\n dirtyOnce()\n ctx.warnings.push(`${ctx.prefix}coerced root type:'${t}' → 'object'`)\n out.type = 'object'\n }\n else if (t === undefined) {\n dirtyOnce()\n out.type = 'object'\n }\n\n // Anthropic + OpenAI both require `properties` on object schemas — even\n // when empty. Synthesise the object so the wire shape is well-formed.\n if (!isPlainObject(out.properties)) {\n if ('properties' in out)\n ctx.warnings.push(`${ctx.prefix}replaced non-object 'properties' at root with {}`)\n dirtyOnce()\n out.properties = {}\n }\n\n for (const key of ['oneOf', 'anyOf', 'allOf'] as const) {\n if (key in out) {\n dirtyOnce()\n ctx.warnings.push(`${ctx.prefix}stripped root '${key}' (providers require a single object schema)`)\n delete out[key]\n }\n }\n\n if ('$ref' in out) {\n dirtyOnce()\n ctx.warnings.push(`${ctx.prefix}stripped root $ref`)\n delete out.$ref\n }\n\n if ('$defs' in out) {\n dirtyOnce()\n delete out.$defs\n }\n if ('definitions' in out) {\n dirtyOnce()\n delete out.definitions\n }\n\n if (ctx.profile === 'anthropic' && '$schema' in out) {\n dirtyOnce()\n delete out.$schema\n }\n\n return out\n}\n\n/**\n * Sanitize a single tool's `inputSchema` for safe forwarding to the\n * provider. Returns the rewritten schema + a list of warnings describing\n * everything that changed.\n *\n * Never mutates the input. Returns the **same reference** when no rewrite\n * was needed (clean-schema fast path) — `sanitizeToolSpecs` relies on\n * this to keep the formatTools hot loop allocation-free across turns\n * when the registered tool set is already wire-valid.\n */\nexport function sanitizeToolSchema(\n input: unknown,\n options: SchemaSanitizeOptions = {},\n): SchemaSanitizeResult {\n const profile: SchemaSanitizeProfile = options.profile ?? 'permissive'\n const prefix = options.toolName ? `[tool:${options.toolName}] ` : ''\n\n // Non-object inputs always allocate a fresh `{}` root — there's no\n // possible identity to preserve, and the wire still needs a valid object\n // schema either way. `sanitizeToolSpecs` won't hit its no-alloc path for\n // these, which is fine; the model has nothing useful to call on a\n // malformed inputSchema anyway.\n if (!isPlainObject(input)) {\n return {\n schema: { type: 'object', properties: {} },\n warnings: [],\n }\n }\n\n const ctx: SanitizeCtx = {\n root: input,\n warnings: [],\n profile,\n prefix,\n }\n\n const recursed = sanitizeNode(input, ctx, 0, '')\n const enforced = enforceRoot(recursed, ctx)\n\n return { schema: enforced, warnings: ctx.warnings }\n}\n\n/**\n * Convenience: sanitize a batch of tools and emit a single de-duped\n * `console.warn` per unique warning line. Returns the rewritten tools\n * preserving original ordering and reference identity for clean schemas\n * (no reallocation when nothing needed to change).\n *\n * The sanitiser runs every request, so log noise from a stable bad\n * schema would multiply across turns; the de-dupe keeps the signal\n * useful in production logs without dropping the first occurrence.\n *\n * Final name guard: a tool whose `name` violates the provider constraint\n * is DROPPED (not throwingly forwarded) with a one-time warning. An invalid\n * name 400s the entire `messages.create` — taking every other tool and the\n * turn down with it — so silently degrading one capability is the strictly\n * safer failure mode. Upstream aliasing already coerces MCP names to valid\n * wire names, so in practice this only fires for names that cannot be\n * represented at all; it's defense-in-depth that guarantees a malformed\n * name can never reach the wire, regardless of how it got into the list.\n */\nexport function sanitizeToolSpecs<T extends { name: string, inputSchema?: unknown }>(\n tools: readonly T[],\n options: { profile?: SchemaSanitizeProfile, onWarning?: (line: string) => void } = {},\n): T[] {\n const profile: SchemaSanitizeProfile = options.profile ?? 'permissive'\n const warn = options.onWarning ?? defaultWarn\n const seen = new Set<string>()\n const emitOnce = (line: string): void => {\n if (seen.has(line))\n return\n seen.add(line)\n warn(line)\n }\n\n const out: T[] = []\n for (const tool of tools) {\n if (!isValidToolName(tool.name, profile)) {\n emitOnce(\n `dropped tool with invalid name \"${clipToolName(tool.name)}\" — `\n + `must match ^[A-Za-z0-9_-]{1,${maxToolNameLen(profile)}}$ for the ${profile} provider`,\n )\n continue\n }\n const result = sanitizeToolSchema(tool.inputSchema, { profile, toolName: tool.name })\n if (result.warnings.length === 0 && result.schema === tool.inputSchema) {\n out.push(tool)\n continue\n }\n for (const line of result.warnings)\n emitOnce(line)\n out.push({ ...tool, inputSchema: result.schema })\n }\n return out\n}\n\n/** Clip an over-long / weird name so a warning line stays readable in logs. */\nfunction clipToolName(name: string): string {\n return name.length > 80 ? `${name.slice(0, 80)}…` : name\n}\n\nfunction defaultWarn(line: string): void {\n console.warn(`[zidane:schema] ${line}`)\n}\n","/**\n * Tolerant parsing for model-emitted tool-call arguments.\n *\n * Tool-call argument JSON is accumulated from many stream deltas, so the\n * failure modes at end-of-stream are characteristic: TRUNCATION (network\n * hiccup, provider crash, max-tokens mid-call — the buffer ends inside a\n * string or with unclosed brackets) and TRAILING GARBAGE (a gateway or model\n * appends bytes after the closing brace). A strict `JSON.parse` turns either\n * into a turn-fatal `SyntaxError` (\"Expected ',' or '}' after property value\n * …\", \"Unexpected end of JSON input\") even when the intended object is\n * trivially recoverable.\n *\n * {@link parseToolCallArguments} tries strict parsing first — byte-exact\n * payloads behave exactly as before — then a bounded repair pass. Repair\n * never invents structure: it only completes what the payload already\n * started (close the open string, close open brackets in stack order, drop a\n * dangling partial token or trailing garbage). Unrepairable payloads return\n * `undefined` so callers keep their existing hard-failure contract.\n */\n\nexport interface ParsedToolArguments {\n input: Record<string, unknown>\n /** True when the payload only parsed after repair (truncated/garbled). */\n repaired: boolean\n}\n\n/**\n * Parse a tool-call arguments payload, repairing truncation and trailing\n * garbage when strict parsing fails. Returns `undefined` when no bounded\n * repair produces valid JSON.\n */\nexport function parseToolCallArguments(raw: string): ParsedToolArguments | undefined {\n try {\n const parsed = JSON.parse(raw) as unknown\n // Arguments are an object by contract — same bar the repair path holds\n // its candidates to below. Providers emit the literal strings `null` /\n // `\"…\"` / `[…]` for degenerate calls (openai-compat vendors send `null`\n // for no-arg tools), and strict-parsing those straight into `input`\n // hands the loop a non-object that explodes later on property access.\n // A clean parse that violates the contract falls through to repair: no\n // candidate starts without `{`, so these return `undefined` and the\n // caller's existing hard-failure contract kicks in.\n if (typeof parsed === 'object' && parsed !== null && !Array.isArray(parsed))\n return { input: parsed as Record<string, unknown>, repaired: false }\n }\n catch {\n // fall through to repair\n }\n\n for (const { candidate, fabricated } of repairCandidates(raw)) {\n try {\n const parsed = JSON.parse(candidate) as unknown\n // Arguments are an object by contract; a repair that collapses to a\n // scalar/array recovered the wrong thing — keep looking. A FABRICATED\n // closure (structural closers we appended, possibly after stripping\n // tokens) must also retain at least one complete key/value pair —\n // otherwise `{complete nonsense` would \"repair\" to `{}` and run the\n // tool with empty args instead of surfacing the typed stream error. A\n // complete-prefix candidate keeps its parsed value as-is: `{}` there\n // was really present in the payload (empty args + trailing garbage).\n if (typeof parsed === 'object' && parsed !== null && !Array.isArray(parsed)) {\n if (fabricated && Object.keys(parsed).length === 0)\n continue\n return { input: parsed as Record<string, unknown>, repaired: true }\n }\n }\n catch {\n // try the next candidate\n }\n }\n return undefined\n}\n\n/**\n * Bounded, ordered repair candidates:\n * 1. longest complete top-level object prefix (trailing-garbage case)\n * 2. payload + structural closers (truncated mid-string / mid-object)\n * 3. payload minus a dangling partial token, + closers (truncated mid-token)\n *\n * `fabricated` marks candidates whose closure was invented here (2 and 3) —\n * the caller holds those to a stricter acceptance bar than a prefix that was\n * complete in the payload itself.\n */\nfunction* repairCandidates(raw: string): Generator<{ candidate: string, fabricated: boolean }> {\n const text = raw.trim()\n if (!text.startsWith('{'))\n return\n\n const scan = scanJson(text)\n\n if (scan.completeAt !== undefined)\n yield { candidate: text.slice(0, scan.completeAt), fabricated: false }\n\n yield { candidate: close(text, scan), fabricated: true }\n\n // Drop dangling partial tokens (`\"b`, `tru`, `12.`) and stranded `,`/`:`\n // back toward the last structural boundary, one unit per step, yielding a\n // closed candidate after each strip. Handles `{\"a\": 1, \"b`, `{\"a\": 1,` and\n // `{\"a\": 1, \"b\": tru` shapes that candidate 2 cannot. Each strip is\n // SCAN-AWARE: the unterminated-string strip is only legal when the scanner\n // says the text actually ends inside a string — otherwise a closing quote\n // would be misread as an opener and eat a whole `\": value,` tail. Bounded:\n // each step must shrink the text, and a dangling key needs at most 4\n // strips (value token, `:`, key string, `,`).\n let trimmed = text\n for (let step = 0; step < 6; step++) {\n const next = (scanJson(trimmed).inString\n ? trimmed.replace(/\"(?:[^\"\\\\]|\\\\.)*$/, '')\n : trimmed.replace(/(?:\"(?:[^\"\\\\]|\\\\.)*\"|[^\\s{}[\\],:\"]+|[,:])\\s*$/, '')\n ).trimEnd()\n if (next === trimmed || !next.startsWith('{'))\n break\n trimmed = next\n yield { candidate: close(trimmed, scanJson(trimmed)), fabricated: true }\n }\n}\n\ninterface JsonScan {\n /** Open-bracket stack, outermost first. */\n stack: Array<'}' | ']'>\n /** True when the payload ends inside a string literal. */\n inString: boolean\n /** End offset (exclusive) of the complete top-level value, when it closed. */\n completeAt?: number\n}\n\nfunction scanJson(text: string): JsonScan {\n const stack: Array<'}' | ']'> = []\n let inString = false\n let escaped = false\n let completeAt: number | undefined\n for (let i = 0; i < text.length; i++) {\n const ch = text[i]\n if (inString) {\n if (escaped)\n escaped = false\n else if (ch === '\\\\')\n escaped = true\n else if (ch === '\"')\n inString = false\n continue\n }\n if (ch === '\"') {\n inString = true\n }\n else if (ch === '{') {\n stack.push('}')\n }\n else if (ch === '[') {\n stack.push(']')\n }\n else if (ch === '}' || ch === ']') {\n stack.pop()\n if (stack.length === 0 && completeAt === undefined)\n completeAt = i + 1\n }\n }\n return { stack, inString, completeAt }\n}\n\nfunction close(text: string, scan: JsonScan): string {\n let out = text\n if (scan.inString) {\n // A trailing lone backslash would escape the closing quote.\n if (/(?:^|[^\\\\])(?:\\\\\\\\)*\\\\$/.test(out))\n out = out.slice(0, -1)\n out += '\"'\n }\n for (let i = scan.stack.length - 1; i >= 0; i--)\n out += scan.stack[i]\n return out\n}\n","import type { AliasMaps } from '../aliasing'\nimport type { SessionMessage } from '../types'\nimport { buildAliasMaps, buildSanitizedWireAliases, rewriteMessagesToWire, toCanonicalName, toWireName } from '../aliasing'\n\n/**\n * Request-local tool-name boundary — provider-agnostic.\n *\n * Normal agent runs already alias third-party MCP names before calling a\n * provider. Direct provider callers do not — notably compaction, which replays\n * historical tool calls with `tools: []` — and REPLAYED HISTORY can carry\n * names the current alias maps know nothing about (sessions imported from\n * other clients, renamed MCP servers, removed tools). A legacy name containing\n * a space/dot/unicode character or exceeding 64 chars then reaches the wire\n * (`input[].name` on Responses, `tools.N.custom.name` / a historical\n * `messages.N.content.M.toolUse.name` on Anthropic-family endpoints including\n * Bedrock, whose Converse API validates history strictly) and 400s the entire\n * request.\n *\n * This boundary gathers names from BOTH history and current tool definitions,\n * lets the caller add request-local aliases against that full name set (a\n * provider moving a name the backend reserves), reserves every remaining valid\n * name, and deterministically aliases invalid names through the same\n * collision-resistant sanitizer used by the agent loop. It rewrites a request\n * copy only: persisted messages remain canonical. Returned tool calls are\n * restored to the caller's input name so the outer loop can still apply its own\n * wire→canonical aliases normally. Clean setups return `changed: false` with\n * byte-identical content, so the provider prompt cache is unaffected.\n */\nexport interface ToolNameBoundary {\n messages: SessionMessage[]\n /** Input/caller name → request-wire name. */\n toWire: (name: string) => string\n /** Request-wire name → input/caller name. */\n fromWire: (name: string) => string\n /** True when at least one historical/spec name needed rewriting. */\n changed: boolean\n}\n\nexport function createToolNameBoundary(\n messages: readonly SessionMessage[],\n currentToolNames: Iterable<string> = [],\n /**\n * Provider-specific aliases, resolved against every name in the request so\n * the caller can pick a target that is provably free. Returning `undefined`\n * (the default) keeps the request byte-identical.\n */\n resolveRequestAliases?: (names: ReadonlySet<string>) => Record<string, string> | undefined,\n): ToolNameBoundary {\n const names = new Set(currentToolNames)\n for (const message of messages) {\n for (const block of message.content) {\n if (block.type === 'tool_call')\n names.add(block.name)\n }\n }\n\n const requestAliases = resolveRequestAliases?.(names)\n const sanitizedAliases = buildSanitizedWireAliases(names, requestAliases)\n const aliases = requestAliases || sanitizedAliases\n ? { ...requestAliases, ...sanitizedAliases }\n : undefined\n const maps: AliasMaps = buildAliasMaps(aliases, names)\n return {\n messages: rewriteMessagesToWire([...messages], maps),\n toWire: name => toWireName(name, maps),\n fromWire: name => toCanonicalName(name, maps),\n changed: maps.aliasByCanonical.size > 0,\n }\n}\n","/**\n * OpenAI-compatible provider factory + shared utilities.\n *\n * `openaiCompat(params)` returns a `Provider` that talks to any OpenAI-compatible\n * HTTP endpoint (OpenRouter, Cerebras, Baseten, Fireworks, Groq, local LM servers, ...).\n * Helpers (`consumeSSE`, `toOAIMessages`, ...) are shared by the bespoke `openrouter`\n * and `cerebras` wrappers, which pin defaults on top of this factory.\n */\n\nimport type { Provider, ProviderCapabilities, StreamCallbacks, StreamOptions, ToolResult, ToolSpec, TurnResult } from '.'\nimport type { ClassifiedError } from '../errors'\nimport type { CacheTtl, PromptPart, SessionContentBlock, SessionMessage, ThinkingLevel, ToolResultContent, ToolResultDocumentContent, TurnFinishReason } from '../types'\nimport type { PromptCacheKeyOption } from './prompt-cache-key'\nimport { estimateTokens } from '../compact/utils'\nimport { classifyErrorPrelude, isRetryableHttpStatus, matchesContextExceeded, matchesToolPairingError } from '../errors'\nimport { unsupportedMediaError } from '../prompt'\nimport { renderSystemForWire, splitSystemPrompt } from '../system-prompt'\nimport { audioFormatFromMediaType } from '../tools/media-sniff'\nimport { assertResolvedMediaBlock, documentBlockMarker } from '../types'\nimport { fillEstimatedCost } from './cost'\nimport { resolvePromptCacheKey } from './prompt-cache-key'\nimport { sanitizeToolSpecs } from './schema-sanitize'\nimport { parseToolCallArguments } from './tool-args'\nimport { createToolNameBoundary } from './tool-name-boundary'\n\n// ---------------------------------------------------------------------------\n// OpenAI-compatible types\n// ---------------------------------------------------------------------------\n\nexport interface OAIMessage {\n role: 'system' | 'user' | 'assistant' | 'tool'\n content?: unknown\n tool_calls?: { id: string, type: 'function', function: { name: string, arguments: string } }[]\n tool_call_id?: string\n /**\n * OpenRouter normalized reasoning envelope. Echoed back unmodified on assistant\n * messages so the gateway can resume an extended-reasoning chain on the\n * upstream route. Ignored by hosts that don't speak the envelope.\n */\n reasoning_details?: unknown[]\n /**\n * Plain reasoning string echoed back on assistant messages for vLLM-style\n * reasoning models (Arcee Trinity-Thinking, DeepSeek-R1). Arcee's API accepts\n * `reasoning_content`; some self-hosted vLLM versions read `reasoning`.\n */\n reasoning_content?: string\n reasoning?: string\n}\n\nexport interface OAITool {\n type: 'function'\n function: { name: string, description: string, parameters: Record<string, unknown> }\n}\n\n// Sentinel tags zidane uses to round-trip tool_calls / tool_results through\n// the OpenAI-compat session shape. `toOpenAI` writes them; `fromOpenAI` reads\n// them back. The `__zidane_` prefix avoids collisions with any literal `_tag`\n// value a host might emit on real wire messages.\nexport const TOOL_RESULTS_TAG = '__zidane_tool_results__'\nexport const ASSISTANT_TOOL_CALLS_TAG = '__zidane_assistant_tc__'\n\n// ---------------------------------------------------------------------------\n// SSE stream parser\n// ---------------------------------------------------------------------------\n\n/**\n * Ceiling on how many UTF-16 code units may accumulate between two newline\n * boundaries when parsing an SSE stream. A broken or malicious server that\n * emits an unbounded stream of non-newline bytes would otherwise grow `buffer`\n * without end and eventually OOM the host. Measured in JS string length (UTF-16\n * units, 8M units ≈ 8–24 MB of UTF-8 input) rather than bytes — close enough\n * for a pathological-stream tripwire without paying `Buffer.byteLength` on\n * every chunk. Safely above the largest tool-call arg JSON we reasonably\n * expect.\n */\nconst SSE_MAX_BUFFER_LENGTH = 8 * 1024 * 1024\n\nexport class OpenAICompatStreamError extends Error {\n constructor(message: string) {\n super(message)\n this.name = 'OpenAICompatStreamError'\n }\n}\n\ninterface OpenAICompatUsage {\n input: number\n output: number\n cost?: number\n cacheRead?: number\n cacheCreation?: number\n}\n\n/**\n * Flatten a chunk-list `content` delta into ordered text runs tagged as\n * reasoning or answer text. Mistral's shape is\n * `[{ type: 'thinking', thinking: [{ type: 'text', text }] }]` for reasoning\n * and `[{ type: 'text', text }]` for plain text; anything unrecognized is\n * skipped rather than string-coerced into the output.\n */\nfunction flattenContentChunks(parts: readonly unknown[]): { kind: 'thinking' | 'text', text: string }[] {\n const out: { kind: 'thinking' | 'text', text: string }[] = []\n for (const part of parts) {\n if (typeof part === 'string') {\n if (part)\n out.push({ kind: 'text', text: part })\n continue\n }\n if (!part || typeof part !== 'object')\n continue\n const record = part as Record<string, unknown>\n if (record.type === 'thinking') {\n const inner = Array.isArray(record.thinking) ? record.thinking : []\n for (const run of flattenContentChunks(inner))\n out.push({ kind: 'thinking', text: run.text })\n continue\n }\n if (typeof record.text === 'string' && record.text)\n out.push({ kind: 'text', text: record.text })\n }\n return out\n}\n\nfunction usageFromChunk(chunk: Record<string, unknown>): OpenAICompatUsage | null {\n const chunkUsage = chunk.usage as\n | {\n prompt_tokens?: number\n completion_tokens?: number\n total_cost?: number\n cache_discount?: number\n prompt_tokens_details?: {\n cached_tokens?: number\n cache_creation_input_tokens?: number\n cache_write_tokens?: number\n }\n cache_creation_input_tokens?: number\n }\n | undefined\n\n if (chunkUsage) {\n const cachedRead = chunkUsage.prompt_tokens_details?.cached_tokens\n // OpenRouter surfaces cache writes via `prompt_tokens_details.cache_creation_input_tokens`\n // (Anthropic passthrough) or `cache_write_tokens` (normalized form). Either is cumulative\n // for the request, so pick whichever the route populated.\n const detailsWrite = chunkUsage.prompt_tokens_details?.cache_creation_input_tokens\n ?? chunkUsage.prompt_tokens_details?.cache_write_tokens\n const cachedWrite = detailsWrite ?? chunkUsage.cache_creation_input_tokens\n // `TurnUsage.input` is Anthropic-shaped: FRESH tokens only, cache\n // traffic broken out (the context gauge and `fillEstimatedCost` both\n // sum the buckets). OpenAI's `prompt_tokens` is the opposite — it\n // INCLUDES everything reported under `prompt_tokens_details` — so the\n // detail buckets must be subtracted here. Without this, cached tokens\n // were double-counted in the context gauge AND double-priced by the\n // estimator (full input rate + cacheRead rate), inflating displayed\n // costs by nearly the whole cache-hit volume on long sessions.\n // The TOP-LEVEL `cache_creation_input_tokens` fallback keeps\n // Anthropic's additive semantics (not part of `prompt_tokens`), so\n // only detail-carried writes are subtracted.\n const includedInPrompt = (cachedRead ?? 0) + (detailsWrite ?? 0)\n return {\n input: Math.max(0, (chunkUsage.prompt_tokens ?? 0) - includedInPrompt),\n output: chunkUsage.completion_tokens ?? 0,\n cost: chunkUsage.total_cost ?? undefined,\n ...(typeof cachedRead === 'number' && cachedRead > 0 ? { cacheRead: cachedRead } : {}),\n ...(typeof cachedWrite === 'number' && cachedWrite > 0 ? { cacheCreation: cachedWrite } : {}),\n }\n }\n\n // Ollama's native streaming final chunk reports usage with these field names.\n // Some gateways expose that shape even when clients use an OpenAI-compatible\n // route, so accept it here before falling back to local estimates.\n const promptEvalCount = chunk.prompt_eval_count\n const evalCount = chunk.eval_count\n if (typeof promptEvalCount === 'number' || typeof evalCount === 'number') {\n return {\n input: typeof promptEvalCount === 'number' ? promptEvalCount : 0,\n output: typeof evalCount === 'number' ? evalCount : 0,\n }\n }\n\n return null\n}\n\nexport async function consumeSSE(\n response: Response,\n callbacks: StreamCallbacks,\n signal?: AbortSignal,\n) {\n // A null body (e.g. a 204, or a proxy that returns headers but no stream)\n // would otherwise NPE on `.getReader()` with an opaque TypeError.\n if (!response.body) {\n throw new OpenAICompatStreamError(\n 'Streaming response had no body — upstream returned an empty stream (status '\n + `${response.status} ${response.statusText}).`,\n )\n }\n const reader = response.body.getReader()\n const decoder = new TextDecoder()\n let buffer = ''\n let text = ''\n let thinking = ''\n let finishReason = 'stop'\n // True once the stream signalled orderly completion (an explicit\n // `finish_reason` or `[DONE]`). Gates tool-arg repair below: repairing is\n // safe for model-mangled JSON on a cleanly terminated stream, but a stream\n // that just stopped mid-arguments should stay a retryable error — the retry\n // gets the COMPLETE args, whereas a silent repair executes partial ones.\n let sawTerminator = false\n let usage: OpenAICompatUsage = { input: 0, output: 0 }\n const tcMap = new Map<number, { id: string, name: string, args: string }>()\n // Per-stream nonce mixed into fabricated tool-call ids. A bare\n // `call_${index}` is stable ACROSS turns — when a host omits ids on every\n // turn, turn N's `call_0` collides with turn 1's, the pairing pass strips\n // the \"duplicate\" tool_use plus its result, and the model loops on\n // `[Tool use interrupted]` forever (the Kimi/Fireworks pairing-storm\n // signature). The nonce keeps fabricated ids unique per request.\n const idNonce = Math.random().toString(36).slice(2, 10)\n // OpenRouter `reasoning_details` deltas, keyed by item index. Items are merged\n // field-by-field: text/summary concatenate, signature/data/format/id/type are\n // last-write-wins (they only appear once per item in practice).\n const reasoningMap = new Map<number, Record<string, unknown>>()\n let sawReasoningDetails = false\n\n try {\n while (true) {\n // Abort can land either between reads (caught here) or mid-`read()`\n // (fetch rejects with AbortError). Throw the same AbortError shape on\n // this path so both converge on `classifyOpenAICompatError` →\n // `{ kind: 'aborted' }` instead of returning partial content as a\n // normal result depending on timing.\n if (signal?.aborted)\n throw new DOMException('The operation was aborted.', 'AbortError')\n const { done, value } = await reader.read()\n if (done)\n break\n\n buffer += decoder.decode(value, { stream: true })\n // Cap the line-less buffer. Any single event must fit under the ceiling.\n if (buffer.length > SSE_MAX_BUFFER_LENGTH) {\n throw new OpenAICompatStreamError(\n `SSE buffer exceeded ${SSE_MAX_BUFFER_LENGTH} characters without a line boundary — upstream may be streaming non-SSE data.`,\n )\n }\n const lines = buffer.split('\\n')\n buffer = lines.pop() || ''\n\n for (const line of lines) {\n // Spec allows both `data:` and `data: ` — the `.trim()` below\n // absorbs the optional single space after the colon.\n if (!line.startsWith('data:'))\n continue\n const data = line.slice(5).trim()\n if (data === '[DONE]') {\n sawTerminator = true\n continue\n }\n\n let chunk: Record<string, unknown>\n try {\n chunk = JSON.parse(data) as Record<string, unknown>\n }\n catch {\n // Malformed JSON frame — skip (SSE keepalives, injected comments, etc.)\n continue\n }\n\n const parsedUsage = usageFromChunk(chunk)\n if (parsedUsage)\n usage = parsedUsage\n\n const choices = chunk.choices as Array<Record<string, unknown>> | undefined\n const choice = choices?.[0]\n if (!choice)\n continue\n const fr = choice.finish_reason as string | undefined\n if (fr) {\n finishReason = fr\n sawTerminator = true\n }\n\n const delta = choice.delta as Record<string, unknown> | undefined\n\n // OpenRouter normalized reasoning envelope. Items arrive as deltas keyed\n // by `index`; merge into a stable list and stream the visible text portion\n // through `onThinking` so the UI sees reasoning live.\n const reasoningDeltaArr = delta?.reasoning_details as Array<Record<string, unknown>> | undefined\n if (reasoningDeltaArr && reasoningDeltaArr.length > 0) {\n sawReasoningDetails = true\n for (const item of reasoningDeltaArr) {\n const idx = (typeof item.index === 'number' ? item.index : 0)\n const existing = reasoningMap.get(idx) ?? {}\n // Concatenate text/summary across deltas.\n if (typeof item.text === 'string') {\n existing.text = ((existing.text as string | undefined) ?? '') + item.text\n thinking += item.text\n callbacks.onThinking?.(item.text)\n }\n if (typeof item.summary === 'string') {\n existing.summary = ((existing.summary as string | undefined) ?? '') + item.summary\n thinking += item.summary\n callbacks.onThinking?.(item.summary)\n }\n // Last-write-wins for opaque fields.\n for (const key of ['type', 'signature', 'data', 'format', 'id'] as const) {\n const v = item[key]\n if (typeof v === 'string')\n existing[key] = v\n }\n reasoningMap.set(idx, existing)\n }\n }\n\n // Legacy text-only reasoning fields (DeepSeek `reasoning_content`,\n // pre-`reasoning_details` OpenRouter, Qwen-thinking, …). Suppressed\n // when the structured envelope is in flight to avoid double-counting\n // the same reasoning text into the `thinking` buffer.\n if (!sawReasoningDetails) {\n const thinkingDelta = (delta?.reasoning_content ?? delta?.reasoning) as string | undefined\n if (thinkingDelta) {\n thinking += thinkingDelta\n callbacks.onThinking?.(thinkingDelta)\n }\n }\n\n const contentDelta = delta?.content as string | unknown[] | undefined\n if (typeof contentDelta === 'string' && contentDelta) {\n text += contentDelta\n callbacks.onText(contentDelta)\n }\n // Mistral streams `delta.content` as a chunk LIST during the thinking\n // phase (`{ type: 'thinking', thinking: [{ type: 'text', text }] }`)\n // and as a plain string during the answer phase. Route thinking chunks\n // to the reasoning buffer and text chunks to the text buffer —\n // otherwise the array string-coerces into `[object Object]` output.\n else if (Array.isArray(contentDelta)) {\n for (const part of flattenContentChunks(contentDelta)) {\n if (part.kind === 'thinking') {\n thinking += part.text\n callbacks.onThinking?.(part.text)\n }\n else {\n text += part.text\n callbacks.onText(part.text)\n }\n }\n }\n\n const toolCallsDelta = delta?.tool_calls as Array<{\n index: number\n id?: string\n function?: { name?: string, arguments?: string }\n }> | undefined\n if (toolCallsDelta) {\n // Liveness signal for tool-only turns (no text/thinking deltas) so\n // the loop's first-response watchdog doesn't abort a stream that's\n // actively emitting tool-call argument deltas. (B2)\n if (toolCallsDelta.length > 0)\n callbacks.onToolCallDelta?.()\n for (const tc of toolCallsDelta) {\n const existing = tcMap.get(tc.index)\n if (existing) {\n // Some hosts deliver `id` / `function.name` on a later delta\n // than the first one for an index — backfill them so the call\n // doesn't keep the fabricated nonce id / empty name.\n if (tc.id)\n existing.id = tc.id\n existing.name ||= tc.function?.name ?? ''\n if (tc.function?.arguments)\n existing.args += tc.function.arguments\n }\n else {\n tcMap.set(tc.index, {\n id: tc.id || `call_${tc.index}_${idNonce}`,\n name: tc.function?.name || '',\n args: tc.function?.arguments || '',\n })\n }\n }\n }\n }\n }\n }\n catch (err) {\n // On a non-abort error (SSE buffer overflow, transport crash), cancel\n // the reader so the underlying HTTP body is actually terminated rather\n // than left half-consumed (a bare releaseLock() leaks the socket until\n // GC). Aborts already tear the body down via the fetch signal, so skip\n // the redundant cancel there.\n const isAbort = err instanceof Error && err.name === 'AbortError'\n if (!isAbort)\n await reader.cancel().catch(() => {})\n throw err\n }\n finally {\n reader.releaseLock()\n }\n\n // Tool call JSON is accumulated across many deltas. If a gateway appends\n // garbage or the model emits mangled JSON on a cleanly terminated stream,\n // `tc.args` won't strict-parse — `parseToolCallArguments` repairs the\n // recoverable shapes (see its doc). HARD truncation (no terminator seen)\n // deliberately stays a typed, retryable error even when the fragment is\n // repairable: the retry fetches the complete arguments, whereas a silent\n // repair would execute partial ones.\n const clipForError = (args: string): string =>\n args.length > 200 ? `${args.slice(0, 200)}… [${args.length} chars]` : args\n const toolCalls: Array<{ id: string, name: string, input: Record<string, unknown> }> = []\n for (const tc of tcMap.values()) {\n if (!tc.args) {\n toolCalls.push({ id: tc.id, name: tc.name, input: {} })\n continue\n }\n const parsed = parseToolCallArguments(tc.args)\n if (!parsed) {\n throw new OpenAICompatStreamError(\n `Tool call \"${tc.name}\" (${tc.id}) arguments were truncated or malformed beyond repair: ${clipForError(tc.args)}`,\n )\n }\n if (parsed.repaired && !sawTerminator) {\n throw new OpenAICompatStreamError(\n `Tool call \"${tc.name}\" (${tc.id}) arguments were cut off by a truncated stream: ${clipForError(tc.args)}`,\n )\n }\n toolCalls.push({ id: tc.id, name: tc.name, input: parsed.input })\n }\n\n // Sort by index so `reasoning_details` round-trip preserves item order.\n const reasoningDetails = Array.from(reasoningMap.entries())\n .sort(([a], [b]) => a - b)\n .map(([, item]) => item)\n\n return { text, thinking, toolCalls, finishReason, usage, reasoningDetails }\n}\n\n// ---------------------------------------------------------------------------\n// Message conversion: SessionMessage[] → OAIMessage[]\n// ---------------------------------------------------------------------------\n\n/**\n * Encode a single image block as an OpenAI `image_url` multi-part entry.\n */\nfunction toImageUrlPart(img: { mediaType: string, data: string }) {\n return {\n type: 'image_url' as const,\n image_url: { url: `data:${img.mediaType};base64,${img.data}` },\n }\n}\n\n/**\n * Encode an audio block as an OpenAI-compat `input_audio` part. The `data` is\n * RAW base64 with NO data-URI prefix (unlike images), per the spec.\n */\nfunction toInputAudioPart(audio: { mediaType: string, data: string }) {\n return {\n type: 'input_audio' as const,\n input_audio: { data: audio.data, format: audioFormatFromMediaType(audio.mediaType) },\n }\n}\n\n/**\n * Encode a video block as an OpenAI-compat `video_url` part. Uses the same\n * base64 data-URL shape as images. Only emitted on video-capable routes\n * (currently Gemini via OpenRouter).\n */\nfunction toVideoUrlPart(video: { mediaType: string, data: string }) {\n return {\n type: 'video_url' as const,\n video_url: { url: `data:${video.mediaType};base64,${video.data}` },\n }\n}\n\nfunction documentMarker(doc: ToolResultDocumentContent): string {\n return documentBlockMarker(doc, 'document omitted')\n}\n\n/**\n * Whether a document block can be forwarded natively to OpenAI's `file` content\n * part. Only base64-encoded files qualify (the `file_data` data-URL shape);\n * text-encoded documents are already inlined as attachment text upstream and\n * have no portable binary representation.\n */\nfunction isForwardableDocument(\n doc: { encoding: 'base64' | 'text', mediaType: string },\n): boolean {\n return doc.encoding === 'base64'\n}\n\n/**\n * Encode a base64 document as an OpenAI-compat `file` content part:\n * `{ type: 'file', file: { filename, file_data: 'data:<mime>;base64,<data>' } }`.\n *\n * Supported by OpenAI (Chat Completions + Responses) and routes that proxy it\n * (e.g. OpenRouter → OpenAI/Gemini/Anthropic). Gated behind `capabilities.documents`.\n */\nfunction toFilePart(doc: { mediaType: string, data: string, name?: string }) {\n return {\n type: 'file' as const,\n file: {\n filename: doc.name ?? `file.${doc.mediaType.split('/')[1] ?? 'bin'}`,\n file_data: `data:${doc.mediaType};base64,${doc.data}`,\n },\n }\n}\n\n/** True when any built OAI message carries a `{ type: 'file' }` content part. */\nfunction messagesContainFilePart(messages: OAIMessage[]): boolean {\n return messages.some(m =>\n Array.isArray(m.content)\n && m.content.some(p => typeof p === 'object' && p !== null && (p as { type?: unknown }).type === 'file'),\n )\n}\n\n/**\n * Summarize a `tool_result` output for the companion-user-message path — text blocks\n * are joined (separated by `\\n`) so the tool message carries all textual context; image\n * blocks are collected in a flat list for the companion user message.\n *\n * Used only on the fallback path; the native path walks `output` in-order to preserve\n * text↔image interleaving.\n */\nfunction summarizeToolResultOutput(output: string | ToolResultContent[], documentsEnabled = false): {\n text: string\n images: Array<{ mediaType: string, data: string }>\n audios: Array<{ mediaType: string, data: string }>\n videos: Array<{ mediaType: string, data: string }>\n documents: Array<{ mediaType: string, data: string, name?: string }>\n} {\n if (typeof output === 'string')\n return { text: output, images: [], audios: [], videos: [], documents: [] }\n\n const texts: string[] = []\n const images: Array<{ mediaType: string, data: string }> = []\n const audios: Array<{ mediaType: string, data: string }> = []\n const videos: Array<{ mediaType: string, data: string }> = []\n const documents: Array<{ mediaType: string, data: string, name?: string }> = []\n for (const block of output) {\n if (block.type === 'text') {\n texts.push(block.text)\n }\n else if (block.type === 'image') {\n assertResolvedMediaBlock(block, 'OpenAI-compatible wire messages')\n images.push({ mediaType: block.mediaType, data: block.data })\n }\n else if (block.type === 'audio') {\n audios.push({ mediaType: block.mediaType, data: block.data })\n }\n else if (block.type === 'video') {\n videos.push({ mediaType: block.mediaType, data: block.data })\n }\n else if (block.type === 'document' && documentsEnabled && isForwardableDocument(block)) {\n assertResolvedMediaBlock(block, 'OpenAI-compatible wire messages')\n documents.push({ mediaType: block.mediaType, data: block.data, ...(block.name ? { name: block.name } : {}) })\n }\n else if (block.type === 'document') {\n assertResolvedMediaBlock(block, 'OpenAI-compatible wire messages')\n texts.push(documentMarker(block))\n }\n }\n return { text: texts.join('\\n'), images, audios, videos, documents }\n}\n\n/**\n * Options that influence OpenAI-compat message shaping.\n *\n * `imageInToolResult` defaults to `false`: the loop routes images via a companion\n * user message emitted immediately after the `tool` message. Providers with\n * genuine multi-part tool support can set it to `true` to emit a single\n * multi-part `tool` message instead.\n */\ninterface ToOAIOptions {\n imageInToolResult?: boolean\n /**\n * Route accepts audio input (`input_audio` content parts). When false,\n * audio blocks throw rather than silently dropping bytes a non-audio model\n * would 400 on.\n */\n audio?: boolean\n /**\n * Route accepts video input (`video_url` content parts, base64 data-URL).\n * Currently Gemini-on-OpenRouter. When false, video blocks throw.\n */\n video?: boolean\n /**\n * Route accepts native document/PDF input (OpenAI `file` content part with\n * base64 `file_data`). When false (default), base64 documents degrade to the\n * `[document omitted: …]` text marker. Text-encoded documents are always\n * inlined upstream regardless of this flag.\n */\n documents?: boolean\n /**\n * Host accepts provider-bound reasoning state echoed back on assistant\n * messages. The exact wire shape is selected by `reasoningMode`: OpenRouter's\n * structured `reasoning_details[]` envelope, or vLLM/Arcee's plain\n * `reasoning_content` / `reasoning` string. When `false` (default), stored reasoning blocks\n * are silently dropped to avoid 400s on hosts that strict-validate the schema.\n */\n supportsReasoning?: boolean\n /**\n * Reasoning round-trip shape. `'openrouter'` echoes structured\n * `reasoning_details[]`; `'reasoning_content'` echoes the plain reasoning\n * string (vLLM/Arcee). Only consulted when `supportsReasoning` is `true`.\n * Default: `'openrouter'`.\n */\n reasoningMode?: 'openrouter' | 'reasoning_content'\n /**\n * Wire field used to echo plain reasoning in `'reasoning_content'` mode.\n * Default: `'reasoning'` (broadest vLLM compatibility).\n */\n reasoningContentField?: 'reasoning_content' | 'reasoning'\n /**\n * Normalize `content: null` → `''` on assistant tool-call turns. Default off.\n */\n normalizeNullContent?: boolean\n /**\n * Active model id. Used to gate `provider_reasoning` blocks — reasoning state\n * is bound to the route that produced it, so a model switch invalidates the\n * embedded signatures.\n */\n model?: string\n}\n\nexport function toOAIMessages(system: string, messages: SessionMessage[], options: ToOAIOptions = {}): OAIMessage[] {\n // Collapse the system-prompt boundary marker before it leaves the harness —\n // it's purely structural metadata for `applyOAICacheBreakpoints`, not\n // content the model should see. Cache-aware routes call the breakpoint\n // helper with the ORIGINAL `system` so the split is preserved on the wire;\n // cache-disabled routes get a clean, marker-free system message here.\n const renderedSystem = renderSystemForWire(system)\n const out: OAIMessage[] = [{ role: 'system', content: renderedSystem }]\n const nativeImageInTool = options.imageInToolResult === true\n const audioEnabled = options.audio === true\n const videoEnabled = options.video === true\n const documentsEnabled = options.documents === true\n const reasoningEnabled = options.supportsReasoning === true\n const reasoningMode = options.reasoningMode ?? 'openrouter'\n const reasoningContentField = options.reasoningContentField ?? 'reasoning'\n const normalizeNullContent = options.normalizeNullContent === true\n const activeModel = options.model\n // Producer whose stored `provider_reasoning` blocks are valid for the active\n // route shape. `reasoning_content` mode round-trips `'vllm'` blocks; the\n // default `'openrouter'` mode round-trips `'openrouter'` blocks.\n const reasoningProducer: 'openrouter' | 'vllm' = reasoningMode === 'reasoning_content' ? 'vllm' : 'openrouter'\n\n for (const msg of messages) {\n // Single-pass partition into per-kind buckets — avoids re-walking\n // `msg.content` once per block type (P5). Reasoning blocks are gated here\n // by producer + active route, matching the previous double-filter.\n const toolResults: Extract<SessionContentBlock, { type: 'tool_result' }>[] = []\n const toolCalls: Extract<SessionContentBlock, { type: 'tool_call' }>[] = []\n const textBlocks: Extract<SessionContentBlock, { type: 'text' }>[] = []\n const imageBlocks: Extract<SessionContentBlock, { type: 'image' }>[] = []\n const audioBlocks: Extract<SessionContentBlock, { type: 'audio' }>[] = []\n const videoBlocks: Extract<SessionContentBlock, { type: 'video' }>[] = []\n const documentBlocks: Extract<SessionContentBlock, { type: 'document' }>[] = []\n const reasoningBlocks: Extract<SessionContentBlock, { type: 'provider_reasoning' }>[] = []\n for (const b of msg.content) {\n switch (b.type) {\n case 'tool_result':\n toolResults.push(b)\n break\n case 'tool_call':\n toolCalls.push(b)\n break\n case 'text':\n textBlocks.push(b)\n break\n case 'image':\n imageBlocks.push(b)\n break\n case 'audio':\n audioBlocks.push(b)\n break\n case 'video':\n videoBlocks.push(b)\n break\n case 'document':\n documentBlocks.push(b)\n break\n case 'provider_reasoning':\n // Provider-bound reasoning state — kept only when the host supports\n // it, the producer matches the active mode's shape, AND the active\n // route matches the producing model (signatures/CoT are route-bound).\n if (reasoningEnabled\n && b.producer === reasoningProducer\n && !(b.model && activeModel && b.model !== activeModel)) {\n reasoningBlocks.push(b)\n }\n break\n }\n }\n const reasoningDetails = reasoningBlocks.flatMap(b => b.producer === 'openrouter' ? b.details : [])\n // Plain reasoning text (vLLM/Arcee) — concatenated across blocks in order.\n const reasoningText = reasoningMode === 'reasoning_content'\n ? reasoningBlocks.map(b => b.producer === 'vllm' ? b.reasoningText : '').filter(Boolean).join('')\n : ''\n\n // Direct message-content audio/video are hard-gated by route capability:\n // throw rather than drop bytes a non-capable model would 400 on (or\n // strip-and-confabulate). Checked up here so every downstream branch\n // (multimodal user message, or a turn that also carries tool_calls) is\n // covered uniformly. Tool-result audio/video are gated per-block in the\n // tool_results branch below, where the companion-vs-native split lives.\n if (audioBlocks.length > 0 && !audioEnabled)\n throw unsupportedMediaError('audio', 'openai-compat')\n if (videoBlocks.length > 0 && !videoEnabled)\n throw unsupportedMediaError('video', 'openai-compat')\n\n // Tool results → individual tool messages (plus optional companion user message for images)\n if (toolResults.length > 0) {\n for (const tr of toolResults) {\n // Pure-string output → text-only fast path, wire-identical to pre-multimodal behavior.\n if (typeof tr.output === 'string') {\n out.push({ role: 'tool', tool_call_id: tr.callId, content: tr.output })\n continue\n }\n\n const imageToolParts = tr.output as Array<\n Extract<ToolResultContent, { type: 'text' | 'image' }>\n >\n const hasNonImageMedia = tr.output.some(block =>\n block.type === 'audio' || block.type === 'video' || block.type === 'document')\n\n // Native multi-part path — walk blocks in order so text↔image\n // interleaving is preserved. `imageInToolResult` only says the tool\n // role accepts images; audio/video/file parts are less portable there,\n // so route those through the companion-user-message path below.\n if (nativeImageInTool && !hasNonImageMedia) {\n const parts = imageToolParts.map((block) => {\n if (block.type === 'image') {\n assertResolvedMediaBlock(block, 'OpenAI-compatible wire messages')\n return toImageUrlPart({ mediaType: block.mediaType, data: block.data })\n }\n return { type: 'text' as const, text: block.text }\n })\n out.push({ role: 'tool', tool_call_id: tr.callId, content: parts })\n continue\n }\n\n // Companion-user-message fallback (Claude Desktop / Cline pattern).\n // Works on any Chat Completions endpoint that accepts images in user messages.\n const { text, images, audios, videos, documents } = summarizeToolResultOutput(tr.output, documentsEnabled)\n if (audios.length > 0 && !audioEnabled)\n throw unsupportedMediaError('audio', 'openai-compat')\n if (videos.length > 0 && !videoEnabled)\n throw unsupportedMediaError('video', 'openai-compat')\n const mediaCount = images.length + audios.length + videos.length + documents.length\n if (mediaCount === 0) {\n // Structured array happened to be text-only — collapse to pure-text path.\n out.push({ role: 'tool', tool_call_id: tr.callId, content: text })\n continue\n }\n\n // Use the precise \"image(s)\" wording for the image-only case (the common\n // path); fall back to the generic \"attachment(s)\" noun only when audio,\n // video, or a document is mixed in.\n const imageOnly = audios.length === 0 && videos.length === 0 && documents.length === 0\n const noun = imageOnly\n ? (mediaCount === 1 ? 'image' : 'images')\n : (mediaCount === 1 ? 'attachment' : 'attachments')\n const attachedMarker = `[${mediaCount} ${noun} attached — see next user message]`\n const toolMarker = text.length > 0 ? `${text}\\n\\n${attachedMarker}` : attachedMarker\n out.push({ role: 'tool', tool_call_id: tr.callId, content: toolMarker })\n\n out.push({\n role: 'user',\n content: [\n ...images.map(toImageUrlPart),\n ...audios.map(toInputAudioPart),\n ...videos.map(toVideoUrlPart),\n ...documents.map(toFilePart),\n { type: 'text' as const, text: `(${noun} returned by tool call ${tr.callId})` },\n ],\n })\n }\n // A message can legitimately carry text/image/audio/video/document blocks\n // alongside its tool_results (e.g. a user follow-up typed while tools ran).\n // Don't drop them — fall through to the non-tool-result shaping below.\n if (textBlocks.length === 0 && imageBlocks.length === 0 && audioBlocks.length === 0\n && videoBlocks.length === 0 && documentBlocks.length === 0) {\n continue\n }\n }\n\n // Tool calls → assistant message with tool_calls array\n if (toolCalls.length > 0) {\n const textContent = textBlocks.length > 0\n ? textBlocks.map(b => b.text).join('\\n')\n : (normalizeNullContent ? '' : null)\n const m: OAIMessage = {\n role: 'assistant',\n content: textContent,\n tool_calls: toolCalls.map(tc => ({\n id: tc.id,\n type: 'function' as const,\n function: { name: tc.name, arguments: JSON.stringify(tc.input) },\n })),\n }\n if (reasoningDetails.length > 0)\n m.reasoning_details = reasoningDetails\n if (reasoningText.length > 0)\n m[reasoningContentField] = reasoningText\n out.push(m)\n continue\n }\n\n // Images/audio/video/documents → multimodal array. Documents are\n // represented as text markers because Chat Completions has no portable\n // PDF/document block.\n if (imageBlocks.length > 0 || audioBlocks.length > 0 || videoBlocks.length > 0 || documentBlocks.length > 0) {\n const parts: unknown[] = []\n for (const img of imageBlocks) {\n assertResolvedMediaBlock(img, 'OpenAI-compatible wire messages')\n parts.push({\n type: 'image_url',\n image_url: { url: `data:${img.mediaType};base64,${img.data}` },\n })\n }\n for (const b of audioBlocks)\n parts.push(toInputAudioPart({ mediaType: b.mediaType, data: b.data }))\n for (const b of videoBlocks)\n parts.push(toVideoUrlPart({ mediaType: b.mediaType, data: b.data }))\n for (const b of documentBlocks) {\n assertResolvedMediaBlock(b, 'OpenAI-compatible wire messages')\n if (documentsEnabled && isForwardableDocument(b))\n parts.push(toFilePart(b))\n else\n parts.push({ type: 'text', text: documentMarker(b) })\n }\n for (const b of textBlocks) {\n parts.push({ type: 'text', text: b.text })\n }\n const m: OAIMessage = { role: msg.role, content: parts }\n if (msg.role === 'assistant' && reasoningDetails.length > 0)\n m.reasoning_details = reasoningDetails\n if (msg.role === 'assistant' && reasoningText.length > 0)\n m[reasoningContentField] = reasoningText\n out.push(m)\n continue\n }\n\n // Pure text\n let pushed: OAIMessage\n if (textBlocks.length === 1) {\n pushed = { role: msg.role, content: textBlocks[0].text }\n }\n else if (textBlocks.length > 1) {\n pushed = { role: msg.role, content: textBlocks.map(b => ({ type: 'text', text: b.text })) }\n }\n else {\n pushed = { role: msg.role, content: null }\n }\n if (msg.role === 'assistant' && reasoningDetails.length > 0)\n pushed.reasoning_details = reasoningDetails\n if (msg.role === 'assistant' && reasoningText.length > 0)\n pushed[reasoningContentField] = reasoningText\n out.push(pushed)\n }\n\n return out\n}\n\n// ---------------------------------------------------------------------------\n// Prompt caching\n// ---------------------------------------------------------------------------\n\nconst EPHEMERAL = { type: 'ephemeral' as const }\n\n/**\n * `cache_control` for the requested TTL. Same contract as the Anthropic\n * provider's `cacheControlFor`: `'5m'`/unset keeps the bare ephemeral shape so\n * default requests stay byte-identical to every prior zidane version; `'1h'`\n * adds the `ttl` field, which OpenRouter forwards to Anthropic-family routes\n * (measured: accepted with normal cache writes/reads, no beta header needed on\n * that path — OpenRouter manages upstream headers itself). Routes that ignore\n * `cache_control` entirely ignore the ttl with it.\n */\nfunction oaiCacheControlFor(ttl: CacheTtl | undefined): { type: 'ephemeral', ttl?: '1h' } {\n return ttl === '1h' ? { type: 'ephemeral', ttl: '1h' } : EPHEMERAL\n}\n\n/**\n * Add `cache_control: { type: 'ephemeral' }` breakpoints to the system message's\n * last text part and the last message's final content part.\n *\n * Mutates `messages` in place. Converts plain-string content into a single-element\n * content array so the cache marker can attach — this shape is accepted verbatim by\n * OpenRouter's Anthropic and Gemini routes and ignored by routes with automatic\n * caching (OpenAI, DeepSeek, Grok, Groq, Moonshot).\n *\n * Skip conditions (safe no-ops):\n * - Empty messages array.\n * - Assistant messages with no text (tool-call-only) — attaching a cache marker to a\n * `tool_calls` block has no defined semantics, so we skip and let the prior\n * system/tools breakpoints carry caching.\n *\n * System-prompt boundary handling: when `originalSystem` (the un-rendered\n * system text passed to {@link toOAIMessages}) contains\n * {@link SYSTEM_PROMPT_BOUNDARY}, the leading system message is rewritten as\n * a two-part array — `cache_control` lands on the static prefix only, so\n * per-turn churn in the dynamic suffix doesn't invalidate the cached\n * doctrine above. `originalSystem` defaults to `undefined` for back-compat;\n * omit it (or pass a marker-free string) for the single-block historic\n * behavior.\n */\nexport function applyOAICacheBreakpoints(messages: OAIMessage[], originalSystem?: string, ttl?: CacheTtl): void {\n if (messages.length === 0)\n return\n\n const cacheControl = oaiCacheControlFor(ttl)\n const first = messages[0]\n if (first.role === 'system')\n markSystemMessage(first, cacheControl, originalSystem)\n\n const lastIdx = messages.length - 1\n if (lastIdx > 0)\n markLastContentPart(messages[lastIdx], cacheControl)\n}\n\n/**\n * Place the cache marker on the static prefix of the system message. When\n * `originalSystem` carried {@link SYSTEM_PROMPT_BOUNDARY}, the message becomes\n * a two-part array — static (cached) then dynamic (uncached). Otherwise falls\n * through to {@link markLastContentPart} for the historic single-block\n * treatment.\n *\n * String-only branch — system messages produced by {@link toOAIMessages} are\n * always plain strings (`{ role: 'system', content: <rendered> }`). Hosts\n * that supply pre-built multi-part system messages bypass this branch and\n * get the generic last-block treatment.\n */\nfunction markSystemMessage(msg: OAIMessage, cacheControl: { type: 'ephemeral', ttl?: '1h' }, originalSystem?: string): void {\n if (typeof msg.content !== 'string' || msg.content.length === 0) {\n markLastContentPart(msg, cacheControl)\n return\n }\n // `originalSystem` is the authoritative source for the boundary split —\n // `msg.content` has typically been pre-rendered by `toOAIMessages` and no\n // longer carries the marker. Fall back to `msg.content` itself when no\n // original was supplied (or an empty original) so an external caller that\n // embedded a marker directly doesn't leak it into the cached block.\n const splitSource = originalSystem && originalSystem.length > 0\n ? originalSystem\n : msg.content\n const parts = splitSystemPrompt(splitSource)\n if (parts.dynamic.length === 0) {\n // No marker — wrap the (already marker-free) string in a single cached\n // block. `parts.static` equals `splitSource` in this branch.\n msg.content = [{ type: 'text', text: parts.static, cache_control: cacheControl }]\n return\n }\n const next: Array<Record<string, unknown>> = []\n if (parts.static.length > 0)\n next.push({ type: 'text', text: parts.static, cache_control: cacheControl })\n next.push({ type: 'text', text: parts.dynamic })\n msg.content = next\n}\n\n/**\n * Mark the last content part of an OAI message with `cache_control`. Normalizes\n * string content into a `[{ type: 'text', text, cache_control }]` array so the\n * marker has a block to attach to.\n *\n * No-op for messages without string or array content (tool-call-only assistant\n * messages fall through; the system/tools breakpoints carry the cache prefix).\n */\nfunction markLastContentPart(msg: OAIMessage, cacheControl: { type: 'ephemeral', ttl?: '1h' } = EPHEMERAL): void {\n if (typeof msg.content === 'string') {\n if (msg.content.length === 0)\n return\n msg.content = [{ type: 'text', text: msg.content, cache_control: cacheControl }]\n return\n }\n if (!Array.isArray(msg.content) || msg.content.length === 0)\n return\n\n const parts = msg.content as Array<Record<string, unknown>>\n const lastBlockIdx = parts.length - 1\n parts[lastBlockIdx] = { ...parts[lastBlockIdx], cache_control: cacheControl }\n}\n\n/**\n * Return a copy of `tools` with `cache_control` on the last entry.\n *\n * OpenRouter accepts the marker alongside the standard `type` + `function` fields\n * and forwards it when routing to Anthropic/Gemini. Leaves the non-cached tools\n * unchanged so the caller's reference is not mutated.\n */\nexport function applyOAIToolCacheBreakpoint(tools: OAITool[], ttl?: CacheTtl): OAITool[] {\n if (tools.length === 0)\n return tools\n const cacheControl = oaiCacheControlFor(ttl)\n const lastIdx = tools.length - 1\n return tools.map((tool, i) =>\n i === lastIdx ? ({ ...tool, cache_control: cacheControl } as OAITool & { cache_control: typeof cacheControl }) : tool,\n )\n}\n\n// ---------------------------------------------------------------------------\n// Shared message builders\n// ---------------------------------------------------------------------------\n\nexport function formatTools(tools: ToolSpec[]): OAITool[] {\n // Same sanitization Anthropic gets — OpenAI-compat hosts (OpenRouter,\n // Cerebras, Groq, ...) reject the same root-level shapes (missing\n // `properties` on object schemas, root `$ref` / `oneOf` / `anyOf`). The\n // `openai` profile is a touch more permissive about annotation\n // keywords, but the core rewrites are identical.\n const sanitized = sanitizeToolSpecs(tools, { profile: 'openai' })\n // `?? ''`: the wire type declares `description: string`, and strict\n // OpenAI-compatible validators (and gateways) reject a missing field —\n // MCP tools routinely omit it. Empty string is valid everywhere.\n return sanitized.map(t => ({\n type: 'function' as const,\n function: { name: t.name, description: t.description ?? '', parameters: t.inputSchema as Record<string, unknown> },\n }))\n}\n\nexport function userMessage(content: string): SessionMessage {\n return { role: 'user', content: [{ type: 'text', text: content }] }\n}\n\n/**\n * Build a user `SessionMessage` from multimodal prompt parts.\n *\n * Passes every kind (text/image/audio/video/document) through as a canonical\n * block; the actual wire emission, capability gating, and unsupported-media\n * throwing all happen later in {@link toOAIMessages}. Implementing this (rather\n * than letting the agent fall back to `defaultPromptMessage`) is what lets\n * openai-compat routes accept base64 documents/PDFs at all — the default\n * builder throws on base64 documents.\n */\nexport function openAICompatPromptMessage(parts: PromptPart[]): SessionMessage {\n const content: SessionContentBlock[] = []\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 if (part.type === 'image' || part.type === 'audio' || part.type === 'video') {\n content.push({ type: part.type, mediaType: part.mediaType, data: part.data, ...(part.name ? { name: part.name } : {}) })\n continue\n }\n // document\n content.push({\n type: 'document',\n mediaType: part.mediaType,\n data: part.data,\n encoding: part.encoding,\n ...(part.name ? { name: part.name } : {}),\n })\n }\n return { role: 'user', content }\n}\n\nexport function assistantMessage(content: string): SessionMessage {\n return { role: 'assistant', content: [{ type: 'text', text: content }] }\n}\n\nexport function toolResultsMessage(results: ToolResult[]): SessionMessage {\n return {\n role: 'user',\n content: results.map(r => ({\n type: 'tool_result' as const,\n callId: r.id,\n output: r.content,\n ...(r.isError ? { isError: true as const } : {}),\n ...(r.metadata ? { metadata: r.metadata } : {}),\n })),\n }\n}\n\n/**\n * Recover human-readable reasoning text from an OpenRouter-style\n * `reasoning_details` array. Used as a fallback when a `reasoning_content`-mode\n * host emits the structured envelope instead of the plain text field — the\n * `text` / `summary` fields carry the visible chain-of-thought; opaque fields\n * (signature/data/format) are ignored.\n */\nfunction extractReasoningDetailsText(details: unknown[]): string {\n let out = ''\n for (const item of details) {\n if (!item || typeof item !== 'object')\n continue\n const rec = item as Record<string, unknown>\n if (typeof rec.text === 'string')\n out += rec.text\n else if (typeof rec.summary === 'string')\n out += rec.summary\n }\n return out\n}\n\nexport function buildAssistantContent(\n text: string,\n toolCalls: { id: string, name: string, input: Record<string, unknown> }[],\n thinking?: string,\n reasoning?:\n | { details: unknown[], producer: 'openrouter', model?: string }\n | { reasoningText: string, producer: 'vllm', model?: string },\n): SessionMessage {\n const content: SessionContentBlock[] = []\n // `provider_reasoning` precedes `thinking` so re-attached envelopes arrive\n // before the legacy text mirror in the assistant content list.\n const hasReasoning = reasoning\n && (reasoning.producer === 'openrouter'\n ? reasoning.details.length > 0\n : reasoning.reasoningText.length > 0)\n if (reasoning && hasReasoning) {\n const block: Extract<SessionContentBlock, { type: 'provider_reasoning' }> = reasoning.producer === 'openrouter'\n ? { type: 'provider_reasoning', producer: 'openrouter', details: reasoning.details }\n : { type: 'provider_reasoning', producer: 'vllm', reasoningText: reasoning.reasoningText }\n if (reasoning.model)\n block.model = reasoning.model\n content.push(block)\n }\n if (thinking)\n content.push({ type: 'thinking', text: thinking })\n if (text)\n content.push({ type: 'text', text })\n for (const tc of toolCalls) {\n content.push({ type: 'tool_call', id: tc.id, name: tc.name, input: tc.input })\n }\n // Never persist an empty assistant content array — replaying\n // `{ role: 'assistant', content: [] }` to providers that strict-validate\n // (Anthropic) 400s the whole request. Marker text mirrors the pairing\n // repair convention in `session/messages.ts`.\n if (content.length === 0)\n content.push({ type: 'text', text: '[no content]' })\n return { role: 'assistant', content }\n}\n\n// ---------------------------------------------------------------------------\n// Error classification\n// ---------------------------------------------------------------------------\n\n/**\n * HTTP error thrown when an OpenAI-compatible endpoint returns a non-OK response.\n *\n * The body is best-effort JSON-parsed; `error.message` / `error.code` / `error.type`\n * are extracted for clean downstream classification.\n */\nexport class OpenAICompatHttpError extends Error {\n readonly status: number\n readonly providerCode?: string\n readonly bodyText: string\n\n constructor(status: number, bodyText: string) {\n let message = bodyText\n let code: string | undefined\n try {\n const parsed = JSON.parse(bodyText)\n message = parsed?.error?.message ?? bodyText\n code = parsed?.error?.code ?? parsed?.error?.type\n }\n catch {\n // Leave message as the raw body.\n }\n super(`HTTP ${status}: ${message}`)\n this.name = 'OpenAICompatHttpError'\n this.status = status\n this.providerCode = code\n this.bodyText = bodyText\n }\n}\n\nconst TRAILING_SLASH_RE = /\\/$/\n\n/**\n * Classify an OpenAI-compatible error into `ClassifiedError`.\n *\n * Recognizes:\n * - `AbortError` (from fetch) → `aborted`.\n * - `OpenAICompatHttpError` with a context-exceeded code or message → `context_exceeded`.\n * - Any other `OpenAICompatHttpError` → `provider_error`.\n *\n * Returns `null` for unrecognized error shapes (the loop falls back to `AgentProviderError`).\n */\nexport function classifyOpenAICompatError(err: unknown): ClassifiedError | null {\n const prelude = classifyErrorPrelude(err)\n if (prelude === 'not-object')\n return null\n if (prelude === 'aborted')\n return { kind: 'aborted' }\n\n // A truncated / malformed SSE stream is a transient network-level failure —\n // typically worth a retry from the caller's perspective.\n if (err instanceof OpenAICompatStreamError) {\n return {\n kind: 'provider_error',\n providerCode: 'stream_error',\n message: err.message,\n retryable: true,\n }\n }\n\n if (!(err instanceof OpenAICompatHttpError))\n return null\n\n const code = err.providerCode\n const msg = err.message\n\n if (code === 'context_length_exceeded' || matchesContextExceeded(msg)) {\n return {\n kind: 'context_exceeded',\n providerCode: code ?? 'context_length_exceeded',\n message: msg,\n }\n }\n\n if (matchesToolPairingError(msg)) {\n return {\n kind: 'tool_pairing_corruption',\n providerCode: code ?? 'invalid_request_error',\n message: msg,\n }\n }\n\n return {\n kind: 'provider_error',\n providerCode: code ?? String(err.status),\n message: msg,\n retryable: isRetryableHttpStatus(err.status),\n }\n}\n\n// ---------------------------------------------------------------------------\n// Finish-reason mapping\n// ---------------------------------------------------------------------------\n\n/**\n * Map an OpenAI-compatible `finish_reason` string to the zidane `TurnFinishReason` union.\n */\nexport function mapOAIFinishReason(reason: string | null | undefined): TurnFinishReason | undefined {\n if (!reason)\n return undefined\n switch (reason) {\n case 'stop':\n return 'stop'\n case 'tool_calls':\n case 'function_call':\n return 'tool-calls'\n case 'length':\n return 'length'\n case 'content_filter':\n return 'content-filter'\n default:\n return 'other'\n }\n}\n\n// ---------------------------------------------------------------------------\n// openaiCompat factory\n// ---------------------------------------------------------------------------\n\n/**\n * Auth header config. `scheme` is prepended to the api key with a space, e.g.\n * `{ name: 'Authorization', scheme: 'Bearer' }` → `Authorization: Bearer <key>`.\n * Omit `scheme` for raw header values (e.g. `{ name: 'X-Api-Key' }` → `X-Api-Key: <key>`).\n *\n * Real-world examples:\n * - Default OpenAI / OpenRouter / Cerebras: `{ name: 'Authorization', scheme: 'Bearer' }`.\n * - Baseten: `{ name: 'Authorization', scheme: 'Api-Key' }`.\n * - Some gateways: `{ name: 'X-Api-Key' }`.\n */\nexport interface OpenAICompatAuthHeader {\n name: string\n scheme?: string\n}\n\nexport interface OpenAICompatParams {\n /** Bearer-style API key. */\n apiKey: string\n /** Base URL — `/chat/completions` is appended. */\n baseURL: string\n /** Default model id when `run({ model })` is omitted. */\n defaultModel?: string\n /** Provider name exposed as `Provider.name`. Defaults to `'openai-compat'`. */\n name?: string\n /** Auth header shape. Defaults to `{ name: 'Authorization', scheme: 'Bearer' }`. */\n authHeader?: OpenAICompatAuthHeader\n /** Extra headers sent with every request (e.g. referer, user-agent). */\n extraHeaders?: Record<string, string>\n /**\n * Provider-level capability flags. Routed into the message shaper and the\n * agent loop so images in tool results + user messages are handled correctly\n * for the underlying model.\n *\n * Defaults when omitted: `vision: false`, `imageInToolResult: false`,\n * `audio: false`, `video: false` — a conservative assumption matching most\n * OSS text-only OpenAI-compat endpoints. Override when routing to a known\n * capable endpoint: set `audio: true` for audio-input models (`input_audio`),\n * `video: true` for video-input models (`video_url`, e.g. Gemini via\n * OpenRouter), `documents: true` for PDF-capable models (OpenAI `file`\n * content part with base64 `file_data`), or `vision: true` for vision models.\n */\n capabilities?: ProviderCapabilities\n /**\n * Whether this endpoint honors `cache_control: { type: 'ephemeral' }` markers\n * on message content parts and tool definitions.\n *\n * - `true` — inject markers when the caller asks for caching. OpenRouter routes\n * to Anthropic/Gemini forward the markers; routes to OpenAI/DeepSeek/\n * Grok/Groq/Moonshot ignore them (those backends cache automatically).\n * - `false` — never inject markers. Safe default for endpoints that strictly\n * validate the request schema (OpenAI direct, most OSS inference\n * servers) and would reject unknown fields.\n *\n * Default: `false`. The `openrouter` wrapper sets this to `true`.\n */\n cacheBreakpoints?: boolean\n /**\n * Whether this endpoint speaks a reasoning round-trip shape selected by\n * `reasoningMode` — either OpenRouter's normalized `reasoning` request field\n * plus structured `reasoning_details[]`, or vLLM/Arcee's plain\n * `reasoning_content` assistant-message field.\n *\n * - `true` — capture compatible reasoning fields from streaming responses\n * into `provider_reasoning` blocks and echo them back on\n * subsequent assistant messages. In `'openrouter'` mode, also map\n * zidane's `behavior.thinking` / `behavior.thinkingBudget` to the\n * request's `reasoning` field.\n * - `false` — never set the field; drop any stored `provider_reasoning`\n * blocks before sending. Safe default for hosts that strict-\n * validate the request schema.\n *\n * Default: `false`. The `openrouter` wrapper sets this to `true`.\n */\n supportsReasoning?: boolean\n /**\n * Shape used to round-trip reasoning state across turns when\n * `supportsReasoning` is `true`. Ignored when `supportsReasoning` is `false`.\n *\n * - `'openrouter'` (default) — capture/echo OpenRouter's structured\n * `reasoning_details[]` envelope.\n * - `'reasoning_content'` — capture the plain `reasoning_content` string and\n * echo it back on assistant messages (DeepSeek-R1 reasoning parser / Arcee\n * Trinity-Thinking served via vLLM). See\n * https://docs.arcee.ai/capabilities/reasoning-traces.\n *\n * Default: `'openrouter'`.\n */\n reasoningMode?: 'openrouter' | 'reasoning_content'\n /**\n * Field name to use when echoing plain reasoning back upstream in\n * `'reasoning_content'` mode. The OpenAI SDK returns `reasoning_content`, but\n * some self-hosted vLLM versions only read `reasoning` on input (vllm#38488).\n * Default: `'reasoning'`.\n */\n reasoningContentField?: 'reasoning_content' | 'reasoning'\n /**\n * Normalize `content: null` → `''` on assistant tool-call turns. Some\n * reasoning models (Arcee/vLLM) degrade or emit malformed follow-ups when a\n * tool-call assistant message carries a null content. Off by default to keep\n * the wire byte-identical for existing providers (OpenRouter, Cerebras, …).\n */\n normalizeNullContent?: boolean\n /**\n * Generic pass-through for fields on the Chat Completions request body that\n * zidane does not yet type. Spread into the request before the typed core\n * (model / messages / tools / max_tokens / stream / tool_choice), so\n * explicit options always win on collision.\n *\n * Forward-compat escape hatch for endpoints that ship one-off fields ahead\n * of zidane (e.g. OpenAI `reasoning_effort`, OpenRouter `provider` routing,\n * vendor-specific `safety_level` knobs).\n */\n extraBodyParams?: Record<string, unknown>\n /**\n * Prompt-cache affinity key — an opt-in routing hint that keeps a session's\n * requests landing on a replica whose KV cache is already warm. See\n * {@link PromptCacheKeyOption} for the shapes, and `./prompt-cache-key.ts`\n * for which routes actually honor it (real on OpenAI/Azure and OpenRouter,\n * effectively a no-op on Baseten, real on any proxy/gateway in front).\n *\n * Default: unset — the field is absent and requests stay byte-identical to\n * what they were before this option existed.\n */\n promptCacheKey?: PromptCacheKeyOption\n /**\n * Request-body field carrying {@link OpenAICompatParams.promptCacheKey}.\n *\n * - `'prompt_cache_key'` (default) — OpenAI's own hint, combined server-side\n * with the prefix hash; also what OpenRouter reads as its sticky-routing\n * key.\n * - `'user'` — the legacy spelling of the same idea, for older routes that\n * bucket on it but reject `prompt_cache_key`.\n *\n * Any other field name is accepted for vendor-specific spellings.\n */\n promptCacheKeyField?: 'prompt_cache_key' | 'user' | (string & {})\n /**\n * Additionally send the affinity key as this HTTP header, for gateways that\n * route on a header rather than a body field. Defers to any same-named entry\n * in {@link OpenAICompatParams.extraHeaders}.\n *\n * Default: unset — no header is added.\n */\n promptCacheKeyHeader?: string\n /**\n * PDF-parsing engine for OpenRouter's `file-parser` plugin. Only takes effect\n * on endpoints that honor the `plugins` request field (OpenRouter) AND only\n * when a request actually carries a forwarded `file` content part — otherwise\n * it's omitted, so it's a safe no-op for plain-text turns and non-OpenRouter\n * endpoints (which ignore the unknown field).\n *\n * Without an explicit engine, OpenRouter's per-model native PDF handling is\n * inconsistent (some models silently fail to receive the parsed document).\n * Pinning the engine makes it deterministic:\n * - `'native'` — use the model's own file support (free; for gpt-4o /\n * Gemini / Claude routes). Default for the `openrouter` wrapper.\n * - `'pdf-text'` — free text-only extraction (no OCR / images).\n * - `'mistral-ocr'`— OCR for scans / image PDFs (paid).\n *\n * When unset, no `plugins` field is added (current behavior preserved).\n */\n pdfEngine?: 'native' | 'pdf-text' | 'mistral-ocr'\n /**\n * Per-request reasoning body planner for hosts whose thinking knob is a\n * vendor-specific request field rather than OpenRouter's `reasoning` object\n * (e.g. Baseten's `reasoning_effort` / `chat_template_args.enable_thinking`).\n * Called on every `stream()` with the resolved model + thinking options;\n * the returned fields are merged into the request body (after\n * `extraBodyParams`, before the typed core, so model/messages/etc. still\n * win on collision). Return `undefined` to send nothing.\n */\n planReasoningRequest?: (ctx: {\n model: string\n thinking?: ThinkingLevel\n thinkingBudget?: number\n }) => Record<string, unknown> | undefined\n}\n\n/**\n * Map zidane's `ThinkingLevel` + optional explicit budget to the OpenRouter\n * `reasoning` request field. Returns `undefined` when reasoning should not be\n * sent (off, or no level + no budget).\n *\n * - `'low' | 'medium' | 'high' | 'xhigh' | 'max'` → `{ effort }`.\n * - `'minimal'` → `{ effort: 'low' }` (closest match in OpenRouter's vocabulary).\n * - `'adaptive'` → `{}` (let upstream decide; OpenRouter passes through).\n * - explicit `thinkingBudget` → `{ max_tokens }` overlaid on the level.\n */\nfunction planOpenRouterReasoning(\n thinking: ThinkingLevel | undefined,\n thinkingBudget: number | undefined,\n): Record<string, unknown> | undefined {\n if ((!thinking || thinking === 'off') && typeof thinkingBudget !== 'number')\n return undefined\n const out: Record<string, unknown> = {}\n if (thinking && thinking !== 'off' && thinking !== 'adaptive') {\n out.effort = thinking === 'minimal' ? 'low' : thinking\n }\n if (typeof thinkingBudget === 'number' && thinkingBudget > 0) {\n out.max_tokens = thinkingBudget\n }\n return out\n}\n\nfunction usageWasReported(usage: OpenAICompatUsage): boolean {\n return usage.input > 0\n || usage.output > 0\n || (usage.cacheRead ?? 0) > 0\n || (usage.cacheCreation ?? 0) > 0\n || usage.cost !== undefined\n}\n\nfunction estimateMissingUsage(body: Record<string, unknown>, result: Awaited<ReturnType<typeof consumeSSE>>): OpenAICompatUsage {\n return {\n input: estimateTokens(JSON.stringify({\n messages: body.messages,\n tools: body.tools,\n })),\n output: estimateTokens([\n result.thinking,\n result.text,\n result.toolCalls.length > 0 ? JSON.stringify(result.toolCalls) : '',\n ].filter(Boolean).join('\\n')),\n }\n}\n\n/**\n * Factory for any OpenAI-compatible HTTP endpoint.\n *\n * Speaks the standard `POST /chat/completions` + `stream: true` + SSE dialect.\n * Thin wrappers (`openrouter`, `cerebras`) call this with pinned defaults.\n *\n * @example Baseten (non-standard auth scheme)\n * ```ts\n * openaiCompat({\n * name: 'baseten',\n * apiKey: process.env.BASETEN_API_KEY!,\n * baseURL: process.env.BASETEN_PROXY_URL!,\n * authHeader: { name: 'Authorization', scheme: 'Api-Key' },\n * })\n * ```\n *\n * @example OpenAI directly — including native PDF input\n * The bundled `openai` provider routes through pi-ai (Codex Responses), whose\n * content union is text+image only, so it degrades documents to a marker. To\n * send PDFs to OpenAI, point this factory at OpenAI's Chat Completions endpoint\n * and enable `documents` — base64 PDFs are forwarded as the `{type:'file'}`\n * content part. NOTE: OpenAI's Chat Completions `file` part accepts\n * `application/pdf` ONLY; spreadsheets/`.docx` are Responses-API-only and will\n * 400 here.\n * ```ts\n * openaiCompat({\n * name: 'openai',\n * apiKey: process.env.OPENAI_API_KEY!,\n * baseURL: 'https://api.openai.com/v1',\n * capabilities: { vision: true, documents: true },\n * })\n * ```\n */\nexport function openaiCompat(params: OpenAICompatParams): Provider {\n const name = params.name ?? 'openai-compat'\n const defaultModel = params.defaultModel ?? 'gpt-4o-mini'\n const authHeaderName = params.authHeader?.name ?? 'Authorization'\n const authHeaderValue = params.authHeader?.scheme\n ? `${params.authHeader.scheme} ${params.apiKey}`\n : (params.authHeader ? params.apiKey : `Bearer ${params.apiKey}`)\n const extraHeaders = params.extraHeaders ?? {}\n const endpoint = `${params.baseURL.replace(TRAILING_SLASH_RE, '')}/chat/completions`\n const capabilities: ProviderCapabilities = {\n vision: params.capabilities?.vision ?? false,\n imageInToolResult: params.capabilities?.imageInToolResult ?? false,\n audio: params.capabilities?.audio ?? false,\n video: params.capabilities?.video ?? false,\n documents: params.capabilities?.documents ?? false,\n }\n\n const cacheBreakpointsEnabled = params.cacheBreakpoints === true\n const reasoningEnabled = params.supportsReasoning === true\n const reasoningMode = params.reasoningMode ?? 'openrouter'\n const reasoningContentField = params.reasoningContentField ?? 'reasoning'\n const normalizeNullContent = params.normalizeNullContent === true\n\n return {\n name,\n meta: { defaultModel, capabilities },\n formatTools,\n userMessage,\n assistantMessage,\n toolResultsMessage,\n promptMessage: openAICompatPromptMessage,\n classifyError: classifyOpenAICompatError,\n\n async stream(options: StreamOptions, callbacks: StreamCallbacks): Promise<TurnResult> {\n const modelId = options.model || defaultModel\n const inputTools = (options.tools ?? []) as OAITool[]\n const nameBoundary = createToolNameBoundary(\n options.messages,\n inputTools.map(tool => tool.function.name),\n )\n const messages = toOAIMessages(options.system, nameBoundary.messages, {\n imageInToolResult: capabilities.imageInToolResult === true,\n audio: capabilities.audio === true,\n video: capabilities.video === true,\n documents: capabilities.documents === true,\n supportsReasoning: reasoningEnabled,\n reasoningMode,\n reasoningContentField,\n normalizeNullContent,\n model: modelId,\n })\n // Prompt caching — insert `cache_control: { type: 'ephemeral' }` breakpoints on\n // the system message's last text part, the last tool definition, and the last\n // message's final content part. Only emitted when the factory was constructed\n // with `cacheBreakpoints: true` (set by the `openrouter` wrapper); endpoints\n // that strict-validate the request schema otherwise reject unknown fields.\n const shouldCache = cacheBreakpointsEnabled && options.cache !== false\n if (shouldCache) {\n // Forward the un-rendered `options.system` so the boundary split\n // lands on the wire — `toOAIMessages` already collapsed the marker\n // into the rendered system content, but the cache helper needs the\n // original byte stream to split correctly.\n applyOAICacheBreakpoints(messages, options.system, options.cacheTtl)\n }\n\n // Output cap. Unset → omit `max_tokens` below so the model runs to a\n // natural stop (the OpenAI-style default), instead of an arbitrary cap\n // truncating mid-output. When an explicit cap IS set and a thinking\n // budget is configured, pad the budget on top so reasoning + reply fit.\n const maxTokens = options.maxTokens === undefined\n ? undefined\n : options.thinkingBudget\n ? options.thinkingBudget + options.maxTokens\n : options.maxTokens\n\n // Vendor-specific thinking fields (Baseten `reasoning_effort` /\n // `chat_template_args`, …) planned per request. Sits between the static\n // extraBodyParams and the typed core in spread order.\n const reasoningBody = params.planReasoningRequest?.({\n model: modelId,\n thinking: options.thinking,\n thinkingBudget: options.thinkingBudget,\n })\n\n const body: Record<string, unknown> = {\n // Spread first so the typed core below wins on collision — explicit\n // always overrides generic.\n ...(params.extraBodyParams ?? {}),\n ...(reasoningBody ?? {}),\n model: modelId,\n messages,\n ...(maxTokens !== undefined ? { max_tokens: maxTokens } : {}),\n stream: true,\n }\n\n // OpenRouter `file-parser` plugin — only when an engine is configured AND\n // the request actually carries a forwarded `file` part. Selecting the\n // engine explicitly makes PDF parsing deterministic (OpenRouter's\n // per-model native fallback is otherwise inconsistent). Omitted entirely\n // for text-only turns and engines-unset configs, so non-OpenRouter\n // endpoints never see the field.\n //\n // Merge (don't clobber) any host-provided `plugins` from extraBodyParams,\n // and defer to a host-supplied `file-parser` if one is already present —\n // explicit caller config wins, matching the rest of the body contract.\n if (params.pdfEngine && messagesContainFilePart(messages)) {\n const existing = Array.isArray(body.plugins) ? (body.plugins as unknown[]) : []\n const hasFileParser = existing.some(p =>\n typeof p === 'object' && p !== null && (p as { id?: unknown }).id === 'file-parser',\n )\n if (!hasFileParser)\n body.plugins = [...existing, { id: 'file-parser', pdf: { engine: params.pdfEngine } }]\n }\n\n // OpenRouter normalized reasoning request field. Only set in\n // `'openrouter'` mode — vLLM/Arcee hosts don't speak the `reasoning`\n // object on requests (thinking there is controlled by serving flags) and\n // would 400 on the unknown field.\n if (reasoningEnabled && reasoningMode === 'openrouter') {\n const reasoning = planOpenRouterReasoning(options.thinking, options.thinkingBudget)\n if (reasoning)\n body.reasoning = reasoning\n }\n\n if (inputTools.length > 0) {\n const tools = inputTools.map(tool => ({\n ...tool,\n function: { ...tool.function, name: nameBoundary.toWire(tool.function.name) },\n }))\n body.tools = shouldCache ? applyOAIToolCacheBreakpoint(tools, options.cacheTtl) : tools\n }\n\n if (options.toolChoice) {\n if (options.toolChoice.type === 'tool' && options.toolChoice.name)\n body.tool_choice = { type: 'function', function: { name: nameBoundary.toWire(options.toolChoice.name) } }\n else if (options.toolChoice.type === 'required')\n body.tool_choice = 'required'\n else\n body.tool_choice = 'auto'\n }\n\n // Prompt-cache affinity key. Derived from the session's stable prefix, so\n // it is byte-identical across a session's turns and cannot itself break a\n // cache. A host- or gateway-supplied value always wins: an AI gateway that\n // sets this itself (or a caller who pinned one through `extraBodyParams`)\n // must not be second-guessed here.\n const promptCacheKey = resolvePromptCacheKey(params.promptCacheKey, {\n model: modelId,\n system: options.system,\n messages: options.messages,\n })\n const promptCacheKeyField = params.promptCacheKeyField ?? 'prompt_cache_key'\n if (promptCacheKey !== undefined && body[promptCacheKeyField] === undefined)\n body[promptCacheKeyField] = promptCacheKey\n\n // HTTP header names are case-insensitive, so the no-clobber probe must be\n // too — an exact-key check would let the affinity header ride alongside a\n // differently-cased caller header, and fetch comma-joins the two values\n // into one header on the wire.\n const affinityHeaderName = params.promptCacheKeyHeader?.toLowerCase()\n const affinityHeader = params.promptCacheKeyHeader !== undefined && promptCacheKey !== undefined\n && !Object.keys(extraHeaders).some(k => k.toLowerCase() === affinityHeaderName)\n ? { [params.promptCacheKeyHeader]: promptCacheKey }\n : {}\n\n const response = await fetch(endpoint, {\n method: 'POST',\n headers: {\n [authHeaderName]: authHeaderValue,\n 'Content-Type': 'application/json',\n ...extraHeaders,\n ...affinityHeader,\n },\n body: JSON.stringify(body),\n signal: options.signal,\n })\n\n if (!response.ok) {\n const errorText = await response.text()\n throw new OpenAICompatHttpError(response.status, errorText)\n }\n\n const result = await consumeSSE(response, callbacks, options.signal)\n const finishReason = mapOAIFinishReason(result.finishReason)\n const usage = usageWasReported(result.usage)\n ? result.usage\n : estimateMissingUsage(body, result)\n\n // Build the provider-bound reasoning block to persist for the next turn.\n // `'openrouter'` → structured details; `'reasoning_content'` → the plain\n // reasoning string captured into `result.thinking` during the stream.\n let reasoningArg:\n | { details: unknown[], producer: 'openrouter', model?: string }\n | { reasoningText: string, producer: 'vllm', model?: string }\n | undefined\n // In `reasoning_content` mode the plain reasoning is round-tripped via the\n // `provider_reasoning` (vllm) block, so suppress the redundant `thinking`\n // mirror to avoid persisting the same text twice. (OpenRouter keeps the\n // mirror — its structured `details` aren't human-readable.)\n let thinkingForBlock: string | undefined = result.thinking\n if (reasoningEnabled) {\n if (reasoningMode === 'reasoning_content') {\n // Plain reasoning normally lands in `result.thinking`. Guard against a\n // host that emits the structured `reasoning_details` envelope instead\n // (which suppresses the legacy text capture in `consumeSSE`) by\n // recovering any visible text from those items.\n const reasoningText = result.thinking.length > 0\n ? result.thinking\n : extractReasoningDetailsText(result.reasoningDetails)\n if (reasoningText.length > 0) {\n reasoningArg = { reasoningText, producer: 'vllm', model: modelId }\n thinkingForBlock = undefined\n }\n }\n else if (result.reasoningDetails.length > 0) {\n reasoningArg = { details: result.reasoningDetails, producer: 'openrouter', model: modelId }\n }\n }\n\n const filledUsage = await fillEstimatedCost({\n input: usage.input,\n output: usage.output,\n ...(usage.cacheRead !== undefined ? { cacheRead: usage.cacheRead } : {}),\n ...(usage.cacheCreation !== undefined ? { cacheCreation: usage.cacheCreation } : {}),\n ...(usage.cost !== undefined ? { cost: usage.cost } : {}),\n ...(finishReason ? { finishReason } : {}),\n modelId,\n }, name, undefined, options.modelCatalog)\n const toolCalls = result.toolCalls.map(call => ({ ...call, name: nameBoundary.fromWire(call.name) }))\n return {\n assistantMessage: buildAssistantContent(\n result.text,\n toolCalls,\n thinkingForBlock,\n reasoningArg,\n ),\n text: result.text,\n toolCalls,\n done: toolCalls.length === 0,\n usage: filledUsage,\n }\n },\n }\n}\n"],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import { $n as HeadlessResult, Jn as FormattedHeadlessTurnEvent, Qn as HeadlessOutputFormat, Wi as Agent, Xn as HeadlessEvent, qn as FormattedHeadlessResult } from "../index-BYKzzYzi.js";
1
+ import { $n as HeadlessResult, Jn as FormattedHeadlessTurnEvent, Qn as HeadlessOutputFormat, Wi as Agent, Xn as HeadlessEvent, qn as FormattedHeadlessResult } from "../index-ZWibV230.js";
2
2
 
3
3
  //#region src/output/stream-json.d.ts
4
4
  type StreamJsonOutputFormat = HeadlessOutputFormat;
@@ -1,4 +1,4 @@
1
- import { Ki as AgentHooks, Wi as Agent, t as Preset } from "../index-BYKzzYzi.js";
1
+ import { Ki as AgentHooks, Wi as Agent, t as Preset } from "../index-ZWibV230.js";
2
2
 
3
3
  //#region src/output/terminal.d.ts
4
4
  interface TerminalOutputOptions {
package/dist/plugins.d.ts CHANGED
@@ -1,2 +1,2 @@
1
- import { $s as AgentPluginsVersion, Gs as PLUGIN_MCP_FILE, Js as parsePluginManifest, Ks as expandPluginVars, Qs as loadAgentPlugin, Ws as PLUGIN_SKILLS_DIR, Xs as PLUGIN_MANIFEST_FILE, Ys as validatePluginManifest, Zs as defaultPluginDataRoot, ac as PluginManifest, cc as agentPluginsSchemaId, ec as LoadAgentPluginOptions, ic as PluginDiagnostic, lc as isSupportedAgentPluginsVersion, nc as PluginAuthor, oc as PluginSchemaKind, qs as ManifestValidation, rc as PluginComponentStatus, sc as SUPPORTED_AGENT_PLUGINS_VERSIONS, tc as LoadedPlugin, uc as parseSchemaId } from "./index-BYKzzYzi.js";
1
+ import { $s as AgentPluginsVersion, Gs as PLUGIN_MCP_FILE, Js as parsePluginManifest, Ks as expandPluginVars, Qs as loadAgentPlugin, Ws as PLUGIN_SKILLS_DIR, Xs as PLUGIN_MANIFEST_FILE, Ys as validatePluginManifest, Zs as defaultPluginDataRoot, ac as PluginManifest, cc as agentPluginsSchemaId, ec as LoadAgentPluginOptions, ic as PluginDiagnostic, lc as isSupportedAgentPluginsVersion, nc as PluginAuthor, oc as PluginSchemaKind, qs as ManifestValidation, rc as PluginComponentStatus, sc as SUPPORTED_AGENT_PLUGINS_VERSIONS, tc as LoadedPlugin, uc as parseSchemaId } from "./index-ZWibV230.js";
2
2
  export { type AgentPluginsVersion, type LoadAgentPluginOptions, type LoadedPlugin, type ManifestValidation, PLUGIN_MANIFEST_FILE, PLUGIN_MCP_FILE, PLUGIN_SKILLS_DIR, type PluginAuthor, type PluginComponentStatus, type PluginDiagnostic, type PluginManifest, type PluginSchemaKind, SUPPORTED_AGENT_PLUGINS_VERSIONS, agentPluginsSchemaId, defaultPluginDataRoot, expandPluginVars, isSupportedAgentPluginsVersion, loadAgentPlugin, parsePluginManifest, parseSchemaId, validatePluginManifest };
package/dist/presets.d.ts CHANGED
@@ -1,2 +1,2 @@
1
- import { Gi as AgentHookMap, a as basicTools, i as _default, n as composePresets, r as definePreset, t as Preset } from "./index-BYKzzYzi.js";
1
+ import { Gi as AgentHookMap, a as basicTools, i as _default, n as composePresets, r as definePreset, t as Preset } from "./index-ZWibV230.js";
2
2
  export { type AgentHookMap, Preset, _default as basic, basicTools, composePresets, definePreset };
package/dist/presets.js CHANGED
@@ -1,2 +1,2 @@
1
- import { _t as composePresets, bt as basic_default, vt as definePreset, yt as basicTools } from "./agent-gZjHsUft.js";
1
+ import { _t as composePresets, bt as basic_default, vt as definePreset, yt as basicTools } from "./agent-CcTWL-Z6.js";
2
2
  export { basic_default as basic, basicTools, composePresets, definePreset };
@@ -1,2 +1,2 @@
1
- import { $p as AnthropicContextManagement, Qp as AnthropicBedrockConfig, am as buildAnthropicClientOptions, cm as defaultAnthropicMaxTokens, dm as resolveAnthropicBetas, em as AnthropicParams, fm as resolveAnthropicMaxOutput, im as applyAnthropicCacheBreakpoints, lm as planAnthropicThinking, nm as EXTENDED_CACHE_TTL_BETA, om as classifyAnthropicError, rm as anthropic, sm as credentialIdentity, tm as AnthropicThinkingPlan, um as resolveAnthropicAuthShape } from "../index-BYKzzYzi.js";
1
+ import { $p as AnthropicContextManagement, Qp as AnthropicBedrockConfig, am as buildAnthropicClientOptions, cm as defaultAnthropicMaxTokens, dm as resolveAnthropicBetas, em as AnthropicParams, fm as resolveAnthropicMaxOutput, im as applyAnthropicCacheBreakpoints, lm as planAnthropicThinking, nm as EXTENDED_CACHE_TTL_BETA, om as classifyAnthropicError, rm as anthropic, sm as credentialIdentity, tm as AnthropicThinkingPlan, um as resolveAnthropicAuthShape } from "../index-ZWibV230.js";
2
2
  export { AnthropicBedrockConfig, AnthropicContextManagement, AnthropicParams, AnthropicThinkingPlan, EXTENDED_CACHE_TTL_BETA, anthropic, applyAnthropicCacheBreakpoints, buildAnthropicClientOptions, classifyAnthropicError, credentialIdentity, defaultAnthropicMaxTokens, planAnthropicThinking, resolveAnthropicAuthShape, resolveAnthropicBetas, resolveAnthropicMaxOutput };
@@ -1,2 +1,2 @@
1
- import { a as classifyAnthropicError, c as planAnthropicThinking, d as resolveAnthropicMaxOutput, i as buildAnthropicClientOptions, l as resolveAnthropicAuthShape, n as anthropic, o as credentialIdentity, r as applyAnthropicCacheBreakpoints, s as defaultAnthropicMaxTokens, t as EXTENDED_CACHE_TTL_BETA, u as resolveAnthropicBetas } from "../anthropic-D3le2d-s.js";
1
+ import { a as classifyAnthropicError, c as planAnthropicThinking, d as resolveAnthropicMaxOutput, i as buildAnthropicClientOptions, l as resolveAnthropicAuthShape, n as anthropic, o as credentialIdentity, r as applyAnthropicCacheBreakpoints, s as defaultAnthropicMaxTokens, t as EXTENDED_CACHE_TTL_BETA, u as resolveAnthropicBetas } from "../anthropic-BomWPIYw.js";
2
2
  export { EXTENDED_CACHE_TTL_BETA, anthropic, applyAnthropicCacheBreakpoints, buildAnthropicClientOptions, classifyAnthropicError, credentialIdentity, defaultAnthropicMaxTokens, planAnthropicThinking, resolveAnthropicAuthShape, resolveAnthropicBetas, resolveAnthropicMaxOutput };
@@ -1,2 +1,2 @@
1
- import { Xp as ArceeParams, Zp as arcee } from "../index-BYKzzYzi.js";
1
+ import { Xp as ArceeParams, Zp as arcee } from "../index-ZWibV230.js";
2
2
  export { ArceeParams, arcee };
@@ -1,4 +1,4 @@
1
- import { m as openaiCompat } from "../openai-compat-hmZzpDrB.js";
1
+ import { m as openaiCompat } from "../openai-compat-BoTi-9fd.js";
2
2
  //#region src/providers/arcee.ts
3
3
  const BASE_URL = "https://api.arcee.ai/api/v1";
4
4
  function getApiKey(params) {
@@ -1,2 +1,2 @@
1
- import { Jp as baseten, Yp as planBasetenReasoning, qp as BasetenParams } from "../index-BYKzzYzi.js";
1
+ import { Jp as baseten, Yp as planBasetenReasoning, qp as BasetenParams } from "../index-ZWibV230.js";
2
2
  export { BasetenParams, baseten, planBasetenReasoning };
@@ -1,4 +1,4 @@
1
- import { m as openaiCompat } from "../openai-compat-hmZzpDrB.js";
1
+ import { m as openaiCompat } from "../openai-compat-BoTi-9fd.js";
2
2
  //#region src/providers/baseten.ts
3
3
  const BASE_URL = "https://inference.baseten.co/v1";
4
4
  /**
@@ -1,2 +1,2 @@
1
- import { Gp as CerebrasParams, Kp as cerebras } from "../index-BYKzzYzi.js";
1
+ import { Gp as CerebrasParams, Kp as cerebras } from "../index-ZWibV230.js";
2
2
  export { CerebrasParams, cerebras };
@@ -1,4 +1,4 @@
1
- import { m as openaiCompat } from "../openai-compat-hmZzpDrB.js";
1
+ import { m as openaiCompat } from "../openai-compat-BoTi-9fd.js";
2
2
  //#region src/providers/cerebras.ts
3
3
  const BASE_URL = "https://api.cerebras.ai/v1";
4
4
  function getApiKey(params) {
@@ -17,7 +17,7 @@ function cerebras(params) {
17
17
  name: "cerebras",
18
18
  apiKey: getApiKey(params),
19
19
  baseURL: BASE_URL,
20
- defaultModel: params?.defaultModel || "zai-glm-4.7",
20
+ defaultModel: params?.defaultModel || "gpt-oss-120b",
21
21
  capabilities: params?.capabilities ?? {
22
22
  vision: false,
23
23
  imageInToolResult: false
@@ -1 +1 @@
1
- {"version":3,"file":"cerebras.js","names":[],"sources":["../../src/providers/cerebras.ts"],"sourcesContent":["import type { Provider, ProviderCapabilities } from '.'\nimport { openaiCompat } from './openai-compat'\n\nconst BASE_URL = 'https://api.cerebras.ai/v1'\n\nexport interface CerebrasParams {\n apiKey?: string\n defaultModel?: string\n /**\n * Provider capability flags. Cerebras currently serves text-only OSS models\n * (GLM, Llama-family, Qwen-family) — default: `{ vision: false, imageInToolResult: false }`.\n * Override when routing to a vision-capable deployment.\n */\n capabilities?: ProviderCapabilities\n /**\n * Extra HTTP headers sent on every request. Forwarded to the underlying\n * {@link openaiCompat} client — use for routing/attribution headers a proxy\n * in front of `baseURL` consumes.\n */\n extraHeaders?: Record<string, string>\n}\n\nfunction getApiKey(params?: CerebrasParams): string {\n // Resolution order is intentionally aligned with `anthropic` + `openai` —\n // explicit params > env var > fail. Host code that composes providers from\n // config (e.g. a YAML-driven CLI) relies on `params.apiKey` winning in all\n // providers, not just some.\n if (typeof params?.apiKey === 'string' && params.apiKey.length > 0)\n return params.apiKey\n\n if (process.env.CEREBRAS_API_KEY)\n return process.env.CEREBRAS_API_KEY\n\n throw new Error('No Cerebras API key found. Pass `apiKey` or set CEREBRAS_API_KEY in your environment.')\n}\n\n/**\n * Cerebras provider.\n *\n * Thin wrapper around {@link openaiCompat} with Cerebras-specific defaults\n * (base URL, default model).\n */\nexport function cerebras(params?: CerebrasParams): Provider {\n const apiKey = getApiKey(params)\n return openaiCompat({\n name: 'cerebras',\n apiKey,\n baseURL: BASE_URL,\n defaultModel: params?.defaultModel || 'zai-glm-4.7',\n capabilities: params?.capabilities ?? { vision: false, imageInToolResult: false },\n extraHeaders: params?.extraHeaders,\n })\n}\n"],"mappings":";;AAGA,MAAM,WAAW;AAmBjB,SAAS,UAAU,QAAiC;CAKlD,IAAI,OAAO,QAAQ,WAAW,YAAY,OAAO,OAAO,SAAS,GAC/D,OAAO,OAAO;CAEhB,IAAI,QAAQ,IAAI,kBACd,OAAO,QAAQ,IAAI;CAErB,MAAM,IAAI,MAAM,uFAAuF;AACzG;;;;;;;AAQA,SAAgB,SAAS,QAAmC;CAE1D,OAAO,aAAa;EAClB,MAAM;EACN,QAHa,UAAU,MAGlB;EACL,SAAS;EACT,cAAc,QAAQ,gBAAgB;EACtC,cAAc,QAAQ,gBAAgB;GAAE,QAAQ;GAAO,mBAAmB;EAAM;EAChF,cAAc,QAAQ;CACxB,CAAC;AACH"}
1
+ {"version":3,"file":"cerebras.js","names":[],"sources":["../../src/providers/cerebras.ts"],"sourcesContent":["import type { Provider, ProviderCapabilities } from '.'\nimport { openaiCompat } from './openai-compat'\n\nconst BASE_URL = 'https://api.cerebras.ai/v1'\n\nexport interface CerebrasParams {\n apiKey?: string\n defaultModel?: string\n /**\n * Provider capability flags. Cerebras currently serves text-only OSS models\n * (GLM, Llama-family, Qwen-family) — default: `{ vision: false, imageInToolResult: false }`.\n * Override when routing to a vision-capable deployment.\n */\n capabilities?: ProviderCapabilities\n /**\n * Extra HTTP headers sent on every request. Forwarded to the underlying\n * {@link openaiCompat} client — use for routing/attribution headers a proxy\n * in front of `baseURL` consumes.\n */\n extraHeaders?: Record<string, string>\n}\n\nfunction getApiKey(params?: CerebrasParams): string {\n // Resolution order is intentionally aligned with `anthropic` + `openai` —\n // explicit params > env var > fail. Host code that composes providers from\n // config (e.g. a YAML-driven CLI) relies on `params.apiKey` winning in all\n // providers, not just some.\n if (typeof params?.apiKey === 'string' && params.apiKey.length > 0)\n return params.apiKey\n\n if (process.env.CEREBRAS_API_KEY)\n return process.env.CEREBRAS_API_KEY\n\n throw new Error('No Cerebras API key found. Pass `apiKey` or set CEREBRAS_API_KEY in your environment.')\n}\n\n/**\n * Cerebras provider.\n *\n * Thin wrapper around {@link openaiCompat} with Cerebras-specific defaults\n * (base URL, default model).\n */\nexport function cerebras(params?: CerebrasParams): Provider {\n const apiKey = getApiKey(params)\n return openaiCompat({\n name: 'cerebras',\n apiKey,\n baseURL: BASE_URL,\n defaultModel: params?.defaultModel || 'gpt-oss-120b',\n capabilities: params?.capabilities ?? { vision: false, imageInToolResult: false },\n extraHeaders: params?.extraHeaders,\n })\n}\n"],"mappings":";;AAGA,MAAM,WAAW;AAmBjB,SAAS,UAAU,QAAiC;CAKlD,IAAI,OAAO,QAAQ,WAAW,YAAY,OAAO,OAAO,SAAS,GAC/D,OAAO,OAAO;CAEhB,IAAI,QAAQ,IAAI,kBACd,OAAO,QAAQ,IAAI;CAErB,MAAM,IAAI,MAAM,uFAAuF;AACzG;;;;;;;AAQA,SAAgB,SAAS,QAAmC;CAE1D,OAAO,aAAa;EAClB,MAAM;EACN,QAHa,UAAU,MAGlB;EACL,SAAS;EACT,cAAc,QAAQ,gBAAgB;EACtC,cAAc,QAAQ,gBAAgB;GAAE,QAAQ;GAAO,mBAAmB;EAAM;EAChF,cAAc,QAAQ;CACxB,CAAC;AACH"}
@@ -1,2 +1,2 @@
1
- import { Hp as local, Vp as LocalParams } from "../index-BYKzzYzi.js";
1
+ import { Hp as local, Vp as LocalParams } from "../index-ZWibV230.js";
2
2
  export { LocalParams, local };
@@ -1,4 +1,4 @@
1
- import { m as openaiCompat } from "../openai-compat-hmZzpDrB.js";
1
+ import { m as openaiCompat } from "../openai-compat-BoTi-9fd.js";
2
2
  //#region src/providers/local.ts
3
3
  function getBaseURL(params) {
4
4
  if (typeof params?.baseURL === "string" && params.baseURL.length > 0) return params.baseURL;