zidane 6.2.11 → 6.2.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{acp-DuEMbOoQ.js → acp-DxYjSCQW.js} +5 -5
- package/dist/{acp-DuEMbOoQ.js.map → acp-DxYjSCQW.js.map} +1 -1
- package/dist/acp-cli.js +6 -6
- package/dist/acp.d.ts +1 -1
- package/dist/acp.js +1 -1
- package/dist/{agent-6iGNP46b.js → agent-DoEx_WD3.js} +86 -5
- package/dist/{agent-6iGNP46b.js.map → agent-DoEx_WD3.js.map} +1 -1
- package/dist/agent.d.ts +1 -1
- package/dist/agent.js +2 -2
- package/dist/aliasing-lVFbm81n.js +217 -0
- package/dist/aliasing-lVFbm81n.js.map +1 -0
- package/dist/{anthropic-BD-lwEKp.js → anthropic-BYsc6qWj.js} +89 -4
- package/dist/anthropic-BYsc6qWj.js.map +1 -0
- package/dist/{auth-BH6G59RL.js → auth-Bb479DFB.js} +4 -4
- package/dist/{auth-BH6G59RL.js.map → auth-Bb479DFB.js.map} +1 -1
- package/dist/chat/pure.d.ts +3 -3
- package/dist/chat.d.ts +4 -4
- package/dist/chat.js +3 -3
- package/dist/contexts/daytona.d.ts +1 -1
- package/dist/contexts/e2b.d.ts +1 -1
- package/dist/eval.d.ts +1 -1
- package/dist/eval.js +1 -1
- package/dist/extensions.d.ts +1 -1
- package/dist/extensions.js +1 -1
- package/dist/headless.d.ts +1 -1
- package/dist/headless.js +3 -3
- package/dist/{index-BFNt9GAs.d.ts → index-l0wehkWU.d.ts} +44 -6
- package/dist/index-l0wehkWU.d.ts.map +1 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.js +9 -9
- package/dist/{login-B5nMPF8w.js → login-Clf-R27F.js} +2 -2
- package/dist/{login-B5nMPF8w.js.map → login-Clf-R27F.js.map} +1 -1
- package/dist/mcp.d.ts +1 -1
- package/dist/{messages-BDpXLFoe.js → messages-CvHfYB7f.js} +3 -3
- package/dist/{messages-BDpXLFoe.js.map → messages-CvHfYB7f.js.map} +1 -1
- package/dist/{openai-compat-J_JZ7qsw.js → openai-compat-pHI0kBWH.js} +37 -8
- package/dist/openai-compat-pHI0kBWH.js.map +1 -0
- package/dist/output/stream-json.d.ts +1 -1
- package/dist/output/terminal.d.ts +1 -1
- package/dist/presets.d.ts +1 -1
- package/dist/presets.js +1 -1
- package/dist/providers/anthropic.d.ts +1 -1
- package/dist/providers/anthropic.js +1 -1
- package/dist/providers/arcee.d.ts +1 -1
- package/dist/providers/arcee.js +1 -1
- package/dist/providers/baseten.d.ts +1 -1
- package/dist/providers/baseten.js +1 -1
- package/dist/providers/cerebras.d.ts +1 -1
- package/dist/providers/cerebras.js +1 -1
- package/dist/providers/local.d.ts +1 -1
- package/dist/providers/local.js +1 -1
- package/dist/providers/openai-compat.d.ts +1 -1
- package/dist/providers/openai-compat.js +1 -1
- package/dist/providers/openai.d.ts +1 -1
- package/dist/providers/openai.js +27 -10
- package/dist/providers/openai.js.map +1 -1
- package/dist/providers/openrouter.d.ts +1 -1
- package/dist/providers/openrouter.js +1 -1
- package/dist/providers/xai.d.ts +1 -1
- package/dist/providers/xai.js +1 -1
- package/dist/{providers-CclOzGxc.js → providers-D9Y2RndM.js} +3 -3
- package/dist/{providers-CclOzGxc.js.map → providers-D9Y2RndM.js.map} +1 -1
- package/dist/providers.d.ts +2 -2
- package/dist/providers.js +4 -4
- package/dist/{read-state-Dq_TXUX1.js → read-state-rDmfeLmz.js} +3 -196
- package/dist/read-state-rDmfeLmz.js.map +1 -0
- package/dist/restate.d.ts +1 -1
- package/dist/restate.js +2 -1
- package/dist/restate.js.map +1 -1
- package/dist/session/sqlite.d.ts +1 -1
- package/dist/{session-CIXE-Bzp.js → session-DzrXKLw-.js} +2 -2
- package/dist/{session-CIXE-Bzp.js.map → session-DzrXKLw-.js.map} +1 -1
- package/dist/session.d.ts +1 -1
- package/dist/session.js +2 -2
- package/dist/{skills-CDpejYOf.js → skills-DKWqoaYH.js} +4 -3
- package/dist/skills-DKWqoaYH.js.map +1 -0
- package/dist/skills.d.ts +1 -1
- package/dist/skills.js +1 -1
- package/dist/{tool-formatters-B_zQsYfa.d.ts → tool-formatters-BSwVkSX2.d.ts} +2 -2
- package/dist/{tool-formatters-B_zQsYfa.d.ts.map → tool-formatters-BSwVkSX2.d.ts.map} +1 -1
- package/dist/tools/fetch-url.d.ts +1 -1
- package/dist/tools/web-search.d.ts +1 -1
- package/dist/tools.d.ts +1 -1
- package/dist/tools.js +2 -2
- package/dist/{transcript-anchors-2wafjV-s.d.ts → transcript-anchors-DKjXpQ0h.d.ts} +2 -2
- package/dist/{transcript-anchors-2wafjV-s.d.ts.map → transcript-anchors-DKjXpQ0h.d.ts.map} +1 -1
- package/dist/{transcript-anchors-B_kUHJ16.js → transcript-anchors-yogJHuBW.js} +106 -34
- package/dist/transcript-anchors-yogJHuBW.js.map +1 -0
- package/dist/tui.d.ts +3 -3
- package/dist/tui.d.ts.map +1 -1
- package/dist/tui.js +201 -116
- package/dist/tui.js.map +1 -1
- package/dist/{turn-operations-DVA52KMG.d.ts → turn-operations-CjkgQKmR.d.ts} +3 -3
- package/dist/{turn-operations-DVA52KMG.d.ts.map → turn-operations-CjkgQKmR.d.ts.map} +1 -1
- package/dist/types.d.ts +1 -1
- package/dist/{xai-D-wALLuZ.js → xai-BwhzoehX.js} +2 -2
- package/dist/{xai-D-wALLuZ.js.map → xai-BwhzoehX.js.map} +1 -1
- package/package.json +1 -1
- package/dist/anthropic-BD-lwEKp.js.map +0 -1
- package/dist/hash-DJRzmcPI.js +0 -24
- package/dist/hash-DJRzmcPI.js.map +0 -1
- package/dist/index-BFNt9GAs.d.ts.map +0 -1
- package/dist/openai-compat-J_JZ7qsw.js.map +0 -1
- package/dist/read-state-Dq_TXUX1.js.map +0 -1
- package/dist/skills-CDpejYOf.js.map +0 -1
- package/dist/transcript-anchors-B_kUHJ16.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"messages-BDpXLFoe.js","names":[],"sources":["../src/session/messages.ts"],"sourcesContent":["/**\n * Canonical SessionMessage format with converters from/to Anthropic and OpenAI-compat formats.\n */\n\nimport type { Provider } from '../providers'\nimport type { ResolveContentRef, SessionContentBlock, SessionMessage, ToolResultContent, ToolResultDocumentContent } from '../types'\nimport { fnv1aBase36 } from '../hash'\nimport { ASSISTANT_TOOL_CALLS_TAG, TOOL_RESULTS_TAG } from '../providers/openai-compat'\nimport { audioFormatFromMediaType, reconcileImageMediaType } from '../tools/media-sniff'\nimport { assertResolvedMediaBlock, documentBlockMarker, isWireTurn } from '../types'\n\ntype ToolCallBlock = Extract<SessionContentBlock, { type: 'tool_call' }>\ninterface ContentRefBlock {\n type: 'image' | 'document'\n ref: string\n mediaType: string\n encoding?: 'base64' | 'text'\n name?: string\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Decode an Anthropic `tool_result.content` wire value back to zidane's canonical\n * `string | ToolResultContent[]` shape.\n *\n * Anthropic accepts three shapes in the wire: `string`, `Array<{type:'text',text}>`,\n * or `Array<{type:'text'|'image', ...}>`. We collapse to `string` only when every\n * block is text to keep simple cases readable.\n */\nfunction decodeAnthropicToolResultContent(content: unknown): string | ToolResultContent[] {\n if (typeof content === 'string')\n return content\n\n if (!Array.isArray(content))\n return JSON.stringify(content)\n\n const blocks: ToolResultContent[] = []\n for (const raw of content) {\n if (!raw || typeof raw !== 'object')\n continue\n const b = raw as Record<string, unknown>\n if (b.type === 'text' && typeof b.text === 'string') {\n blocks.push({ type: 'text', text: b.text })\n continue\n }\n if (b.type === 'image' && b.source && typeof b.source === 'object') {\n const src = b.source as Record<string, unknown>\n if (src.type === 'base64' && typeof src.data === 'string' && typeof src.media_type === 'string') {\n blocks.push({ type: 'image', mediaType: src.media_type, data: src.data })\n continue\n }\n }\n // Unknown block → stringify into text for visibility.\n blocks.push({ type: 'text', text: JSON.stringify(raw) })\n }\n\n if (blocks.length === 0)\n return ''\n\n const hasNonText = blocks.some(b => b.type !== 'text')\n if (!hasNonText)\n return blocks.map(b => (b as { type: 'text', text: string }).text).join('\\n')\n\n return blocks\n}\n\n/**\n * Build an Anthropic `image.source` block, reconciling the declared media\n * type against the actual magic bytes.\n *\n * Anthropic's API rejects the entire request when `media_type` disagrees\n * with the payload (`messages.N.content.K.image.source.base64: The image\n * was specified using the X media type, but the image appears to be a\n * Y image`). The upstream sources of `{type:'image'}` blocks each sniff\n * before producing the block (read_file / MCP / TUI paste), but this\n * wire-boundary reconciliation is a final safety net for any future code\n * path or host-supplied prompt part that slips through without sniffing.\n *\n * The reconciler is conservative: it overrides only when the magic bytes\n * positively identify a *different* known format. Unknown / too-short\n * payloads pass `mediaType` through unchanged.\n */\nfunction anthropicImageSource(mediaType: string, data: string): { type: 'base64', media_type: string, data: string } {\n return { type: 'base64' as const, media_type: reconcileImageMediaType(mediaType, data), data }\n}\n\nfunction anthropicDocumentBlocks(doc: Pick<ToolResultDocumentContent, 'mediaType' | 'data' | 'encoding' | 'name'>): AnthropicWireBlock[] {\n if (doc.encoding === 'base64' && doc.mediaType === 'application/pdf') {\n return [{\n type: 'document',\n source: { type: 'base64', media_type: 'application/pdf', data: doc.data },\n ...(doc.name ? { title: doc.name } : {}),\n }]\n }\n\n const header = doc.name\n ? `<attachment name=\"${doc.name}\" media_type=\"${doc.mediaType}\" encoding=\"${doc.encoding}\">`\n : `<attachment media_type=\"${doc.mediaType}\" encoding=\"${doc.encoding}\">`\n return [{ type: 'text', text: `${header}\\n${doc.data}\\n</attachment>` }]\n}\n\nfunction documentMarker(doc: ToolResultDocumentContent): string {\n return documentBlockMarker(doc, 'document attached')\n}\n\nfunction isContentRefBlock(block: SessionContentBlock | ToolResultContent): block is (SessionContentBlock | ToolResultContent) & ContentRefBlock {\n return (block.type === 'image' || block.type === 'document')\n && typeof (block as { ref?: unknown }).ref === 'string'\n}\n\nfunction unresolvedContentRefError(block: ContentRefBlock): Error {\n return new Error(`Cannot project content ref \"${block.ref}\" to provider wire messages without resolveContentRef.`)\n}\n\n/**\n * Coerce a tool-call id to Anthropic's wire constraint `^[a-zA-Z0-9_-]+$`.\n *\n * Sessions started under another provider (OpenAI-compat / OpenRouter /\n * Cerebras) or carrying MCP-namespaced calls can hold ids with `.`, `:`,\n * `/` or other characters that 400 the request the moment the user switches\n * to an Anthropic model (`messages.N.content.M.tool_use.id: String should\n * match pattern '^[a-zA-Z0-9_-]+$'`).\n *\n * The mapping is deterministic — every disallowed char becomes `_` — so a\n * `tool_use` and its paired `tool_result` (sanitized identically below) keep\n * matching ids and the pairing survives. When sanitization actually changed\n * the id, a short hash of the ORIGINAL id is appended so distinct inputs\n * that would collapse to the same replacement (`call.1` vs `call_1` vs\n * `call:1`) stay distinct on the wire. Empty ids get a stable placeholder\n * so the block stays well-formed.\n */\nconst ANTHROPIC_TOOL_ID_INVALID = /[^\\w-]/g\nfunction sanitizeAnthropicToolId(id: string): string {\n if (!id)\n return 'tool_0'\n // `replace` with a global regex is idempotent for already-valid ids and\n // avoids the stateful `lastIndex` pitfall of reusing a `/g` regex `.test()`.\n const sanitized = id.replace(ANTHROPIC_TOOL_ID_INVALID, '_')\n if (sanitized === id)\n return id\n // Append a short hash of the ORIGINAL id so distinct inputs that collapse to\n // the same replacement stay distinct, deterministically, so a `tool_use` and\n // its paired `tool_result` keep matching ids.\n return `${sanitized}_${fnv1aBase36(id)}`\n}\n\n/**\n * Encode zidane's canonical `string | ToolResultContent[]` shape to Anthropic's\n * wire format for `tool_result.content`.\n */\nfunction encodeAnthropicToolResultContent(\n output: string | ToolResultContent[],\n): string | Array<{ type: 'text', text: string } | { type: 'image', source: { type: 'base64', media_type: string, data: string } }> {\n if (typeof output === 'string')\n return output\n return output.map((b) => {\n if (b.type === 'text')\n return { type: 'text' as const, text: b.text }\n if (b.type === 'document') {\n assertResolvedMediaBlock(b, 'Anthropic wire messages')\n return { type: 'text' as const, text: documentMarker(b) }\n }\n if (b.type === 'audio')\n return { type: 'text' as const, text: `[audio omitted: ${b.mediaType}]` }\n if (b.type === 'video')\n return { type: 'text' as const, text: `[video omitted: ${b.mediaType}]` }\n assertResolvedMediaBlock(b, 'Anthropic wire messages')\n return {\n type: 'image' as const,\n source: anthropicImageSource(b.mediaType, b.data),\n }\n })\n}\n\n// ---------------------------------------------------------------------------\n// fromAnthropic\n// ---------------------------------------------------------------------------\n\nexport function fromAnthropic(msg: { role: string, content: unknown }): SessionMessage {\n const role = msg.role as 'user' | 'assistant'\n const content: SessionContentBlock[] = []\n\n if (typeof msg.content === 'string') {\n content.push({ type: 'text', text: msg.content })\n return { role, content }\n }\n\n if (Array.isArray(msg.content)) {\n for (const block of msg.content) {\n if (!block || typeof block !== 'object')\n continue\n const b = block as Record<string, unknown>\n\n if (b.type === 'text') {\n content.push({ type: 'text', text: b.text as string })\n }\n else if (b.type === 'image') {\n const source = b.source as Record<string, unknown>\n if (source?.type === 'base64') {\n content.push({ type: 'image', mediaType: source.media_type as string, data: source.data as string })\n }\n }\n else if (b.type === 'document') {\n const source = b.source as Record<string, unknown>\n if (source?.type === 'base64' && typeof source.media_type === 'string' && typeof source.data === 'string') {\n content.push({\n type: 'document',\n mediaType: source.media_type,\n data: source.data,\n encoding: 'base64',\n ...(typeof b.title === 'string' ? { name: b.title } : {}),\n })\n }\n else if (source?.type === 'text' && typeof source.media_type === 'string' && typeof source.data === 'string') {\n content.push({\n type: 'document',\n mediaType: source.media_type,\n data: source.data,\n encoding: 'text',\n ...(typeof b.title === 'string' ? { name: b.title } : {}),\n })\n }\n }\n else if (b.type === 'tool_use') {\n content.push({ type: 'tool_call', id: b.id as string, name: b.name as string, input: b.input as Record<string, unknown> })\n }\n else if (b.type === 'tool_result') {\n const output = decodeAnthropicToolResultContent(b.content)\n const block: Extract<SessionContentBlock, { type: 'tool_result' }> = {\n type: 'tool_result',\n callId: b.tool_use_id as string,\n output,\n }\n if (b.is_error === true)\n block.isError = true\n content.push(block)\n }\n else if (b.type === 'thinking') {\n const block: Extract<SessionContentBlock, { type: 'thinking' }> = {\n type: 'thinking',\n text: (b.thinking as string | undefined) ?? '',\n }\n if (typeof b.signature === 'string') {\n block.signature = b.signature\n // Tag the producer so cross-provider senders can drop foreign signatures.\n block.signatureProducer = 'anthropic'\n }\n content.push(block)\n }\n else if (b.type === 'redacted_thinking') {\n // Encrypted reasoning that must be echoed back unmodified to keep\n // interleaved-thinking + tool-use chains working on Sonnet 3.7+/Opus 4.x.\n content.push({ type: 'redacted_thinking', data: (b.data as string | undefined) ?? '' })\n }\n else if (b.type === 'server_tool_use') {\n // Anthropic server-tool invocation (e.g. `web_search_20250305`). Captured\n // so multi-turn citation context survives — Anthropic's docs require\n // these blocks to be echoed back on follow-up turns.\n content.push({\n type: 'server_tool_use',\n producer: 'anthropic',\n id: (b.id as string | undefined) ?? '',\n name: (b.name as string | undefined) ?? '',\n input: (b.input as Record<string, unknown> | undefined) ?? {},\n })\n }\n else if (b.type === 'web_search_tool_result') {\n // Pairs with `server_tool_use`. `content` is opaque (search hit array\n // with `encrypted_content` / `encrypted_index`) — store unchanged.\n content.push({\n type: 'server_tool_result',\n producer: 'anthropic',\n toolUseId: (b.tool_use_id as string | undefined) ?? '',\n toolName: 'web_search',\n content: b.content,\n })\n }\n // Unknown block types are silently skipped\n }\n }\n\n return { role, content }\n}\n\n// ---------------------------------------------------------------------------\n// fromOpenAI\n// ---------------------------------------------------------------------------\n\nexport function fromOpenAI(msg: { role: string, content: unknown }): SessionMessage {\n const role = msg.role as 'user' | 'assistant'\n const content: SessionContentBlock[] = []\n const c: unknown = msg.content\n\n if (c == null) {\n return { role, content }\n }\n\n if (typeof c === 'string') {\n content.push({ type: 'text', text: c })\n return { role, content }\n }\n\n if (typeof c === 'object' && !Array.isArray(c) && (c as { _tag?: unknown })._tag === ASSISTANT_TOOL_CALLS_TAG) {\n const tagged = c as { text?: unknown, tool_calls?: unknown }\n if (typeof tagged.text === 'string' && tagged.text) {\n content.push({ type: 'text', text: tagged.text })\n }\n if (Array.isArray(tagged.tool_calls)) {\n for (const raw of tagged.tool_calls) {\n if (!raw || typeof raw !== 'object')\n continue\n const tc = raw as { id?: string, function?: { name?: string, arguments?: unknown } }\n const rawArgs = tc.function?.arguments\n // Malformed argument JSON (truncated stream, hand-edited history)\n // must not kill the whole conversion — fall back to `{}`, same\n // degradation `consumeSSE` applies to truncated tool-call args.\n let input: Record<string, unknown> = {}\n if (typeof rawArgs === 'string') {\n try {\n input = JSON.parse(rawArgs) as Record<string, unknown>\n }\n catch {\n input = {}\n }\n }\n else if (rawArgs && typeof rawArgs === 'object') {\n input = rawArgs as Record<string, unknown>\n }\n content.push({ type: 'tool_call', id: tc.id ?? '', name: tc.function?.name ?? '', input })\n }\n }\n return { role, content }\n }\n\n if (typeof c === 'object' && !Array.isArray(c) && (c as { _tag?: unknown })._tag === TOOL_RESULTS_TAG) {\n const tagged = c as { results?: unknown, text?: unknown }\n if (Array.isArray(tagged.results)) {\n for (const raw of tagged.results) {\n if (!raw || typeof raw !== 'object')\n continue\n const r = raw as { tool_call_id?: string, content?: string | ToolResultContent[] }\n content.push({ type: 'tool_result', callId: r.tool_call_id ?? '', output: r.content ?? '' })\n }\n }\n // Sibling text preserved by `toOpenAI` alongside the results.\n if (typeof tagged.text === 'string' && tagged.text)\n content.push({ type: 'text', text: tagged.text })\n return { role, content }\n }\n\n if (Array.isArray(c)) {\n for (const block of c) {\n if (!block || typeof block !== 'object')\n continue\n const b = block as Record<string, unknown>\n\n if (b.type === 'text') {\n content.push({ type: 'text', text: b.text as string })\n }\n else if (b.type === 'image_url') {\n const imageUrl = (b.image_url as Record<string, unknown>)?.url as string\n if (imageUrl?.startsWith('data:')) {\n const [meta, data] = imageUrl.slice(5).split(',', 2)\n const mediaType = meta.replace(';base64', '')\n content.push({ type: 'image', mediaType, data })\n }\n }\n }\n return { role, content }\n }\n\n return { role, content }\n}\n\n// ---------------------------------------------------------------------------\n// toAnthropic\n// ---------------------------------------------------------------------------\n\ntype AnthropicWireBlock\n = | { type: 'text', text: string }\n | { type: 'image', source: { type: 'base64', media_type: string, data: string } }\n | { type: 'document', source: { type: 'base64' | 'text', media_type: string, data: string }, title?: string }\n | { type: 'tool_use', id: string, name: string, input: Record<string, unknown> }\n | { type: 'tool_result', tool_use_id: string, content: ReturnType<typeof encodeAnthropicToolResultContent>, is_error?: true }\n | { type: 'thinking', thinking: string, signature: string }\n | { type: 'redacted_thinking', data: string }\n | { type: 'server_tool_use', id: string, name: string, input: Record<string, unknown> }\n | { type: 'web_search_tool_result', tool_use_id: string, content: unknown }\n\nexport function toAnthropic(msg: SessionMessage): { role: string, content: unknown } {\n const blocks: AnthropicWireBlock[] = msg.content\n // Drop foreign reasoning state. Three independent rejection paths,\n // each driven by an Anthropic API constraint we've hit in production:\n //\n // 1. `signatureProducer === 'openai'` — OpenAI's `encrypted_content`\n // isn't a valid Anthropic HMAC; sending it 400s with an unhelpful\n // \"invalid signature\" error.\n // 2. `type === 'thinking' && !signature` — Anthropic's\n // `ThinkingBlockParam` requires a signature field. A non-Anthropic\n // producer that doesn't sign thinking (Cerebras, Gemini, OSS via\n // OpenAI-compat, etc.) leaves a block in the session that 400s\n // here with `messages.N.content.M.thinking.signature: Field\n // required` the next time the user picks an Anthropic model. Drop\n // them — the model can re-think on its own turn, no info loss\n // beyond the prior provider's chain-of-thought, which Anthropic\n // can't consume anyway.\n // 3. `type === 'provider_reasoning'` — opaque provider-bound reasoning\n // state; not an Anthropic block type at all.\n .filter((b) => {\n if (b.type === 'provider_reasoning')\n return false\n if (b.type !== 'thinking')\n return true\n if (b.signatureProducer === 'openai')\n return false\n if (!b.signature)\n return false\n return true\n })\n .flatMap((block): AnthropicWireBlock[] => {\n switch (block.type) {\n case 'text':\n return [{ type: 'text', text: block.text }]\n case 'image':\n assertResolvedMediaBlock(block, 'Anthropic wire messages')\n return [{ type: 'image', source: anthropicImageSource(block.mediaType, block.data) }]\n case 'audio':\n // Anthropic has no audio input block. The prompt path throws before a\n // top-level audio block can reach here; this is the defensive marker\n // for any other code path (e.g. a cross-provider session replay).\n return [{ type: 'text', text: `[audio omitted: ${block.mediaType}]` }]\n case 'video':\n return [{ type: 'text', text: `[video omitted: ${block.mediaType}]` }]\n case 'document':\n assertResolvedMediaBlock(block, 'Anthropic wire messages')\n return anthropicDocumentBlocks(block)\n case 'tool_call':\n return [{ type: 'tool_use', id: sanitizeAnthropicToolId(block.id), name: block.name, input: block.input }]\n case 'tool_result': {\n const out: AnthropicWireBlock = {\n type: 'tool_result',\n tool_use_id: sanitizeAnthropicToolId(block.callId),\n content: encodeAnthropicToolResultContent(block.output),\n }\n if (block.isError)\n out.is_error = true\n const docs = typeof block.output === 'string'\n ? []\n : block.output.filter((part): part is ToolResultDocumentContent => part.type === 'document' && typeof part.data === 'string')\n return [out, ...docs.flatMap(anthropicDocumentBlocks)]\n }\n case 'thinking':\n // Filter above guarantees `signature` is present and either\n // anthropic-produced or legacy (back-compat for pre-tagging\n // Anthropic sessions where producer was never recorded).\n return [{ type: 'thinking', thinking: block.text, signature: block.signature ?? '' }]\n case 'redacted_thinking':\n return [{ type: 'redacted_thinking', data: block.data }]\n case 'server_tool_use':\n // Anthropic-produced server-tool invocation echoed back so the\n // model can resume the citation chain. Producer is always\n // `anthropic` (other providers don't emit this variant).\n return [{ type: 'server_tool_use', id: sanitizeAnthropicToolId(block.id), name: block.name, input: block.input }]\n case 'server_tool_result':\n return [{ type: 'web_search_tool_result', tool_use_id: sanitizeAnthropicToolId(block.toolUseId), content: block.content }]\n default:\n return [{ type: 'text', text: '' }]\n }\n })\n\n // Simplify to plain string if only one text block\n const first = blocks[0]\n if (blocks.length === 1 && first.type === 'text')\n return { role: msg.role, content: first.text }\n\n return { role: msg.role, content: blocks }\n}\n\n// ---------------------------------------------------------------------------\n// toOpenAI\n// ---------------------------------------------------------------------------\n\nexport function toOpenAI(msg: SessionMessage): { role: string, content: unknown } {\n const toolCalls = msg.content.filter(b => b.type === 'tool_call')\n const toolResults = msg.content.filter(b => b.type === 'tool_result')\n const textBlocks = msg.content.filter(b => b.type === 'text')\n const imageBlocks = msg.content.filter(b => b.type === 'image')\n const audioBlocks = msg.content.filter(b => b.type === 'audio')\n const videoBlocks = msg.content.filter(b => b.type === 'video')\n const documentBlocks = msg.content.filter(b => b.type === 'document')\n\n // Tool results → tagged format. Sibling text blocks (a user follow-up\n // typed while tools ran) ride along on `text` — the early return here\n // used to silently drop them, breaking round-trip fidelity vs. the wire\n // shaper (`toOAIMessages`), which preserves them.\n if (toolResults.length > 0) {\n const siblingText = textBlocks.map(b => b.text).join('\\n')\n return {\n role: msg.role,\n content: {\n _tag: TOOL_RESULTS_TAG,\n results: toolResults.map(tr => ({ tool_call_id: tr.callId, content: tr.output })),\n ...(siblingText ? { text: siblingText } : {}),\n },\n }\n }\n\n // Tool calls → tagged format\n if (toolCalls.length > 0) {\n const textContent = textBlocks.length > 0 ? textBlocks[0].text : null\n return {\n role: msg.role,\n content: {\n _tag: ASSISTANT_TOOL_CALLS_TAG,\n text: textContent,\n tool_calls: toolCalls.map(tc => ({\n id: tc.id,\n type: 'function',\n function: { name: tc.name, arguments: JSON.stringify(tc.input) },\n })),\n },\n }\n }\n\n // Images/audio/video/documents → multimodal array. Documents have no portable\n // OpenAI Chat Completions block here, so preserve them as markers.\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({ type: 'input_audio', input_audio: { data: b.data, format: audioFormatFromMediaType(b.mediaType) } })\n for (const b of videoBlocks)\n parts.push({ type: 'video_url', video_url: { url: `data:${b.mediaType};base64,${b.data}` } })\n for (const b of documentBlocks) {\n assertResolvedMediaBlock(b, 'OpenAI-compatible wire messages')\n parts.push({ type: 'text', text: documentMarker(b) })\n }\n for (const b of textBlocks)\n parts.push({ type: 'text', text: b.text })\n return { role: msg.role, content: parts }\n }\n\n // Pure text → string\n if (textBlocks.length === 1)\n return { role: msg.role, content: textBlocks[0].text }\n\n // Multiple text blocks or empty\n if (textBlocks.length > 1)\n return { role: msg.role, content: textBlocks.map(b => ({ type: 'text', text: b.text })) }\n\n return { role: msg.role, content: null }\n}\n\n// ---------------------------------------------------------------------------\n// autoDetectAndConvert\n// ---------------------------------------------------------------------------\n\n// ---------------------------------------------------------------------------\n// ensureToolResultPairing\n// ---------------------------------------------------------------------------\n\n/**\n * Placeholder content inserted into a synthetic `tool_result` when the harness\n * has to repair an orphan `tool_use`. Exported so downstream consumers\n * (training-data collectors, HFI submission) can reject any payload containing\n * it — the marker satisfies the wire-level pairing contract structurally but\n * the content itself is fake and would poison fine-tuning data.\n */\nexport const SYNTHETIC_TOOL_RESULT_PLACEHOLDER = '[Tool result missing due to internal error]'\n\n/**\n * Replacement text for an assistant message whose every content block was\n * stripped during pairing repair (e.g. the only blocks were orphan\n * `tool_call`s). Providers reject empty `content` arrays — the marker keeps\n * the turn shape valid while signaling \"this assistant turn lost its\n * outputs\" to the model so it can recover.\n */\nexport const TOOL_USE_INTERRUPTED_MARKER = '[Tool use interrupted]'\n\n/**\n * Replacement text for a user message whose every content block was a\n * `tool_result` with no matching upstream `tool_call` (e.g. the assistant\n * pair was stripped by an earlier compaction). Same role as\n * {@link TOOL_USE_INTERRUPTED_MARKER} on the user side.\n */\nexport const ORPHANED_TOOL_RESULT_MARKER = '[Orphaned tool result removed due to conversation resume]'\n\n/**\n * Classification of a single repair the pairing pass performed. Surfaced via\n * {@link EnsureToolResultPairingOptions.onRepair} so consumers can wire\n * telemetry, debug logs, or strict-mode rejection without re-implementing the\n * walk.\n *\n * Each entry corresponds to one of the corruption modes documented on\n * {@link ensureToolResultPairing}.\n */\nexport type PairingRepairMode\n = | 'orphan-tool-use-prepend' // mode 1: tool_use with no matching tool_result; next msg is user → prepend synthetic\n | 'orphan-tool-use-append' // mode 2: tool_use is followed by nothing (or a non-user msg) → append synthetic user turn\n | 'orphan-tool-result-strip' // mode 3: tool_result with no preceding tool_call → strip block\n | 'duplicate-tool-use-strip' // mode 4: duplicate tool_use id across assistant msgs → strip later instance\n | 'duplicate-tool-result-strip' // mode 5: duplicate tool_result callId in one user msg → strip later instance\n | 'empty-assistant-marker' // mode 6: assistant msg emptied by mode-4 stripping → replace with marker text\n | 'duplicate-tool-use-remint' // persist-time: fresh turn reused an existing tool_call id → re-minted before persist (loop-side, not part of ensureToolResultPairing)\n\nexport interface PairingRepair {\n mode: PairingRepairMode\n /** Tool-use / tool-result id this repair concerns. Absent for empty-marker repairs. */\n callId?: string\n /** Zero-based index into the INPUT `messages` array where the corruption was found. */\n messageIndex: number\n}\n\nexport interface EnsureToolResultPairingOptions {\n /**\n * Fired once per repair the pass performed. Synchronous so the loop can\n * stay on its hot path. Throwing from the callback aborts the pass —\n * consumers wanting fail-fast (strict mode) can re-throw with their own\n * typed error.\n */\n onRepair?: (repair: PairingRepair) => void\n}\n\n/**\n * Defensive repair pass that rewrites a message list so it satisfies the\n * wire-level `tool_use` ↔ `tool_result` adjacency contract every modern\n * provider enforces.\n *\n * Anthropic 400s on orphans with `'tool_use' ids were found without\n * 'tool_result' blocks immediately after` (and its inverse, `tool_result\n * must be preceded by a tool_call with the same toolCallId`). OpenAI's\n * Chat Completions API rejects most mismatches with a 400 too.\n *\n * Six repair modes — modeled after Anthropic's Claude Code defenses:\n *\n * | # | Corruption | Repair |\n * |---|------------|--------|\n * | 1 | assistant `tool_use` whose next user msg lacks a matching `tool_result` | **Prepend** a synthetic `tool_result` block carrying {@link SYNTHETIC_TOOL_RESULT_PLACEHOLDER} with `isError: true` |\n * | 2 | assistant `tool_use` followed by nothing (or by a non-user msg) | **Insert** a new synthetic user message with the same placeholder |\n * | 3 | user `tool_result` with no preceding assistant `tool_call` | **Strip** the orphan block; if it empties the msg, replace with {@link ORPHANED_TOOL_RESULT_MARKER} text block |\n * | 4 | duplicate `tool_call.id` across assistant messages (CC-1212) | **Strip** later instances + their matching tool_results |\n * | 5 | duplicate `tool_result.callId` within a single user message | **Dedupe** by `callId`, keep first |\n * | 6 | assistant message emptied by mode-4 stripping | **Replace** content with {@link TOOL_USE_INTERRUPTED_MARKER} text block |\n *\n * **Repair, not drop.** Earlier versions of this pass simply dropped orphan\n * blocks, which (a) silently rewrote history the model had reasoned over and\n * (b) cascaded — dropping an assistant tool_call would orphan its\n * tool_result, which would then get dropped too, removing any trace of\n * \"what the model tried to do\" from the transcript. The repair-based pass\n * preserves the model's tool_use shape and patches the dangling result with\n * an `is_error` placeholder so the model sees \"I tried X, the result was\n * lost\" and can retry intelligently.\n *\n * Adjacency contract: `tool_result` blocks must live in the user message\n * IMMEDIATELY following the assistant message that emitted the matching\n * `tool_use`. The pass enforces strict adjacency — a tool_result two\n * messages downstream of its tool_call is still an orphan.\n *\n * Idempotent: returns the input reference unchanged when no repairs were\n * necessary. Re-running on already-repaired output is a no-op.\n *\n * Pure: does not mutate input messages or their content arrays — every\n * repair allocates a fresh array / object.\n */\nexport function ensureToolResultPairing(\n messages: SessionMessage[],\n options: EnsureToolResultPairingOptions = {},\n): SessionMessage[] {\n if (messages.length === 0)\n return messages\n\n const fireRepair = options.onRepair ?? (() => {})\n\n // Pre-pass: first-occurrence index of every assistant `tool_call.id`.\n // Mode 4 strips any block whose first occurrence wasn't in the current\n // message — the first instance wins so the original semantics stand.\n const firstSeenAt = new Map<string, number>()\n for (let i = 0; i < messages.length; i++) {\n const msg = messages[i]\n if (msg.role !== 'assistant')\n continue\n for (const block of msg.content) {\n if (block.type === 'tool_call' && !firstSeenAt.has(block.id))\n firstSeenAt.set(block.id, i)\n }\n }\n\n // Scoped to this call. Keyed by INPUT index of the user message that\n // should receive prepended synthetic results from the prior assistant\n // turn's mode-1 repair. Drained as the outer loop reaches each user msg.\n const pendingOrphansByUserIndex = new Map<number, string[]>()\n\n let changed = false\n const markChanged = () => { changed = true }\n const out: SessionMessage[] = []\n\n for (let i = 0; i < messages.length; i++) {\n const msg = messages[i]\n\n if (msg.role === 'assistant') {\n processAssistantMessage({\n index: i,\n msg,\n messages,\n firstSeenAt,\n pendingOrphansByUserIndex,\n out,\n fireRepair,\n markChanged,\n })\n }\n else {\n processUserMessage({\n index: i,\n msg,\n pendingOrphansByUserIndex,\n out,\n fireRepair,\n markChanged,\n })\n }\n }\n\n return changed ? out : messages\n}\n\ninterface ProcessAssistantArgs {\n index: number\n msg: SessionMessage\n messages: SessionMessage[]\n firstSeenAt: Map<string, number>\n pendingOrphansByUserIndex: Map<number, string[]>\n out: SessionMessage[]\n fireRepair: (repair: PairingRepair) => void\n markChanged: () => void\n}\n\n/**\n * Process one assistant message: strip mode-4 duplicates, queue mode-1 / 2\n * orphan-tool-use repairs for the next user message (or insert a synthetic\n * one), and emit a mode-6 marker when dedup empties the message.\n */\nfunction processAssistantMessage(args: ProcessAssistantArgs): void {\n const { index, msg, messages, firstSeenAt, pendingOrphansByUserIndex, out, fireRepair, markChanged } = args\n\n // Mode 4: drop tool_call blocks that are duplicates either:\n // - across messages (first occurrence was in an earlier assistant msg), or\n // - within this same message (same id appearing twice in one turn —\n // pathological but observed in the wild with retry-replay providers).\n //\n // The per-message `seenHere` set guards against the intra-message case:\n // `firstSeenAt` records only the first OCCURRENCE per id, so two blocks\n // sharing an id within the same message both pass the cross-message\n // check (`firstSeenAt.get(id) === index`). The local set tracks which\n // ids we've already kept this iteration and strips later repeats.\n const dedupedContent: SessionContentBlock[] = []\n const seenHere = new Set<string>()\n let dedupedChanged = false\n for (const block of msg.content) {\n if (block.type === 'tool_call') {\n const isCrossMsgDup = firstSeenAt.get(block.id) !== index\n const isIntraMsgDup = seenHere.has(block.id)\n if (isCrossMsgDup || isIntraMsgDup) {\n dedupedChanged = true\n fireRepair({ mode: 'duplicate-tool-use-strip', callId: block.id, messageIndex: index })\n continue\n }\n seenHere.add(block.id)\n }\n dedupedContent.push(block)\n }\n\n const surviving = collectIds<ToolCallBlock>(dedupedContent, 'tool_call', b => b.id)\n const next = messages[index + 1]\n const nextIsUser = next && next.role === 'user'\n const nextResultIds = nextIsUser ? collectResultIds(next.content) : new Set<string>()\n const orphans: string[] = []\n for (const id of surviving) {\n if (!nextResultIds.has(id))\n orphans.push(id)\n }\n\n // Emit the (possibly deduped) assistant message — modes 1 / 2 patch the\n // matching user side rather than touching tool_use blocks.\n emitAssistantAfterDedup({ index, msg, dedupedContent, dedupedChanged, out, fireRepair, markChanged })\n\n if (orphans.length === 0)\n return\n\n // Mode 1: stash for the next user msg processor to prepend.\n if (nextIsUser) {\n markChanged()\n pendingOrphansByUserIndex.set(index + 1, orphans)\n for (const callId of orphans)\n fireRepair({ mode: 'orphan-tool-use-prepend', callId, messageIndex: index })\n return\n }\n\n // Mode 2: synthesize a user message with placeholders for every orphan.\n markChanged()\n out.push({\n role: 'user',\n content: orphans.map(callId => syntheticResultBlock(callId)),\n })\n for (const callId of orphans)\n fireRepair({ mode: 'orphan-tool-use-append', callId, messageIndex: index })\n}\n\ninterface EmitAssistantArgs {\n index: number\n msg: SessionMessage\n dedupedContent: SessionContentBlock[]\n dedupedChanged: boolean\n out: SessionMessage[]\n fireRepair: (repair: PairingRepair) => void\n markChanged: () => void\n}\n\nfunction emitAssistantAfterDedup(args: EmitAssistantArgs): void {\n const { index, msg, dedupedContent, dedupedChanged, out, fireRepair, markChanged } = args\n if (!dedupedChanged) {\n out.push(msg)\n return\n }\n markChanged()\n if (dedupedContent.length === 0) {\n out.push({ ...msg, content: [{ type: 'text', text: TOOL_USE_INTERRUPTED_MARKER }] })\n fireRepair({ mode: 'empty-assistant-marker', messageIndex: index })\n return\n }\n out.push({ ...msg, content: dedupedContent })\n}\n\ninterface ProcessUserArgs {\n index: number\n msg: SessionMessage\n pendingOrphansByUserIndex: Map<number, string[]>\n out: SessionMessage[]\n fireRepair: (repair: PairingRepair) => void\n markChanged: () => void\n}\n\n/**\n * Process one user message: dedup mode-5 duplicate tool_results, strip\n * mode-3 orphans whose tool_call is missing from the previously-emitted\n * assistant message, and prepend any synthetic results queued by the prior\n * assistant's mode-1 repair.\n */\nfunction processUserMessage(args: ProcessUserArgs): void {\n const { index, msg, pendingOrphansByUserIndex, out, fireRepair, markChanged } = args\n const queuedOrphans = pendingOrphansByUserIndex.get(index)\n pendingOrphansByUserIndex.delete(index)\n\n const prev = out.length > 0 ? out[out.length - 1] : null\n const validIds = prev && prev.role === 'assistant'\n ? collectIds<ToolCallBlock>(prev.content, 'tool_call', b => b.id)\n : new Set<string>()\n\n // Pass 1 — mode 5: dedupe by callId within this msg.\n const seenCallIds = new Set<string>()\n let modifiedHere = false\n const afterDedup: SessionContentBlock[] = []\n for (const block of msg.content) {\n if (block.type !== 'tool_result') {\n afterDedup.push(block)\n continue\n }\n if (seenCallIds.has(block.callId)) {\n modifiedHere = true\n fireRepair({ mode: 'duplicate-tool-result-strip', callId: block.callId, messageIndex: index })\n continue\n }\n seenCallIds.add(block.callId)\n afterDedup.push(block)\n }\n\n // Pass 2 — mode 3: strip orphan tool_results.\n const afterStrip: SessionContentBlock[] = []\n for (const block of afterDedup) {\n if (block.type === 'tool_result' && !validIds.has(block.callId)) {\n modifiedHere = true\n fireRepair({ mode: 'orphan-tool-result-strip', callId: block.callId, messageIndex: index })\n continue\n }\n afterStrip.push(block)\n }\n\n // Pass 3: prepend queued mode-1 synthetic results.\n const finalContent: SessionContentBlock[] = queuedOrphans\n ? [...queuedOrphans.map(syntheticResultBlock), ...afterStrip]\n : afterStrip\n\n if (!modifiedHere && queuedOrphans === undefined) {\n out.push(msg)\n return\n }\n\n markChanged()\n if (finalContent.length === 0) {\n out.push({ ...msg, content: [{ type: 'text', text: ORPHANED_TOOL_RESULT_MARKER }] })\n return\n }\n out.push({ ...msg, content: finalContent })\n}\n\n// ---------------------------------------------------------------------------\n// remintDuplicateToolCallIds — persist-time duplicate-id healing\n// ---------------------------------------------------------------------------\n\nexport interface RemintResult {\n /** Tool calls with duplicate ids replaced. Same reference when unchanged. */\n toolCalls: Array<{ id: string, name: string, input: Record<string, unknown> }>\n /** Assistant content with matching `tool_call` block ids rewritten. Same reference when unchanged. */\n content: SessionContentBlock[]\n /** One entry per re-minted occurrence, in emission order. */\n reminted: Array<{ oldId: string, newId: string }>\n}\n\n/**\n * Re-mint tool-call ids that collide with ids already present in the\n * transcript (or with an earlier sibling in the same turn), BEFORE the turn\n * is persisted and its tools dispatched.\n *\n * Why this exists: some providers/serving stacks emit non-globally-unique\n * tool_use ids (per-turn counters like `call_0`, retry-replay duplicates,\n * Kimi-K2.6 on Fireworks). The wire-time pairing pass\n * ({@link ensureToolResultPairing}) can only STRIP such duplicates — which\n * erases the model's tool calls and results from the wire view, stalls\n * progress, and re-accumulates repairs quadratically because the persisted\n * session keeps the corrupted ids. Re-minting at persist time keeps the\n * pairing intact instead: the call, its execution, and its tool_result all\n * carry the fresh id, so the pairing pass has nothing to strip on any later\n * turn.\n *\n * Fresh ids are `${oldId}_r{n}` with `n` bumped until unique — deterministic\n * shape, Anthropic-wire-safe charset (given a safe input id), and visibly\n * traceable back to the original.\n *\n * Alignment contract: `toolCalls` and the `tool_call` blocks inside\n * `content` are two views of the same emission and MUST be in the same\n * order (true for every provider — both are built from one source array).\n * Occurrences are matched positionally per old id so intra-turn duplicates\n * map to distinct fresh ids on both views.\n *\n * Pure: returns the input references unchanged when no id collides.\n */\nexport function remintDuplicateToolCallIds(\n toolCalls: ReadonlyArray<{ id: string, name: string, input: Record<string, unknown> }>,\n content: readonly SessionContentBlock[],\n priorIds: ReadonlySet<string>,\n): RemintResult {\n const seen = new Set(priorIds)\n const reminted: Array<{ oldId: string, newId: string }> = []\n // Final id per occurrence, queued per old id so the content walk below\n // can re-associate positionally (handles intra-turn duplicates).\n const finalIdQueues = new Map<string, string[]>()\n\n let changed = false\n const outCalls = toolCalls.map((tc) => {\n let finalId = tc.id\n if (seen.has(tc.id)) {\n let n = 1\n while (seen.has(`${tc.id}_r${n}`))\n n++\n finalId = `${tc.id}_r${n}`\n reminted.push({ oldId: tc.id, newId: finalId })\n changed = true\n }\n seen.add(finalId)\n const queue = finalIdQueues.get(tc.id)\n if (queue)\n queue.push(finalId)\n else\n finalIdQueues.set(tc.id, [finalId])\n return finalId === tc.id ? tc : { ...tc, id: finalId }\n })\n\n if (!changed) {\n return {\n toolCalls: toolCalls as RemintResult['toolCalls'],\n content: content as SessionContentBlock[],\n reminted,\n }\n }\n\n const outContent = content.map((block) => {\n if (block.type !== 'tool_call')\n return block\n const queue = finalIdQueues.get(block.id)\n const finalId = queue?.shift()\n if (finalId === undefined || finalId === block.id)\n return block\n return { ...block, id: finalId }\n })\n\n return { toolCalls: outCalls as RemintResult['toolCalls'], content: outContent, reminted }\n}\n\nfunction syntheticResultBlock(callId: string): SessionContentBlock {\n return {\n type: 'tool_result',\n callId,\n output: SYNTHETIC_TOOL_RESULT_PLACEHOLDER,\n isError: true,\n }\n}\n\nfunction collectResultIds(content: SessionContentBlock[]): Set<string> {\n const ids = new Set<string>()\n for (const block of content) {\n if (block.type === 'tool_result')\n ids.add(block.callId)\n }\n return ids\n}\n\nfunction collectIds<B extends SessionContentBlock>(\n content: SessionContentBlock[],\n type: B['type'],\n getId: (block: B) => string,\n): Set<string> {\n const ids = new Set<string>()\n for (const block of content) {\n if (block.type === type)\n ids.add(getId(block as B))\n }\n return ids\n}\n\n// ---------------------------------------------------------------------------\n// Resume-time filters (L1)\n// ---------------------------------------------------------------------------\n\n/**\n * Drop ASSISTANT turns whose every `tool_call` block is unresolved\n * (no matching `tool_result` block anywhere later in the transcript).\n * Tool_call blocks with at least one matching tool_result are kept — modes\n * 1/3 of {@link ensureToolResultPairing} handle the partial-pair case at\n * wire-send time.\n *\n * Use case: session resume. A turn that emitted three `tool_use` blocks but\n * never persisted the matching `tool_result` turn (process death, crash,\n * `kill -9`) leaves the transcript with an orphan that Anthropic rejects on\n * the FIRST API call after reload. Dropping the whole assistant turn — not\n * just the orphan blocks — preserves text-only turns that legitimately\n * carry reasoning the model wants to see again.\n *\n * Does NOT mint fresh ids: re-id'ing on every resume would cause\n * exponential transcript growth across repeated resumes of an interrupted\n * session.\n *\n * Pure: returns the input reference unchanged when no turn was dropped.\n */\nexport function filterUnresolvedToolUses<T extends { role: string, content: SessionContentBlock[] }>(\n turns: T[],\n): T[] {\n if (turns.length === 0)\n return turns\n\n // Gather every resolved tool_result id (visible anywhere later in the\n // transcript). A tool_use is \"resolved\" iff its id appears in this set.\n const resolvedIds = new Set<string>()\n for (const turn of turns) {\n for (const block of turn.content) {\n if (block.type === 'tool_result')\n resolvedIds.add(block.callId)\n }\n }\n\n let changed = false\n const out: T[] = []\n for (const turn of turns) {\n if (turn.role !== 'assistant') {\n out.push(turn)\n continue\n }\n const toolCalls = turn.content.filter(b => b.type === 'tool_call')\n if (toolCalls.length === 0) {\n out.push(turn)\n continue\n }\n const allUnresolved = toolCalls.every(b => b.type === 'tool_call' && !resolvedIds.has(b.id))\n if (allUnresolved) {\n changed = true\n continue\n }\n out.push(turn)\n }\n return changed ? out : turns\n}\n\n/**\n * Classification of a session's tail state for resume purposes.\n *\n * - `'clean'` — the trailing turn is a user message (the model would respond\n * next), or the transcript is empty. Safe to resume by appending a fresh\n * user prompt.\n * - `'interrupted'` — the trailing turn is an assistant message that has at\n * least one `tool_call` block with no matching `tool_result` anywhere\n * later. The run was almost certainly killed between persisting the\n * assistant turn and persisting its tool-results turn. Hosts that want\n * Claude-Code-style auto-resume can detect this and inject a\n * `\"Continue from where you left off.\"` user message before the next run.\n * - `'completed'` — the trailing turn is an assistant message with no\n * orphan tool_calls. The model considers itself done. Hosts that want to\n * resume must supply a new prompt; auto-continuation here would be a\n * non-sequitur.\n */\nexport type TurnInterruptionState = 'clean' | 'interrupted' | 'completed'\n\n/**\n * Inspect a session's trailing turn to classify whether the run was\n * interrupted mid-tool-call. Pure / synchronous — does not modify the input.\n *\n * Pair with {@link filterUnresolvedToolUses} on the resume path: filter\n * first so the result reflects the post-cleanup state (a turn whose orphans\n * were stripped reads as 'completed', not 'interrupted').\n */\nexport function detectTurnInterruption<T extends { role: string, content: SessionContentBlock[] }>(\n turns: T[],\n): TurnInterruptionState {\n if (turns.length === 0)\n return 'clean'\n const last = turns[turns.length - 1]\n if (last.role === 'user')\n return 'clean'\n if (last.role !== 'assistant')\n return 'clean'\n // Walk all turns to find resolved tool_result ids — a tool_use anywhere\n // earlier in the run may still be answered by a later tool_result.\n const resolvedIds = new Set<string>()\n for (const turn of turns) {\n for (const block of turn.content) {\n if (block.type === 'tool_result')\n resolvedIds.add(block.callId)\n }\n }\n for (const block of last.content) {\n if (block.type === 'tool_call' && !resolvedIds.has(block.id))\n return 'interrupted'\n }\n return 'completed'\n}\n\n/**\n * Default directive injected by {@link ensureEndsWithUserMessage} when the\n * caller doesn't pass one. Neutral enough to read sensibly in any context —\n * compaction passes its own directive, the agent loop falls back to this.\n *\n * The trailing newline is omitted on purpose; provider `userMessage()`\n * helpers wrap the content in the right block shape and any trailing\n * whitespace would land in the wire payload as-is.\n */\nexport const DEFAULT_USER_TAIL_DIRECTIVE = 'Continue.'\n\n/**\n * Guarantee a message list ends with a `role: 'user'` message, appending a\n * synthetic directive when it doesn't.\n *\n * Why: Anthropic's API (and several OpenAI-compat reasoning models — opus\n * 4.7, the o-series translation layers on Bedrock/Vertex) rejects requests\n * whose final message is `role: 'assistant'` with\n * \"This model does not support assistant message prefill. The conversation\n * must end with a user message.\"\n *\n * Triggers in zidane:\n * - Compaction over a session whose last turn is the agent's response\n * (the common case — autocompact fires right after `agent.run()`).\n * - Schema enforcement after an assistant text turn (no `tool_use`, so\n * `ensureToolResultPairing` doesn't synthesize a user tail).\n * - `context:transform` hooks that strip the trailing user turn.\n *\n * The pair-repair pass {@link ensureToolResultPairing} already covers the\n * orphan-`tool_use` case via mode 2; this helper closes the plain-text\n * assistant-tail case. They compose: run pair-repair first, then this\n * pass, so an assistant turn with orphan tool_uses gets its synthetic\n * `tool_result` user turn AND this pass is a no-op (the synthetic turn\n * is already user-role).\n *\n * Empty input passes through unchanged — the caller's \"nothing to send\"\n * guard is responsible for that case; we don't materialize a conversation\n * out of an empty list.\n *\n * Idempotent: returns the input reference unchanged when the list is\n * already user-terminated (or empty).\n *\n * @param messages Wire-ready message list.\n * @param provider Provider whose `userMessage()` shape we use so the\n * synthetic turn matches the wire format the rest of\n * the list already follows.\n * @param directive Text to put in the synthetic user message. Defaults\n * to {@link DEFAULT_USER_TAIL_DIRECTIVE} (\"Continue.\").\n * Compaction overrides this with a summary-specific cue.\n */\nexport function ensureEndsWithUserMessage(\n messages: SessionMessage[],\n provider: Provider,\n directive: string = DEFAULT_USER_TAIL_DIRECTIVE,\n): SessionMessage[] {\n if (messages.length === 0)\n return messages\n if (messages[messages.length - 1].role === 'user')\n return messages\n return [...messages, provider.userMessage(directive)]\n}\n\n// ---------------------------------------------------------------------------\n// toWireMessages — canonical \"give me provider-ready messages\" projection\n// ---------------------------------------------------------------------------\n\nexport interface ToWireMessagesOptions {\n /**\n * Pairing-repair telemetry. Fires once per repair the underlying\n * {@link ensureToolResultPairing} pass performs. Mirrors the\n * `pairing:repair` hook surface so consumers can wire structured logging\n * without an agent instance.\n */\n onRepair?: (repair: PairingRepair) => void\n /**\n * When set, also runs {@link ensureEndsWithUserMessage} so the result is\n * safe for assistant-prefill-rejecting models (opus 4.7, o-series\n * translation layers). Omit to skip — pure pairing repair only.\n */\n provider?: Provider\n /**\n * Override for the synthetic user-tail directive. Only consulted when\n * `provider` is set. Defaults to {@link DEFAULT_USER_TAIL_DIRECTIVE}.\n */\n userTailDirective?: string\n /**\n * Resolve persisted content references into provider-native payload bytes.\n * The returned data is wire-only and must not be written back to turns.\n */\n resolveContentRef?: ResolveContentRef\n}\n\nasync function resolveRefContentBlock(\n block: SessionContentBlock,\n resolveContentRef: ResolveContentRef | undefined,\n): Promise<{ block: SessionContentBlock, changed: boolean }> {\n if (isContentRefBlock(block)) {\n if (!resolveContentRef)\n throw unresolvedContentRefError(block)\n const { data } = await resolveContentRef({\n type: block.type,\n ref: block.ref,\n mediaType: block.mediaType,\n ...(block.type === 'document' ? { encoding: block.encoding } : {}),\n ...(block.name ? { name: block.name } : {}),\n })\n if (block.type === 'image') {\n return {\n block: { type: 'image', mediaType: block.mediaType, data, ...(block.name ? { name: block.name } : {}) },\n changed: true,\n }\n }\n return {\n block: { type: 'document', mediaType: block.mediaType, encoding: block.encoding, data, ...(block.name ? { name: block.name } : {}) },\n changed: true,\n }\n }\n\n if (block.type !== 'tool_result' || typeof block.output === 'string')\n return { block, changed: false }\n\n const resolved = await resolveToolResultContent(block.output, resolveContentRef)\n if (!resolved.changed)\n return { block, changed: false }\n return { block: { ...block, output: resolved.output }, changed: true }\n}\n\nasync function resolveToolResultContent(\n output: ToolResultContent[],\n resolveContentRef: ResolveContentRef | undefined,\n): Promise<{ output: ToolResultContent[], changed: boolean }> {\n let changed = false\n const next: ToolResultContent[] = []\n for (const part of output) {\n if (!isContentRefBlock(part)) {\n next.push(part)\n continue\n }\n if (!resolveContentRef)\n throw unresolvedContentRefError(part)\n const { data } = await resolveContentRef({\n type: part.type,\n ref: part.ref,\n mediaType: part.mediaType,\n ...(part.type === 'document' ? { encoding: part.encoding } : {}),\n ...(part.name ? { name: part.name } : {}),\n })\n changed = true\n if (part.type === 'image') {\n next.push({ type: 'image', mediaType: part.mediaType, data, ...(part.name ? { name: part.name } : {}) })\n }\n else {\n next.push({ type: 'document', mediaType: part.mediaType, encoding: part.encoding, data, ...(part.name ? { name: part.name } : {}) })\n }\n }\n return { output: changed ? next : output, changed }\n}\n\n/**\n * Resolve image/document ref blocks into data blocks for provider wire use.\n * This is intentionally wire-only: callers must never persist the returned\n * messages back into `session.turns`.\n *\n * Returns the input array reference unchanged when no block carried a ref —\n * the common case (most turns hold no refs, and re-running on already-resolved\n * output is a no-op), so the multi-pass wire pipeline doesn't pay per-message\n * copies for nothing.\n */\nexport async function resolveContentRefsInMessages(\n messages: readonly SessionMessage[],\n options: { resolveContentRef?: ResolveContentRef } = {},\n): Promise<SessionMessage[]> {\n let anyChanged = false\n const out: SessionMessage[] = []\n for (const msg of messages) {\n let changed = false\n const content: SessionContentBlock[] = []\n for (const block of msg.content) {\n const resolved = await resolveRefContentBlock(block, options.resolveContentRef)\n if (resolved.changed)\n changed = true\n content.push(resolved.block)\n }\n if (changed)\n anyChanged = true\n out.push(changed ? { ...msg, content } : msg)\n }\n return anyChanged ? out : (messages as SessionMessage[])\n}\n\nfunction finalizeWireMessages(messages: SessionMessage[], options: ToWireMessagesOptions): SessionMessage[] {\n const paired = ensureToolResultPairing(messages, options.onRepair ? { onRepair: options.onRepair } : {})\n if (!options.provider)\n return paired\n return ensureEndsWithUserMessage(paired, options.provider, options.userTailDirective)\n}\n\n/**\n * Build a wire-ready `SessionMessage[]` from raw persisted `SessionTurn[]`.\n *\n * **The canonical \"I want to send these to a provider\" projection.** Use\n * this — not raw `session.turns` / `store.getTurns()` — when constructing a\n * provider request outside `agent.run()`. The agent's own wire pipeline\n * (`executeTurn` in `src/loop.ts`) applies the same repair just-in-time;\n * downstream consumers that bypass the loop hit `tool_result must be\n * preceded by a tool_call` 400s precisely because they shipped raw turns.\n *\n * Pipeline:\n * 1. Filter `system`-role turns (the wire only carries user/assistant).\n * 2. {@link ensureToolResultPairing} — repairs all six orphan / dup\n * corruption modes by inserting synthetic placeholders rather than\n * dropping (preserves the model's tool_use shape).\n * 3. {@link ensureEndsWithUserMessage} when `options.provider` is set —\n * guards against assistant-tail prefill rejection (opus 4.7, o-series).\n *\n * Pure + idempotent. Does not mutate the input turns. Re-running on the\n * output is a no-op.\n *\n * **Do not feed the result back into `session.setTurns` / `appendTurns`.**\n * The repair can insert `SYNTHETIC_TOOL_RESULT_PLACEHOLDER` / `[Orphaned\n * tool result removed …]` markers; round-tripping those into persisted\n * state contaminates the session's reasoning history and defeats the\n * \"don't rewrite the past\" invariant the wire-only repair was designed\n * around.\n */\nexport async function toWireMessages(\n turns: readonly { role: 'user' | 'assistant' | 'system' | 'command', content: SessionContentBlock[] }[],\n options: ToWireMessagesOptions = {},\n): Promise<SessionMessage[]> {\n const messages: SessionMessage[] = []\n for (const t of turns) {\n // Only wire turns reach the model (drops out-of-band `system` +\n // display-only `command`). Shared, fail-safe guard — see `isWireTurn`.\n if (!isWireTurn(t))\n continue\n messages.push({ role: t.role, content: t.content })\n }\n const resolved = await resolveContentRefsInMessages(messages, { resolveContentRef: options.resolveContentRef })\n return finalizeWireMessages(resolved, options)\n}\n\nexport function autoDetectAndConvert(msg: { role: string, content: unknown }): SessionMessage {\n const c: unknown = msg.content\n\n // OpenAI-compat tagged messages\n if (c && typeof c === 'object' && !Array.isArray(c)) {\n const tag = (c as { _tag?: unknown })._tag\n if (typeof tag === 'string' && tag.startsWith('__zidane_'))\n return fromOpenAI(msg)\n }\n\n // Array: inspect block types\n if (Array.isArray(c)) {\n for (const block of c) {\n if (!block || typeof block !== 'object')\n continue\n const b = block as Record<string, unknown>\n if (b.type === 'tool_use' || (b.type === 'tool_result' && 'tool_use_id' in b)) {\n return fromAnthropic(msg)\n }\n if (b.type === 'image_url') {\n return fromOpenAI(msg)\n }\n }\n // Default array → Anthropic\n return fromAnthropic(msg)\n }\n\n // String → either format is the same\n if (typeof c === 'string') {\n return fromAnthropic(msg)\n }\n\n // Default\n return fromAnthropic(msg)\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"messages-CvHfYB7f.js","names":[],"sources":["../src/session/messages.ts"],"sourcesContent":["/**\n * Canonical SessionMessage format with converters from/to Anthropic and OpenAI-compat formats.\n */\n\nimport type { Provider } from '../providers'\nimport type { ResolveContentRef, SessionContentBlock, SessionMessage, ToolResultContent, ToolResultDocumentContent } from '../types'\nimport { fnv1aBase36 } from '../hash'\nimport { ASSISTANT_TOOL_CALLS_TAG, TOOL_RESULTS_TAG } from '../providers/openai-compat'\nimport { audioFormatFromMediaType, reconcileImageMediaType } from '../tools/media-sniff'\nimport { assertResolvedMediaBlock, documentBlockMarker, isWireTurn } from '../types'\n\ntype ToolCallBlock = Extract<SessionContentBlock, { type: 'tool_call' }>\ninterface ContentRefBlock {\n type: 'image' | 'document'\n ref: string\n mediaType: string\n encoding?: 'base64' | 'text'\n name?: string\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Decode an Anthropic `tool_result.content` wire value back to zidane's canonical\n * `string | ToolResultContent[]` shape.\n *\n * Anthropic accepts three shapes in the wire: `string`, `Array<{type:'text',text}>`,\n * or `Array<{type:'text'|'image', ...}>`. We collapse to `string` only when every\n * block is text to keep simple cases readable.\n */\nfunction decodeAnthropicToolResultContent(content: unknown): string | ToolResultContent[] {\n if (typeof content === 'string')\n return content\n\n if (!Array.isArray(content))\n return JSON.stringify(content)\n\n const blocks: ToolResultContent[] = []\n for (const raw of content) {\n if (!raw || typeof raw !== 'object')\n continue\n const b = raw as Record<string, unknown>\n if (b.type === 'text' && typeof b.text === 'string') {\n blocks.push({ type: 'text', text: b.text })\n continue\n }\n if (b.type === 'image' && b.source && typeof b.source === 'object') {\n const src = b.source as Record<string, unknown>\n if (src.type === 'base64' && typeof src.data === 'string' && typeof src.media_type === 'string') {\n blocks.push({ type: 'image', mediaType: src.media_type, data: src.data })\n continue\n }\n }\n // Unknown block → stringify into text for visibility.\n blocks.push({ type: 'text', text: JSON.stringify(raw) })\n }\n\n if (blocks.length === 0)\n return ''\n\n const hasNonText = blocks.some(b => b.type !== 'text')\n if (!hasNonText)\n return blocks.map(b => (b as { type: 'text', text: string }).text).join('\\n')\n\n return blocks\n}\n\n/**\n * Build an Anthropic `image.source` block, reconciling the declared media\n * type against the actual magic bytes.\n *\n * Anthropic's API rejects the entire request when `media_type` disagrees\n * with the payload (`messages.N.content.K.image.source.base64: The image\n * was specified using the X media type, but the image appears to be a\n * Y image`). The upstream sources of `{type:'image'}` blocks each sniff\n * before producing the block (read_file / MCP / TUI paste), but this\n * wire-boundary reconciliation is a final safety net for any future code\n * path or host-supplied prompt part that slips through without sniffing.\n *\n * The reconciler is conservative: it overrides only when the magic bytes\n * positively identify a *different* known format. Unknown / too-short\n * payloads pass `mediaType` through unchanged.\n */\nfunction anthropicImageSource(mediaType: string, data: string): { type: 'base64', media_type: string, data: string } {\n return { type: 'base64' as const, media_type: reconcileImageMediaType(mediaType, data), data }\n}\n\nfunction anthropicDocumentBlocks(doc: Pick<ToolResultDocumentContent, 'mediaType' | 'data' | 'encoding' | 'name'>): AnthropicWireBlock[] {\n if (doc.encoding === 'base64' && doc.mediaType === 'application/pdf') {\n return [{\n type: 'document',\n source: { type: 'base64', media_type: 'application/pdf', data: doc.data },\n ...(doc.name ? { title: doc.name } : {}),\n }]\n }\n\n const header = doc.name\n ? `<attachment name=\"${doc.name}\" media_type=\"${doc.mediaType}\" encoding=\"${doc.encoding}\">`\n : `<attachment media_type=\"${doc.mediaType}\" encoding=\"${doc.encoding}\">`\n return [{ type: 'text', text: `${header}\\n${doc.data}\\n</attachment>` }]\n}\n\nfunction documentMarker(doc: ToolResultDocumentContent): string {\n return documentBlockMarker(doc, 'document attached')\n}\n\nfunction isContentRefBlock(block: SessionContentBlock | ToolResultContent): block is (SessionContentBlock | ToolResultContent) & ContentRefBlock {\n return (block.type === 'image' || block.type === 'document')\n && typeof (block as { ref?: unknown }).ref === 'string'\n}\n\nfunction unresolvedContentRefError(block: ContentRefBlock): Error {\n return new Error(`Cannot project content ref \"${block.ref}\" to provider wire messages without resolveContentRef.`)\n}\n\n/**\n * Coerce a tool-call id to Anthropic's wire constraint `^[a-zA-Z0-9_-]+$`.\n *\n * Sessions started under another provider (OpenAI-compat / OpenRouter /\n * Cerebras) or carrying MCP-namespaced calls can hold ids with `.`, `:`,\n * `/` or other characters that 400 the request the moment the user switches\n * to an Anthropic model (`messages.N.content.M.tool_use.id: String should\n * match pattern '^[a-zA-Z0-9_-]+$'`).\n *\n * The mapping is deterministic — every disallowed char becomes `_` — so a\n * `tool_use` and its paired `tool_result` (sanitized identically below) keep\n * matching ids and the pairing survives. When sanitization actually changed\n * the id, a short hash of the ORIGINAL id is appended so distinct inputs\n * that would collapse to the same replacement (`call.1` vs `call_1` vs\n * `call:1`) stay distinct on the wire. Empty ids get a stable placeholder\n * so the block stays well-formed.\n */\nconst ANTHROPIC_TOOL_ID_INVALID = /[^\\w-]/g\nfunction sanitizeAnthropicToolId(id: string): string {\n if (!id)\n return 'tool_0'\n // `replace` with a global regex is idempotent for already-valid ids and\n // avoids the stateful `lastIndex` pitfall of reusing a `/g` regex `.test()`.\n const sanitized = id.replace(ANTHROPIC_TOOL_ID_INVALID, '_')\n if (sanitized === id)\n return id\n // Append a short hash of the ORIGINAL id so distinct inputs that collapse to\n // the same replacement stay distinct, deterministically, so a `tool_use` and\n // its paired `tool_result` keep matching ids.\n return `${sanitized}_${fnv1aBase36(id)}`\n}\n\n/**\n * Encode zidane's canonical `string | ToolResultContent[]` shape to Anthropic's\n * wire format for `tool_result.content`.\n */\nfunction encodeAnthropicToolResultContent(\n output: string | ToolResultContent[],\n): string | Array<{ type: 'text', text: string } | { type: 'image', source: { type: 'base64', media_type: string, data: string } }> {\n if (typeof output === 'string')\n return output\n return output.map((b) => {\n if (b.type === 'text')\n return { type: 'text' as const, text: b.text }\n if (b.type === 'document') {\n assertResolvedMediaBlock(b, 'Anthropic wire messages')\n return { type: 'text' as const, text: documentMarker(b) }\n }\n if (b.type === 'audio')\n return { type: 'text' as const, text: `[audio omitted: ${b.mediaType}]` }\n if (b.type === 'video')\n return { type: 'text' as const, text: `[video omitted: ${b.mediaType}]` }\n assertResolvedMediaBlock(b, 'Anthropic wire messages')\n return {\n type: 'image' as const,\n source: anthropicImageSource(b.mediaType, b.data),\n }\n })\n}\n\n// ---------------------------------------------------------------------------\n// fromAnthropic\n// ---------------------------------------------------------------------------\n\nexport function fromAnthropic(msg: { role: string, content: unknown }): SessionMessage {\n const role = msg.role as 'user' | 'assistant'\n const content: SessionContentBlock[] = []\n\n if (typeof msg.content === 'string') {\n content.push({ type: 'text', text: msg.content })\n return { role, content }\n }\n\n if (Array.isArray(msg.content)) {\n for (const block of msg.content) {\n if (!block || typeof block !== 'object')\n continue\n const b = block as Record<string, unknown>\n\n if (b.type === 'text') {\n content.push({ type: 'text', text: b.text as string })\n }\n else if (b.type === 'image') {\n const source = b.source as Record<string, unknown>\n if (source?.type === 'base64') {\n content.push({ type: 'image', mediaType: source.media_type as string, data: source.data as string })\n }\n }\n else if (b.type === 'document') {\n const source = b.source as Record<string, unknown>\n if (source?.type === 'base64' && typeof source.media_type === 'string' && typeof source.data === 'string') {\n content.push({\n type: 'document',\n mediaType: source.media_type,\n data: source.data,\n encoding: 'base64',\n ...(typeof b.title === 'string' ? { name: b.title } : {}),\n })\n }\n else if (source?.type === 'text' && typeof source.media_type === 'string' && typeof source.data === 'string') {\n content.push({\n type: 'document',\n mediaType: source.media_type,\n data: source.data,\n encoding: 'text',\n ...(typeof b.title === 'string' ? { name: b.title } : {}),\n })\n }\n }\n else if (b.type === 'tool_use') {\n content.push({ type: 'tool_call', id: b.id as string, name: b.name as string, input: b.input as Record<string, unknown> })\n }\n else if (b.type === 'tool_result') {\n const output = decodeAnthropicToolResultContent(b.content)\n const block: Extract<SessionContentBlock, { type: 'tool_result' }> = {\n type: 'tool_result',\n callId: b.tool_use_id as string,\n output,\n }\n if (b.is_error === true)\n block.isError = true\n content.push(block)\n }\n else if (b.type === 'thinking') {\n const block: Extract<SessionContentBlock, { type: 'thinking' }> = {\n type: 'thinking',\n text: (b.thinking as string | undefined) ?? '',\n }\n if (typeof b.signature === 'string') {\n block.signature = b.signature\n // Tag the producer so cross-provider senders can drop foreign signatures.\n block.signatureProducer = 'anthropic'\n }\n content.push(block)\n }\n else if (b.type === 'redacted_thinking') {\n // Encrypted reasoning that must be echoed back unmodified to keep\n // interleaved-thinking + tool-use chains working on Sonnet 3.7+/Opus 4.x.\n content.push({ type: 'redacted_thinking', data: (b.data as string | undefined) ?? '' })\n }\n else if (b.type === 'server_tool_use') {\n // Anthropic server-tool invocation (e.g. `web_search_20250305`). Captured\n // so multi-turn citation context survives — Anthropic's docs require\n // these blocks to be echoed back on follow-up turns.\n content.push({\n type: 'server_tool_use',\n producer: 'anthropic',\n id: (b.id as string | undefined) ?? '',\n name: (b.name as string | undefined) ?? '',\n input: (b.input as Record<string, unknown> | undefined) ?? {},\n })\n }\n else if (b.type === 'web_search_tool_result') {\n // Pairs with `server_tool_use`. `content` is opaque (search hit array\n // with `encrypted_content` / `encrypted_index`) — store unchanged.\n content.push({\n type: 'server_tool_result',\n producer: 'anthropic',\n toolUseId: (b.tool_use_id as string | undefined) ?? '',\n toolName: 'web_search',\n content: b.content,\n })\n }\n // Unknown block types are silently skipped\n }\n }\n\n return { role, content }\n}\n\n// ---------------------------------------------------------------------------\n// fromOpenAI\n// ---------------------------------------------------------------------------\n\nexport function fromOpenAI(msg: { role: string, content: unknown }): SessionMessage {\n const role = msg.role as 'user' | 'assistant'\n const content: SessionContentBlock[] = []\n const c: unknown = msg.content\n\n if (c == null) {\n return { role, content }\n }\n\n if (typeof c === 'string') {\n content.push({ type: 'text', text: c })\n return { role, content }\n }\n\n if (typeof c === 'object' && !Array.isArray(c) && (c as { _tag?: unknown })._tag === ASSISTANT_TOOL_CALLS_TAG) {\n const tagged = c as { text?: unknown, tool_calls?: unknown }\n if (typeof tagged.text === 'string' && tagged.text) {\n content.push({ type: 'text', text: tagged.text })\n }\n if (Array.isArray(tagged.tool_calls)) {\n for (const raw of tagged.tool_calls) {\n if (!raw || typeof raw !== 'object')\n continue\n const tc = raw as { id?: string, function?: { name?: string, arguments?: unknown } }\n const rawArgs = tc.function?.arguments\n // Malformed argument JSON (truncated stream, hand-edited history)\n // must not kill the whole conversion — fall back to `{}`, same\n // degradation `consumeSSE` applies to truncated tool-call args.\n let input: Record<string, unknown> = {}\n if (typeof rawArgs === 'string') {\n try {\n input = JSON.parse(rawArgs) as Record<string, unknown>\n }\n catch {\n input = {}\n }\n }\n else if (rawArgs && typeof rawArgs === 'object') {\n input = rawArgs as Record<string, unknown>\n }\n content.push({ type: 'tool_call', id: tc.id ?? '', name: tc.function?.name ?? '', input })\n }\n }\n return { role, content }\n }\n\n if (typeof c === 'object' && !Array.isArray(c) && (c as { _tag?: unknown })._tag === TOOL_RESULTS_TAG) {\n const tagged = c as { results?: unknown, text?: unknown }\n if (Array.isArray(tagged.results)) {\n for (const raw of tagged.results) {\n if (!raw || typeof raw !== 'object')\n continue\n const r = raw as { tool_call_id?: string, content?: string | ToolResultContent[] }\n content.push({ type: 'tool_result', callId: r.tool_call_id ?? '', output: r.content ?? '' })\n }\n }\n // Sibling text preserved by `toOpenAI` alongside the results.\n if (typeof tagged.text === 'string' && tagged.text)\n content.push({ type: 'text', text: tagged.text })\n return { role, content }\n }\n\n if (Array.isArray(c)) {\n for (const block of c) {\n if (!block || typeof block !== 'object')\n continue\n const b = block as Record<string, unknown>\n\n if (b.type === 'text') {\n content.push({ type: 'text', text: b.text as string })\n }\n else if (b.type === 'image_url') {\n const imageUrl = (b.image_url as Record<string, unknown>)?.url as string\n if (imageUrl?.startsWith('data:')) {\n const [meta, data] = imageUrl.slice(5).split(',', 2)\n const mediaType = meta.replace(';base64', '')\n content.push({ type: 'image', mediaType, data })\n }\n }\n }\n return { role, content }\n }\n\n return { role, content }\n}\n\n// ---------------------------------------------------------------------------\n// toAnthropic\n// ---------------------------------------------------------------------------\n\ntype AnthropicWireBlock\n = | { type: 'text', text: string }\n | { type: 'image', source: { type: 'base64', media_type: string, data: string } }\n | { type: 'document', source: { type: 'base64' | 'text', media_type: string, data: string }, title?: string }\n | { type: 'tool_use', id: string, name: string, input: Record<string, unknown> }\n | { type: 'tool_result', tool_use_id: string, content: ReturnType<typeof encodeAnthropicToolResultContent>, is_error?: true }\n | { type: 'thinking', thinking: string, signature: string }\n | { type: 'redacted_thinking', data: string }\n | { type: 'server_tool_use', id: string, name: string, input: Record<string, unknown> }\n | { type: 'web_search_tool_result', tool_use_id: string, content: unknown }\n\nexport function toAnthropic(msg: SessionMessage): { role: string, content: unknown } {\n const blocks: AnthropicWireBlock[] = msg.content\n // Drop foreign reasoning state. Three independent rejection paths,\n // each driven by an Anthropic API constraint we've hit in production:\n //\n // 1. `signatureProducer === 'openai'` — OpenAI's `encrypted_content`\n // isn't a valid Anthropic HMAC; sending it 400s with an unhelpful\n // \"invalid signature\" error.\n // 2. `type === 'thinking' && !signature` — Anthropic's\n // `ThinkingBlockParam` requires a signature field. A non-Anthropic\n // producer that doesn't sign thinking (Cerebras, Gemini, OSS via\n // OpenAI-compat, etc.) leaves a block in the session that 400s\n // here with `messages.N.content.M.thinking.signature: Field\n // required` the next time the user picks an Anthropic model. Drop\n // them — the model can re-think on its own turn, no info loss\n // beyond the prior provider's chain-of-thought, which Anthropic\n // can't consume anyway.\n // 3. `type === 'provider_reasoning'` — opaque provider-bound reasoning\n // state; not an Anthropic block type at all.\n .filter((b) => {\n if (b.type === 'provider_reasoning')\n return false\n if (b.type !== 'thinking')\n return true\n if (b.signatureProducer === 'openai')\n return false\n if (!b.signature)\n return false\n return true\n })\n .flatMap((block): AnthropicWireBlock[] => {\n switch (block.type) {\n case 'text':\n return [{ type: 'text', text: block.text }]\n case 'image':\n assertResolvedMediaBlock(block, 'Anthropic wire messages')\n return [{ type: 'image', source: anthropicImageSource(block.mediaType, block.data) }]\n case 'audio':\n // Anthropic has no audio input block. The prompt path throws before a\n // top-level audio block can reach here; this is the defensive marker\n // for any other code path (e.g. a cross-provider session replay).\n return [{ type: 'text', text: `[audio omitted: ${block.mediaType}]` }]\n case 'video':\n return [{ type: 'text', text: `[video omitted: ${block.mediaType}]` }]\n case 'document':\n assertResolvedMediaBlock(block, 'Anthropic wire messages')\n return anthropicDocumentBlocks(block)\n case 'tool_call':\n return [{ type: 'tool_use', id: sanitizeAnthropicToolId(block.id), name: block.name, input: block.input }]\n case 'tool_result': {\n const out: AnthropicWireBlock = {\n type: 'tool_result',\n tool_use_id: sanitizeAnthropicToolId(block.callId),\n content: encodeAnthropicToolResultContent(block.output),\n }\n if (block.isError)\n out.is_error = true\n const docs = typeof block.output === 'string'\n ? []\n : block.output.filter((part): part is ToolResultDocumentContent => part.type === 'document' && typeof part.data === 'string')\n return [out, ...docs.flatMap(anthropicDocumentBlocks)]\n }\n case 'thinking':\n // Filter above guarantees `signature` is present and either\n // anthropic-produced or legacy (back-compat for pre-tagging\n // Anthropic sessions where producer was never recorded).\n return [{ type: 'thinking', thinking: block.text, signature: block.signature ?? '' }]\n case 'redacted_thinking':\n return [{ type: 'redacted_thinking', data: block.data }]\n case 'server_tool_use':\n // Anthropic-produced server-tool invocation echoed back so the\n // model can resume the citation chain. Producer is always\n // `anthropic` (other providers don't emit this variant).\n return [{ type: 'server_tool_use', id: sanitizeAnthropicToolId(block.id), name: block.name, input: block.input }]\n case 'server_tool_result':\n return [{ type: 'web_search_tool_result', tool_use_id: sanitizeAnthropicToolId(block.toolUseId), content: block.content }]\n default:\n return [{ type: 'text', text: '' }]\n }\n })\n\n // Simplify to plain string if only one text block\n const first = blocks[0]\n if (blocks.length === 1 && first.type === 'text')\n return { role: msg.role, content: first.text }\n\n return { role: msg.role, content: blocks }\n}\n\n// ---------------------------------------------------------------------------\n// toOpenAI\n// ---------------------------------------------------------------------------\n\nexport function toOpenAI(msg: SessionMessage): { role: string, content: unknown } {\n const toolCalls = msg.content.filter(b => b.type === 'tool_call')\n const toolResults = msg.content.filter(b => b.type === 'tool_result')\n const textBlocks = msg.content.filter(b => b.type === 'text')\n const imageBlocks = msg.content.filter(b => b.type === 'image')\n const audioBlocks = msg.content.filter(b => b.type === 'audio')\n const videoBlocks = msg.content.filter(b => b.type === 'video')\n const documentBlocks = msg.content.filter(b => b.type === 'document')\n\n // Tool results → tagged format. Sibling text blocks (a user follow-up\n // typed while tools ran) ride along on `text` — the early return here\n // used to silently drop them, breaking round-trip fidelity vs. the wire\n // shaper (`toOAIMessages`), which preserves them.\n if (toolResults.length > 0) {\n const siblingText = textBlocks.map(b => b.text).join('\\n')\n return {\n role: msg.role,\n content: {\n _tag: TOOL_RESULTS_TAG,\n results: toolResults.map(tr => ({ tool_call_id: tr.callId, content: tr.output })),\n ...(siblingText ? { text: siblingText } : {}),\n },\n }\n }\n\n // Tool calls → tagged format\n if (toolCalls.length > 0) {\n const textContent = textBlocks.length > 0 ? textBlocks[0].text : null\n return {\n role: msg.role,\n content: {\n _tag: ASSISTANT_TOOL_CALLS_TAG,\n text: textContent,\n tool_calls: toolCalls.map(tc => ({\n id: tc.id,\n type: 'function',\n function: { name: tc.name, arguments: JSON.stringify(tc.input) },\n })),\n },\n }\n }\n\n // Images/audio/video/documents → multimodal array. Documents have no portable\n // OpenAI Chat Completions block here, so preserve them as markers.\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({ type: 'input_audio', input_audio: { data: b.data, format: audioFormatFromMediaType(b.mediaType) } })\n for (const b of videoBlocks)\n parts.push({ type: 'video_url', video_url: { url: `data:${b.mediaType};base64,${b.data}` } })\n for (const b of documentBlocks) {\n assertResolvedMediaBlock(b, 'OpenAI-compatible wire messages')\n parts.push({ type: 'text', text: documentMarker(b) })\n }\n for (const b of textBlocks)\n parts.push({ type: 'text', text: b.text })\n return { role: msg.role, content: parts }\n }\n\n // Pure text → string\n if (textBlocks.length === 1)\n return { role: msg.role, content: textBlocks[0].text }\n\n // Multiple text blocks or empty\n if (textBlocks.length > 1)\n return { role: msg.role, content: textBlocks.map(b => ({ type: 'text', text: b.text })) }\n\n return { role: msg.role, content: null }\n}\n\n// ---------------------------------------------------------------------------\n// autoDetectAndConvert\n// ---------------------------------------------------------------------------\n\n// ---------------------------------------------------------------------------\n// ensureToolResultPairing\n// ---------------------------------------------------------------------------\n\n/**\n * Placeholder content inserted into a synthetic `tool_result` when the harness\n * has to repair an orphan `tool_use`. Exported so downstream consumers\n * (training-data collectors, HFI submission) can reject any payload containing\n * it — the marker satisfies the wire-level pairing contract structurally but\n * the content itself is fake and would poison fine-tuning data.\n */\nexport const SYNTHETIC_TOOL_RESULT_PLACEHOLDER = '[Tool result missing due to internal error]'\n\n/**\n * Replacement text for an assistant message whose every content block was\n * stripped during pairing repair (e.g. the only blocks were orphan\n * `tool_call`s). Providers reject empty `content` arrays — the marker keeps\n * the turn shape valid while signaling \"this assistant turn lost its\n * outputs\" to the model so it can recover.\n */\nexport const TOOL_USE_INTERRUPTED_MARKER = '[Tool use interrupted]'\n\n/**\n * Replacement text for a user message whose every content block was a\n * `tool_result` with no matching upstream `tool_call` (e.g. the assistant\n * pair was stripped by an earlier compaction). Same role as\n * {@link TOOL_USE_INTERRUPTED_MARKER} on the user side.\n */\nexport const ORPHANED_TOOL_RESULT_MARKER = '[Orphaned tool result removed due to conversation resume]'\n\n/**\n * Classification of a single repair the pairing pass performed. Surfaced via\n * {@link EnsureToolResultPairingOptions.onRepair} so consumers can wire\n * telemetry, debug logs, or strict-mode rejection without re-implementing the\n * walk.\n *\n * Each entry corresponds to one of the corruption modes documented on\n * {@link ensureToolResultPairing}.\n */\nexport type PairingRepairMode\n = | 'orphan-tool-use-prepend' // mode 1: tool_use with no matching tool_result; next msg is user → prepend synthetic\n | 'orphan-tool-use-append' // mode 2: tool_use is followed by nothing (or a non-user msg) → append synthetic user turn\n | 'orphan-tool-result-strip' // mode 3: tool_result with no preceding tool_call → strip block\n | 'duplicate-tool-use-strip' // mode 4: duplicate tool_use id across assistant msgs → strip later instance\n | 'duplicate-tool-result-strip' // mode 5: duplicate tool_result callId in one user msg → strip later instance\n | 'empty-assistant-marker' // mode 6: assistant msg emptied by mode-4 stripping → replace with marker text\n | 'duplicate-tool-use-remint' // persist-time: fresh turn reused an existing tool_call id → re-minted before persist (loop-side, not part of ensureToolResultPairing)\n\nexport interface PairingRepair {\n mode: PairingRepairMode\n /** Tool-use / tool-result id this repair concerns. Absent for empty-marker repairs. */\n callId?: string\n /** Zero-based index into the INPUT `messages` array where the corruption was found. */\n messageIndex: number\n}\n\nexport interface EnsureToolResultPairingOptions {\n /**\n * Fired once per repair the pass performed. Synchronous so the loop can\n * stay on its hot path. Throwing from the callback aborts the pass —\n * consumers wanting fail-fast (strict mode) can re-throw with their own\n * typed error.\n */\n onRepair?: (repair: PairingRepair) => void\n}\n\n/**\n * Defensive repair pass that rewrites a message list so it satisfies the\n * wire-level `tool_use` ↔ `tool_result` adjacency contract every modern\n * provider enforces.\n *\n * Anthropic 400s on orphans with `'tool_use' ids were found without\n * 'tool_result' blocks immediately after` (and its inverse, `tool_result\n * must be preceded by a tool_call with the same toolCallId`). OpenAI's\n * Chat Completions API rejects most mismatches with a 400 too.\n *\n * Six repair modes — modeled after Anthropic's Claude Code defenses:\n *\n * | # | Corruption | Repair |\n * |---|------------|--------|\n * | 1 | assistant `tool_use` whose next user msg lacks a matching `tool_result` | **Prepend** a synthetic `tool_result` block carrying {@link SYNTHETIC_TOOL_RESULT_PLACEHOLDER} with `isError: true` |\n * | 2 | assistant `tool_use` followed by nothing (or by a non-user msg) | **Insert** a new synthetic user message with the same placeholder |\n * | 3 | user `tool_result` with no preceding assistant `tool_call` | **Strip** the orphan block; if it empties the msg, replace with {@link ORPHANED_TOOL_RESULT_MARKER} text block |\n * | 4 | duplicate `tool_call.id` across assistant messages (CC-1212) | **Strip** later instances + their matching tool_results |\n * | 5 | duplicate `tool_result.callId` within a single user message | **Dedupe** by `callId`, keep first |\n * | 6 | assistant message emptied by mode-4 stripping | **Replace** content with {@link TOOL_USE_INTERRUPTED_MARKER} text block |\n *\n * **Repair, not drop.** Earlier versions of this pass simply dropped orphan\n * blocks, which (a) silently rewrote history the model had reasoned over and\n * (b) cascaded — dropping an assistant tool_call would orphan its\n * tool_result, which would then get dropped too, removing any trace of\n * \"what the model tried to do\" from the transcript. The repair-based pass\n * preserves the model's tool_use shape and patches the dangling result with\n * an `is_error` placeholder so the model sees \"I tried X, the result was\n * lost\" and can retry intelligently.\n *\n * Adjacency contract: `tool_result` blocks must live in the user message\n * IMMEDIATELY following the assistant message that emitted the matching\n * `tool_use`. The pass enforces strict adjacency — a tool_result two\n * messages downstream of its tool_call is still an orphan.\n *\n * Idempotent: returns the input reference unchanged when no repairs were\n * necessary. Re-running on already-repaired output is a no-op.\n *\n * Pure: does not mutate input messages or their content arrays — every\n * repair allocates a fresh array / object.\n */\nexport function ensureToolResultPairing(\n messages: SessionMessage[],\n options: EnsureToolResultPairingOptions = {},\n): SessionMessage[] {\n if (messages.length === 0)\n return messages\n\n const fireRepair = options.onRepair ?? (() => {})\n\n // Pre-pass: first-occurrence index of every assistant `tool_call.id`.\n // Mode 4 strips any block whose first occurrence wasn't in the current\n // message — the first instance wins so the original semantics stand.\n const firstSeenAt = new Map<string, number>()\n for (let i = 0; i < messages.length; i++) {\n const msg = messages[i]\n if (msg.role !== 'assistant')\n continue\n for (const block of msg.content) {\n if (block.type === 'tool_call' && !firstSeenAt.has(block.id))\n firstSeenAt.set(block.id, i)\n }\n }\n\n // Scoped to this call. Keyed by INPUT index of the user message that\n // should receive prepended synthetic results from the prior assistant\n // turn's mode-1 repair. Drained as the outer loop reaches each user msg.\n const pendingOrphansByUserIndex = new Map<number, string[]>()\n\n let changed = false\n const markChanged = () => { changed = true }\n const out: SessionMessage[] = []\n\n for (let i = 0; i < messages.length; i++) {\n const msg = messages[i]\n\n if (msg.role === 'assistant') {\n processAssistantMessage({\n index: i,\n msg,\n messages,\n firstSeenAt,\n pendingOrphansByUserIndex,\n out,\n fireRepair,\n markChanged,\n })\n }\n else {\n processUserMessage({\n index: i,\n msg,\n pendingOrphansByUserIndex,\n out,\n fireRepair,\n markChanged,\n })\n }\n }\n\n return changed ? out : messages\n}\n\ninterface ProcessAssistantArgs {\n index: number\n msg: SessionMessage\n messages: SessionMessage[]\n firstSeenAt: Map<string, number>\n pendingOrphansByUserIndex: Map<number, string[]>\n out: SessionMessage[]\n fireRepair: (repair: PairingRepair) => void\n markChanged: () => void\n}\n\n/**\n * Process one assistant message: strip mode-4 duplicates, queue mode-1 / 2\n * orphan-tool-use repairs for the next user message (or insert a synthetic\n * one), and emit a mode-6 marker when dedup empties the message.\n */\nfunction processAssistantMessage(args: ProcessAssistantArgs): void {\n const { index, msg, messages, firstSeenAt, pendingOrphansByUserIndex, out, fireRepair, markChanged } = args\n\n // Mode 4: drop tool_call blocks that are duplicates either:\n // - across messages (first occurrence was in an earlier assistant msg), or\n // - within this same message (same id appearing twice in one turn —\n // pathological but observed in the wild with retry-replay providers).\n //\n // The per-message `seenHere` set guards against the intra-message case:\n // `firstSeenAt` records only the first OCCURRENCE per id, so two blocks\n // sharing an id within the same message both pass the cross-message\n // check (`firstSeenAt.get(id) === index`). The local set tracks which\n // ids we've already kept this iteration and strips later repeats.\n const dedupedContent: SessionContentBlock[] = []\n const seenHere = new Set<string>()\n let dedupedChanged = false\n for (const block of msg.content) {\n if (block.type === 'tool_call') {\n const isCrossMsgDup = firstSeenAt.get(block.id) !== index\n const isIntraMsgDup = seenHere.has(block.id)\n if (isCrossMsgDup || isIntraMsgDup) {\n dedupedChanged = true\n fireRepair({ mode: 'duplicate-tool-use-strip', callId: block.id, messageIndex: index })\n continue\n }\n seenHere.add(block.id)\n }\n dedupedContent.push(block)\n }\n\n const surviving = collectIds<ToolCallBlock>(dedupedContent, 'tool_call', b => b.id)\n const next = messages[index + 1]\n const nextIsUser = next && next.role === 'user'\n const nextResultIds = nextIsUser ? collectResultIds(next.content) : new Set<string>()\n const orphans: string[] = []\n for (const id of surviving) {\n if (!nextResultIds.has(id))\n orphans.push(id)\n }\n\n // Emit the (possibly deduped) assistant message — modes 1 / 2 patch the\n // matching user side rather than touching tool_use blocks.\n emitAssistantAfterDedup({ index, msg, dedupedContent, dedupedChanged, out, fireRepair, markChanged })\n\n if (orphans.length === 0)\n return\n\n // Mode 1: stash for the next user msg processor to prepend.\n if (nextIsUser) {\n markChanged()\n pendingOrphansByUserIndex.set(index + 1, orphans)\n for (const callId of orphans)\n fireRepair({ mode: 'orphan-tool-use-prepend', callId, messageIndex: index })\n return\n }\n\n // Mode 2: synthesize a user message with placeholders for every orphan.\n markChanged()\n out.push({\n role: 'user',\n content: orphans.map(callId => syntheticResultBlock(callId)),\n })\n for (const callId of orphans)\n fireRepair({ mode: 'orphan-tool-use-append', callId, messageIndex: index })\n}\n\ninterface EmitAssistantArgs {\n index: number\n msg: SessionMessage\n dedupedContent: SessionContentBlock[]\n dedupedChanged: boolean\n out: SessionMessage[]\n fireRepair: (repair: PairingRepair) => void\n markChanged: () => void\n}\n\nfunction emitAssistantAfterDedup(args: EmitAssistantArgs): void {\n const { index, msg, dedupedContent, dedupedChanged, out, fireRepair, markChanged } = args\n if (!dedupedChanged) {\n out.push(msg)\n return\n }\n markChanged()\n if (dedupedContent.length === 0) {\n out.push({ ...msg, content: [{ type: 'text', text: TOOL_USE_INTERRUPTED_MARKER }] })\n fireRepair({ mode: 'empty-assistant-marker', messageIndex: index })\n return\n }\n out.push({ ...msg, content: dedupedContent })\n}\n\ninterface ProcessUserArgs {\n index: number\n msg: SessionMessage\n pendingOrphansByUserIndex: Map<number, string[]>\n out: SessionMessage[]\n fireRepair: (repair: PairingRepair) => void\n markChanged: () => void\n}\n\n/**\n * Process one user message: dedup mode-5 duplicate tool_results, strip\n * mode-3 orphans whose tool_call is missing from the previously-emitted\n * assistant message, and prepend any synthetic results queued by the prior\n * assistant's mode-1 repair.\n */\nfunction processUserMessage(args: ProcessUserArgs): void {\n const { index, msg, pendingOrphansByUserIndex, out, fireRepair, markChanged } = args\n const queuedOrphans = pendingOrphansByUserIndex.get(index)\n pendingOrphansByUserIndex.delete(index)\n\n const prev = out.length > 0 ? out[out.length - 1] : null\n const validIds = prev && prev.role === 'assistant'\n ? collectIds<ToolCallBlock>(prev.content, 'tool_call', b => b.id)\n : new Set<string>()\n\n // Pass 1 — mode 5: dedupe by callId within this msg.\n const seenCallIds = new Set<string>()\n let modifiedHere = false\n const afterDedup: SessionContentBlock[] = []\n for (const block of msg.content) {\n if (block.type !== 'tool_result') {\n afterDedup.push(block)\n continue\n }\n if (seenCallIds.has(block.callId)) {\n modifiedHere = true\n fireRepair({ mode: 'duplicate-tool-result-strip', callId: block.callId, messageIndex: index })\n continue\n }\n seenCallIds.add(block.callId)\n afterDedup.push(block)\n }\n\n // Pass 2 — mode 3: strip orphan tool_results.\n const afterStrip: SessionContentBlock[] = []\n for (const block of afterDedup) {\n if (block.type === 'tool_result' && !validIds.has(block.callId)) {\n modifiedHere = true\n fireRepair({ mode: 'orphan-tool-result-strip', callId: block.callId, messageIndex: index })\n continue\n }\n afterStrip.push(block)\n }\n\n // Pass 3: prepend queued mode-1 synthetic results.\n const finalContent: SessionContentBlock[] = queuedOrphans\n ? [...queuedOrphans.map(syntheticResultBlock), ...afterStrip]\n : afterStrip\n\n if (!modifiedHere && queuedOrphans === undefined) {\n out.push(msg)\n return\n }\n\n markChanged()\n if (finalContent.length === 0) {\n out.push({ ...msg, content: [{ type: 'text', text: ORPHANED_TOOL_RESULT_MARKER }] })\n return\n }\n out.push({ ...msg, content: finalContent })\n}\n\n// ---------------------------------------------------------------------------\n// remintDuplicateToolCallIds — persist-time duplicate-id healing\n// ---------------------------------------------------------------------------\n\nexport interface RemintResult {\n /** Tool calls with duplicate ids replaced. Same reference when unchanged. */\n toolCalls: Array<{ id: string, name: string, input: Record<string, unknown> }>\n /** Assistant content with matching `tool_call` block ids rewritten. Same reference when unchanged. */\n content: SessionContentBlock[]\n /** One entry per re-minted occurrence, in emission order. */\n reminted: Array<{ oldId: string, newId: string }>\n}\n\n/**\n * Re-mint tool-call ids that collide with ids already present in the\n * transcript (or with an earlier sibling in the same turn), BEFORE the turn\n * is persisted and its tools dispatched.\n *\n * Why this exists: some providers/serving stacks emit non-globally-unique\n * tool_use ids (per-turn counters like `call_0`, retry-replay duplicates,\n * Kimi-K2.6 on Fireworks). The wire-time pairing pass\n * ({@link ensureToolResultPairing}) can only STRIP such duplicates — which\n * erases the model's tool calls and results from the wire view, stalls\n * progress, and re-accumulates repairs quadratically because the persisted\n * session keeps the corrupted ids. Re-minting at persist time keeps the\n * pairing intact instead: the call, its execution, and its tool_result all\n * carry the fresh id, so the pairing pass has nothing to strip on any later\n * turn.\n *\n * Fresh ids are `${oldId}_r{n}` with `n` bumped until unique — deterministic\n * shape, Anthropic-wire-safe charset (given a safe input id), and visibly\n * traceable back to the original.\n *\n * Alignment contract: `toolCalls` and the `tool_call` blocks inside\n * `content` are two views of the same emission and MUST be in the same\n * order (true for every provider — both are built from one source array).\n * Occurrences are matched positionally per old id so intra-turn duplicates\n * map to distinct fresh ids on both views.\n *\n * Pure: returns the input references unchanged when no id collides.\n */\nexport function remintDuplicateToolCallIds(\n toolCalls: ReadonlyArray<{ id: string, name: string, input: Record<string, unknown> }>,\n content: readonly SessionContentBlock[],\n priorIds: ReadonlySet<string>,\n): RemintResult {\n const seen = new Set(priorIds)\n const reminted: Array<{ oldId: string, newId: string }> = []\n // Final id per occurrence, queued per old id so the content walk below\n // can re-associate positionally (handles intra-turn duplicates).\n const finalIdQueues = new Map<string, string[]>()\n\n let changed = false\n const outCalls = toolCalls.map((tc) => {\n let finalId = tc.id\n if (seen.has(tc.id)) {\n let n = 1\n while (seen.has(`${tc.id}_r${n}`))\n n++\n finalId = `${tc.id}_r${n}`\n reminted.push({ oldId: tc.id, newId: finalId })\n changed = true\n }\n seen.add(finalId)\n const queue = finalIdQueues.get(tc.id)\n if (queue)\n queue.push(finalId)\n else\n finalIdQueues.set(tc.id, [finalId])\n return finalId === tc.id ? tc : { ...tc, id: finalId }\n })\n\n if (!changed) {\n return {\n toolCalls: toolCalls as RemintResult['toolCalls'],\n content: content as SessionContentBlock[],\n reminted,\n }\n }\n\n const outContent = content.map((block) => {\n if (block.type !== 'tool_call')\n return block\n const queue = finalIdQueues.get(block.id)\n const finalId = queue?.shift()\n if (finalId === undefined || finalId === block.id)\n return block\n return { ...block, id: finalId }\n })\n\n return { toolCalls: outCalls as RemintResult['toolCalls'], content: outContent, reminted }\n}\n\nfunction syntheticResultBlock(callId: string): SessionContentBlock {\n return {\n type: 'tool_result',\n callId,\n output: SYNTHETIC_TOOL_RESULT_PLACEHOLDER,\n isError: true,\n }\n}\n\nfunction collectResultIds(content: SessionContentBlock[]): Set<string> {\n const ids = new Set<string>()\n for (const block of content) {\n if (block.type === 'tool_result')\n ids.add(block.callId)\n }\n return ids\n}\n\nfunction collectIds<B extends SessionContentBlock>(\n content: SessionContentBlock[],\n type: B['type'],\n getId: (block: B) => string,\n): Set<string> {\n const ids = new Set<string>()\n for (const block of content) {\n if (block.type === type)\n ids.add(getId(block as B))\n }\n return ids\n}\n\n// ---------------------------------------------------------------------------\n// Resume-time filters (L1)\n// ---------------------------------------------------------------------------\n\n/**\n * Drop ASSISTANT turns whose every `tool_call` block is unresolved\n * (no matching `tool_result` block anywhere later in the transcript).\n * Tool_call blocks with at least one matching tool_result are kept — modes\n * 1/3 of {@link ensureToolResultPairing} handle the partial-pair case at\n * wire-send time.\n *\n * Use case: session resume. A turn that emitted three `tool_use` blocks but\n * never persisted the matching `tool_result` turn (process death, crash,\n * `kill -9`) leaves the transcript with an orphan that Anthropic rejects on\n * the FIRST API call after reload. Dropping the whole assistant turn — not\n * just the orphan blocks — preserves text-only turns that legitimately\n * carry reasoning the model wants to see again.\n *\n * Does NOT mint fresh ids: re-id'ing on every resume would cause\n * exponential transcript growth across repeated resumes of an interrupted\n * session.\n *\n * Pure: returns the input reference unchanged when no turn was dropped.\n */\nexport function filterUnresolvedToolUses<T extends { role: string, content: SessionContentBlock[] }>(\n turns: T[],\n): T[] {\n if (turns.length === 0)\n return turns\n\n // Gather every resolved tool_result id (visible anywhere later in the\n // transcript). A tool_use is \"resolved\" iff its id appears in this set.\n const resolvedIds = new Set<string>()\n for (const turn of turns) {\n for (const block of turn.content) {\n if (block.type === 'tool_result')\n resolvedIds.add(block.callId)\n }\n }\n\n let changed = false\n const out: T[] = []\n for (const turn of turns) {\n if (turn.role !== 'assistant') {\n out.push(turn)\n continue\n }\n const toolCalls = turn.content.filter(b => b.type === 'tool_call')\n if (toolCalls.length === 0) {\n out.push(turn)\n continue\n }\n const allUnresolved = toolCalls.every(b => b.type === 'tool_call' && !resolvedIds.has(b.id))\n if (allUnresolved) {\n changed = true\n continue\n }\n out.push(turn)\n }\n return changed ? out : turns\n}\n\n/**\n * Classification of a session's tail state for resume purposes.\n *\n * - `'clean'` — the trailing turn is a user message (the model would respond\n * next), or the transcript is empty. Safe to resume by appending a fresh\n * user prompt.\n * - `'interrupted'` — the trailing turn is an assistant message that has at\n * least one `tool_call` block with no matching `tool_result` anywhere\n * later. The run was almost certainly killed between persisting the\n * assistant turn and persisting its tool-results turn. Hosts that want\n * Claude-Code-style auto-resume can detect this and inject a\n * `\"Continue from where you left off.\"` user message before the next run.\n * - `'completed'` — the trailing turn is an assistant message with no\n * orphan tool_calls. The model considers itself done. Hosts that want to\n * resume must supply a new prompt; auto-continuation here would be a\n * non-sequitur.\n */\nexport type TurnInterruptionState = 'clean' | 'interrupted' | 'completed'\n\n/**\n * Inspect a session's trailing turn to classify whether the run was\n * interrupted mid-tool-call. Pure / synchronous — does not modify the input.\n *\n * Pair with {@link filterUnresolvedToolUses} on the resume path: filter\n * first so the result reflects the post-cleanup state (a turn whose orphans\n * were stripped reads as 'completed', not 'interrupted').\n */\nexport function detectTurnInterruption<T extends { role: string, content: SessionContentBlock[] }>(\n turns: T[],\n): TurnInterruptionState {\n if (turns.length === 0)\n return 'clean'\n const last = turns[turns.length - 1]\n if (last.role === 'user')\n return 'clean'\n if (last.role !== 'assistant')\n return 'clean'\n // Walk all turns to find resolved tool_result ids — a tool_use anywhere\n // earlier in the run may still be answered by a later tool_result.\n const resolvedIds = new Set<string>()\n for (const turn of turns) {\n for (const block of turn.content) {\n if (block.type === 'tool_result')\n resolvedIds.add(block.callId)\n }\n }\n for (const block of last.content) {\n if (block.type === 'tool_call' && !resolvedIds.has(block.id))\n return 'interrupted'\n }\n return 'completed'\n}\n\n/**\n * Default directive injected by {@link ensureEndsWithUserMessage} when the\n * caller doesn't pass one. Neutral enough to read sensibly in any context —\n * compaction passes its own directive, the agent loop falls back to this.\n *\n * The trailing newline is omitted on purpose; provider `userMessage()`\n * helpers wrap the content in the right block shape and any trailing\n * whitespace would land in the wire payload as-is.\n */\nexport const DEFAULT_USER_TAIL_DIRECTIVE = 'Continue.'\n\n/**\n * Guarantee a message list ends with a `role: 'user'` message, appending a\n * synthetic directive when it doesn't.\n *\n * Why: Anthropic's API (and several OpenAI-compat reasoning models — opus\n * 4.7, the o-series translation layers on Bedrock/Vertex) rejects requests\n * whose final message is `role: 'assistant'` with\n * \"This model does not support assistant message prefill. The conversation\n * must end with a user message.\"\n *\n * Triggers in zidane:\n * - Compaction over a session whose last turn is the agent's response\n * (the common case — autocompact fires right after `agent.run()`).\n * - Schema enforcement after an assistant text turn (no `tool_use`, so\n * `ensureToolResultPairing` doesn't synthesize a user tail).\n * - `context:transform` hooks that strip the trailing user turn.\n *\n * The pair-repair pass {@link ensureToolResultPairing} already covers the\n * orphan-`tool_use` case via mode 2; this helper closes the plain-text\n * assistant-tail case. They compose: run pair-repair first, then this\n * pass, so an assistant turn with orphan tool_uses gets its synthetic\n * `tool_result` user turn AND this pass is a no-op (the synthetic turn\n * is already user-role).\n *\n * Empty input passes through unchanged — the caller's \"nothing to send\"\n * guard is responsible for that case; we don't materialize a conversation\n * out of an empty list.\n *\n * Idempotent: returns the input reference unchanged when the list is\n * already user-terminated (or empty).\n *\n * @param messages Wire-ready message list.\n * @param provider Provider whose `userMessage()` shape we use so the\n * synthetic turn matches the wire format the rest of\n * the list already follows.\n * @param directive Text to put in the synthetic user message. Defaults\n * to {@link DEFAULT_USER_TAIL_DIRECTIVE} (\"Continue.\").\n * Compaction overrides this with a summary-specific cue.\n */\nexport function ensureEndsWithUserMessage(\n messages: SessionMessage[],\n provider: Provider,\n directive: string = DEFAULT_USER_TAIL_DIRECTIVE,\n): SessionMessage[] {\n if (messages.length === 0)\n return messages\n if (messages[messages.length - 1].role === 'user')\n return messages\n return [...messages, provider.userMessage(directive)]\n}\n\n// ---------------------------------------------------------------------------\n// toWireMessages — canonical \"give me provider-ready messages\" projection\n// ---------------------------------------------------------------------------\n\nexport interface ToWireMessagesOptions {\n /**\n * Pairing-repair telemetry. Fires once per repair the underlying\n * {@link ensureToolResultPairing} pass performs. Mirrors the\n * `pairing:repair` hook surface so consumers can wire structured logging\n * without an agent instance.\n */\n onRepair?: (repair: PairingRepair) => void\n /**\n * When set, also runs {@link ensureEndsWithUserMessage} so the result is\n * safe for assistant-prefill-rejecting models (opus 4.7, o-series\n * translation layers). Omit to skip — pure pairing repair only.\n */\n provider?: Provider\n /**\n * Override for the synthetic user-tail directive. Only consulted when\n * `provider` is set. Defaults to {@link DEFAULT_USER_TAIL_DIRECTIVE}.\n */\n userTailDirective?: string\n /**\n * Resolve persisted content references into provider-native payload bytes.\n * The returned data is wire-only and must not be written back to turns.\n */\n resolveContentRef?: ResolveContentRef\n}\n\nasync function resolveRefContentBlock(\n block: SessionContentBlock,\n resolveContentRef: ResolveContentRef | undefined,\n): Promise<{ block: SessionContentBlock, changed: boolean }> {\n if (isContentRefBlock(block)) {\n if (!resolveContentRef)\n throw unresolvedContentRefError(block)\n const { data } = await resolveContentRef({\n type: block.type,\n ref: block.ref,\n mediaType: block.mediaType,\n ...(block.type === 'document' ? { encoding: block.encoding } : {}),\n ...(block.name ? { name: block.name } : {}),\n })\n if (block.type === 'image') {\n return {\n block: { type: 'image', mediaType: block.mediaType, data, ...(block.name ? { name: block.name } : {}) },\n changed: true,\n }\n }\n return {\n block: { type: 'document', mediaType: block.mediaType, encoding: block.encoding, data, ...(block.name ? { name: block.name } : {}) },\n changed: true,\n }\n }\n\n if (block.type !== 'tool_result' || typeof block.output === 'string')\n return { block, changed: false }\n\n const resolved = await resolveToolResultContent(block.output, resolveContentRef)\n if (!resolved.changed)\n return { block, changed: false }\n return { block: { ...block, output: resolved.output }, changed: true }\n}\n\nasync function resolveToolResultContent(\n output: ToolResultContent[],\n resolveContentRef: ResolveContentRef | undefined,\n): Promise<{ output: ToolResultContent[], changed: boolean }> {\n let changed = false\n const next: ToolResultContent[] = []\n for (const part of output) {\n if (!isContentRefBlock(part)) {\n next.push(part)\n continue\n }\n if (!resolveContentRef)\n throw unresolvedContentRefError(part)\n const { data } = await resolveContentRef({\n type: part.type,\n ref: part.ref,\n mediaType: part.mediaType,\n ...(part.type === 'document' ? { encoding: part.encoding } : {}),\n ...(part.name ? { name: part.name } : {}),\n })\n changed = true\n if (part.type === 'image') {\n next.push({ type: 'image', mediaType: part.mediaType, data, ...(part.name ? { name: part.name } : {}) })\n }\n else {\n next.push({ type: 'document', mediaType: part.mediaType, encoding: part.encoding, data, ...(part.name ? { name: part.name } : {}) })\n }\n }\n return { output: changed ? next : output, changed }\n}\n\n/**\n * Resolve image/document ref blocks into data blocks for provider wire use.\n * This is intentionally wire-only: callers must never persist the returned\n * messages back into `session.turns`.\n *\n * Returns the input array reference unchanged when no block carried a ref —\n * the common case (most turns hold no refs, and re-running on already-resolved\n * output is a no-op), so the multi-pass wire pipeline doesn't pay per-message\n * copies for nothing.\n */\nexport async function resolveContentRefsInMessages(\n messages: readonly SessionMessage[],\n options: { resolveContentRef?: ResolveContentRef } = {},\n): Promise<SessionMessage[]> {\n let anyChanged = false\n const out: SessionMessage[] = []\n for (const msg of messages) {\n let changed = false\n const content: SessionContentBlock[] = []\n for (const block of msg.content) {\n const resolved = await resolveRefContentBlock(block, options.resolveContentRef)\n if (resolved.changed)\n changed = true\n content.push(resolved.block)\n }\n if (changed)\n anyChanged = true\n out.push(changed ? { ...msg, content } : msg)\n }\n return anyChanged ? out : (messages as SessionMessage[])\n}\n\nfunction finalizeWireMessages(messages: SessionMessage[], options: ToWireMessagesOptions): SessionMessage[] {\n const paired = ensureToolResultPairing(messages, options.onRepair ? { onRepair: options.onRepair } : {})\n if (!options.provider)\n return paired\n return ensureEndsWithUserMessage(paired, options.provider, options.userTailDirective)\n}\n\n/**\n * Build a wire-ready `SessionMessage[]` from raw persisted `SessionTurn[]`.\n *\n * **The canonical \"I want to send these to a provider\" projection.** Use\n * this — not raw `session.turns` / `store.getTurns()` — when constructing a\n * provider request outside `agent.run()`. The agent's own wire pipeline\n * (`executeTurn` in `src/loop.ts`) applies the same repair just-in-time;\n * downstream consumers that bypass the loop hit `tool_result must be\n * preceded by a tool_call` 400s precisely because they shipped raw turns.\n *\n * Pipeline:\n * 1. Filter `system`-role turns (the wire only carries user/assistant).\n * 2. {@link ensureToolResultPairing} — repairs all six orphan / dup\n * corruption modes by inserting synthetic placeholders rather than\n * dropping (preserves the model's tool_use shape).\n * 3. {@link ensureEndsWithUserMessage} when `options.provider` is set —\n * guards against assistant-tail prefill rejection (opus 4.7, o-series).\n *\n * Pure + idempotent. Does not mutate the input turns. Re-running on the\n * output is a no-op.\n *\n * **Do not feed the result back into `session.setTurns` / `appendTurns`.**\n * The repair can insert `SYNTHETIC_TOOL_RESULT_PLACEHOLDER` / `[Orphaned\n * tool result removed …]` markers; round-tripping those into persisted\n * state contaminates the session's reasoning history and defeats the\n * \"don't rewrite the past\" invariant the wire-only repair was designed\n * around.\n */\nexport async function toWireMessages(\n turns: readonly { role: 'user' | 'assistant' | 'system' | 'command', content: SessionContentBlock[] }[],\n options: ToWireMessagesOptions = {},\n): Promise<SessionMessage[]> {\n const messages: SessionMessage[] = []\n for (const t of turns) {\n // Only wire turns reach the model (drops out-of-band `system` +\n // display-only `command`). Shared, fail-safe guard — see `isWireTurn`.\n if (!isWireTurn(t))\n continue\n messages.push({ role: t.role, content: t.content })\n }\n const resolved = await resolveContentRefsInMessages(messages, { resolveContentRef: options.resolveContentRef })\n return finalizeWireMessages(resolved, options)\n}\n\nexport function autoDetectAndConvert(msg: { role: string, content: unknown }): SessionMessage {\n const c: unknown = msg.content\n\n // OpenAI-compat tagged messages\n if (c && typeof c === 'object' && !Array.isArray(c)) {\n const tag = (c as { _tag?: unknown })._tag\n if (typeof tag === 'string' && tag.startsWith('__zidane_'))\n return fromOpenAI(msg)\n }\n\n // Array: inspect block types\n if (Array.isArray(c)) {\n for (const block of c) {\n if (!block || typeof block !== 'object')\n continue\n const b = block as Record<string, unknown>\n if (b.type === 'tool_use' || (b.type === 'tool_result' && 'tool_use_id' in b)) {\n return fromAnthropic(msg)\n }\n if (b.type === 'image_url') {\n return fromOpenAI(msg)\n }\n }\n // Default array → Anthropic\n return fromAnthropic(msg)\n }\n\n // String → either format is the same\n if (typeof c === 'string') {\n return fromAnthropic(msg)\n }\n\n // Default\n return fromAnthropic(msg)\n}\n"],"mappings":"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"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { a as rewriteMessagesToWire, n as buildAliasMaps, o as toCanonicalName, r as buildSanitizedWireAliases, s as toWireName } from "./aliasing-lVFbm81n.js";
|
|
1
2
|
import { i as documentBlockMarker, n as assertResolvedMediaBlock } from "./types-BIarq1qE.js";
|
|
2
3
|
import { n as estimateTokens } from "./utils-ngQzYzZD.js";
|
|
3
4
|
import { c as classifyErrorPrelude, d as isRetryableHttpStatus, f as matchesContextExceeded, l as errorMessage, p as matchesToolPairingError } from "./errors-DJUxZg9b.js";
|
|
@@ -236,6 +237,19 @@ async function fillEstimatedCost(usage, provider, multiplier) {
|
|
|
236
237
|
};
|
|
237
238
|
}
|
|
238
239
|
//#endregion
|
|
240
|
+
//#region src/providers/openai-tool-names.ts
|
|
241
|
+
function createOpenAIToolNameBoundary(messages, currentToolNames = []) {
|
|
242
|
+
const names = new Set(currentToolNames);
|
|
243
|
+
for (const message of messages) for (const block of message.content) if (block.type === "tool_call") names.add(block.name);
|
|
244
|
+
const maps = buildAliasMaps(buildSanitizedWireAliases(names, void 0), names);
|
|
245
|
+
return {
|
|
246
|
+
messages: rewriteMessagesToWire([...messages], maps),
|
|
247
|
+
toWire: (name) => toWireName(name, maps),
|
|
248
|
+
fromWire: (name) => toCanonicalName(name, maps),
|
|
249
|
+
changed: maps.aliasByCanonical.size > 0
|
|
250
|
+
};
|
|
251
|
+
}
|
|
252
|
+
//#endregion
|
|
239
253
|
//#region src/providers/schema-sanitize.ts
|
|
240
254
|
/**
|
|
241
255
|
* Provider tool-name constraint. The charset (`[A-Za-z0-9_-]`) is universal
|
|
@@ -1516,7 +1530,9 @@ function openaiCompat(params) {
|
|
|
1516
1530
|
classifyError: classifyOpenAICompatError,
|
|
1517
1531
|
async stream(options, callbacks) {
|
|
1518
1532
|
const modelId = options.model || defaultModel;
|
|
1519
|
-
const
|
|
1533
|
+
const inputTools = options.tools ?? [];
|
|
1534
|
+
const nameBoundary = createOpenAIToolNameBoundary(options.messages, inputTools.map((tool) => tool.function.name));
|
|
1535
|
+
const messages = toOAIMessages(options.system, nameBoundary.messages, {
|
|
1520
1536
|
imageInToolResult: capabilities.imageInToolResult === true,
|
|
1521
1537
|
audio: capabilities.audio === true,
|
|
1522
1538
|
video: capabilities.video === true,
|
|
@@ -1554,10 +1570,19 @@ function openaiCompat(params) {
|
|
|
1554
1570
|
const reasoning = planOpenRouterReasoning(options.thinking, options.thinkingBudget);
|
|
1555
1571
|
if (reasoning) body.reasoning = reasoning;
|
|
1556
1572
|
}
|
|
1557
|
-
if (
|
|
1573
|
+
if (inputTools.length > 0) {
|
|
1574
|
+
const tools = inputTools.map((tool) => ({
|
|
1575
|
+
...tool,
|
|
1576
|
+
function: {
|
|
1577
|
+
...tool.function,
|
|
1578
|
+
name: nameBoundary.toWire(tool.function.name)
|
|
1579
|
+
}
|
|
1580
|
+
}));
|
|
1581
|
+
body.tools = shouldCache ? applyOAIToolCacheBreakpoint(tools) : tools;
|
|
1582
|
+
}
|
|
1558
1583
|
if (options.toolChoice) if (options.toolChoice.type === "tool" && options.toolChoice.name) body.tool_choice = {
|
|
1559
1584
|
type: "function",
|
|
1560
|
-
function: { name: options.toolChoice.name }
|
|
1585
|
+
function: { name: nameBoundary.toWire(options.toolChoice.name) }
|
|
1561
1586
|
};
|
|
1562
1587
|
else if (options.toolChoice.type === "required") body.tool_choice = "required";
|
|
1563
1588
|
else body.tool_choice = "auto";
|
|
@@ -1606,17 +1631,21 @@ function openaiCompat(params) {
|
|
|
1606
1631
|
...finishReason ? { finishReason } : {},
|
|
1607
1632
|
modelId
|
|
1608
1633
|
}, name);
|
|
1634
|
+
const toolCalls = result.toolCalls.map((call) => ({
|
|
1635
|
+
...call,
|
|
1636
|
+
name: nameBoundary.fromWire(call.name)
|
|
1637
|
+
}));
|
|
1609
1638
|
return {
|
|
1610
|
-
assistantMessage: buildAssistantContent(result.text,
|
|
1639
|
+
assistantMessage: buildAssistantContent(result.text, toolCalls, thinkingForBlock, reasoningArg),
|
|
1611
1640
|
text: result.text,
|
|
1612
|
-
toolCalls
|
|
1613
|
-
done:
|
|
1641
|
+
toolCalls,
|
|
1642
|
+
done: toolCalls.length === 0,
|
|
1614
1643
|
usage: filledUsage
|
|
1615
1644
|
};
|
|
1616
1645
|
}
|
|
1617
1646
|
};
|
|
1618
1647
|
}
|
|
1619
1648
|
//#endregion
|
|
1620
|
-
export {
|
|
1649
|
+
export { canonicalizePrompt as C, buildPromptMessage as S, unsupportedMediaError as T, userMessage as _, applyOAICacheBreakpoints as a, createOpenAIToolNameBoundary as b, buildAssistantContent as c, formatTools as d, mapOAIFinishReason as f, toolResultsMessage as g, toOAIMessages as h, TOOL_RESULTS_TAG as i, classifyOpenAICompatError as l, openaiCompat as m, OpenAICompatHttpError as n, applyOAIToolCacheBreakpoint as o, openAICompatPromptMessage as p, OpenAICompatStreamError as r, assistantMessage as s, ASSISTANT_TOOL_CALLS_TAG as t, consumeSSE as u, sanitizeToolSchema as v, defaultPromptMessage as w, fillEstimatedCost as x, sanitizeToolSpecs as y };
|
|
1621
1650
|
|
|
1622
|
-
//# sourceMappingURL=openai-compat-
|
|
1651
|
+
//# sourceMappingURL=openai-compat-pHI0kBWH.js.map
|