@copilotkit/aimock 1.34.0 → 1.35.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (151) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/CHANGELOG.md +22 -0
  4. package/README.md +2 -1
  5. package/dist/agui-types.d.cts.map +1 -1
  6. package/dist/bedrock-converse.cjs +110 -8
  7. package/dist/bedrock-converse.cjs.map +1 -1
  8. package/dist/bedrock-converse.d.cts.map +1 -1
  9. package/dist/bedrock-converse.d.ts.map +1 -1
  10. package/dist/bedrock-converse.js +111 -9
  11. package/dist/bedrock-converse.js.map +1 -1
  12. package/dist/bedrock.cjs +141 -4
  13. package/dist/bedrock.cjs.map +1 -1
  14. package/dist/bedrock.d.cts.map +1 -1
  15. package/dist/bedrock.d.ts.map +1 -1
  16. package/dist/bedrock.js +142 -5
  17. package/dist/bedrock.js.map +1 -1
  18. package/dist/cohere.cjs +89 -7
  19. package/dist/cohere.cjs.map +1 -1
  20. package/dist/cohere.d.cts.map +1 -1
  21. package/dist/cohere.d.ts.map +1 -1
  22. package/dist/cohere.js +90 -8
  23. package/dist/cohere.js.map +1 -1
  24. package/dist/config-loader.d.cts.map +1 -1
  25. package/dist/fixture-loader.cjs +121 -20
  26. package/dist/fixture-loader.cjs.map +1 -1
  27. package/dist/fixture-loader.d.cts.map +1 -1
  28. package/dist/fixture-loader.d.ts.map +1 -1
  29. package/dist/fixture-loader.js +121 -20
  30. package/dist/fixture-loader.js.map +1 -1
  31. package/dist/gemini-interactions.cjs +97 -60
  32. package/dist/gemini-interactions.cjs.map +1 -1
  33. package/dist/gemini-interactions.d.cts.map +1 -1
  34. package/dist/gemini-interactions.d.ts.map +1 -1
  35. package/dist/gemini-interactions.js +98 -61
  36. package/dist/gemini-interactions.js.map +1 -1
  37. package/dist/gemini.cjs +71 -6
  38. package/dist/gemini.cjs.map +1 -1
  39. package/dist/gemini.d.cts.map +1 -1
  40. package/dist/gemini.d.ts.map +1 -1
  41. package/dist/gemini.js +72 -7
  42. package/dist/gemini.js.map +1 -1
  43. package/dist/grok-video.cjs +834 -0
  44. package/dist/grok-video.cjs.map +1 -0
  45. package/dist/grok-video.d.cts +74 -0
  46. package/dist/grok-video.d.cts.map +1 -0
  47. package/dist/grok-video.d.ts +74 -0
  48. package/dist/grok-video.d.ts.map +1 -0
  49. package/dist/grok-video.js +829 -0
  50. package/dist/grok-video.js.map +1 -0
  51. package/dist/helpers.cjs +181 -2
  52. package/dist/helpers.cjs.map +1 -1
  53. package/dist/helpers.d.cts +33 -3
  54. package/dist/helpers.d.cts.map +1 -1
  55. package/dist/helpers.d.ts +33 -3
  56. package/dist/helpers.d.ts.map +1 -1
  57. package/dist/helpers.js +181 -3
  58. package/dist/helpers.js.map +1 -1
  59. package/dist/index.cjs +11 -0
  60. package/dist/index.d.cts +5 -3
  61. package/dist/index.d.ts +5 -3
  62. package/dist/index.js +4 -2
  63. package/dist/messages.cjs +99 -10
  64. package/dist/messages.cjs.map +1 -1
  65. package/dist/messages.d.cts.map +1 -1
  66. package/dist/messages.d.ts.map +1 -1
  67. package/dist/messages.js +100 -11
  68. package/dist/messages.js.map +1 -1
  69. package/dist/metrics.cjs +11 -1
  70. package/dist/metrics.cjs.map +1 -1
  71. package/dist/metrics.d.cts.map +1 -1
  72. package/dist/metrics.d.ts.map +1 -1
  73. package/dist/metrics.js +8 -1
  74. package/dist/metrics.js.map +1 -1
  75. package/dist/ollama.cjs +74 -4
  76. package/dist/ollama.cjs.map +1 -1
  77. package/dist/ollama.d.cts.map +1 -1
  78. package/dist/ollama.d.ts.map +1 -1
  79. package/dist/ollama.js +75 -5
  80. package/dist/ollama.js.map +1 -1
  81. package/dist/openrouter-video.cjs +19 -138
  82. package/dist/openrouter-video.cjs.map +1 -1
  83. package/dist/openrouter-video.d.cts.map +1 -1
  84. package/dist/openrouter-video.d.ts.map +1 -1
  85. package/dist/openrouter-video.js +1 -120
  86. package/dist/openrouter-video.js.map +1 -1
  87. package/dist/recorder.cjs +36 -20
  88. package/dist/recorder.cjs.map +1 -1
  89. package/dist/recorder.js +36 -20
  90. package/dist/recorder.js.map +1 -1
  91. package/dist/responses.cjs +136 -58
  92. package/dist/responses.cjs.map +1 -1
  93. package/dist/responses.d.cts +2 -2
  94. package/dist/responses.d.cts.map +1 -1
  95. package/dist/responses.d.ts +2 -2
  96. package/dist/responses.d.ts.map +1 -1
  97. package/dist/responses.js +137 -59
  98. package/dist/responses.js.map +1 -1
  99. package/dist/server.cjs +162 -20
  100. package/dist/server.cjs.map +1 -1
  101. package/dist/server.d.cts +4 -0
  102. package/dist/server.d.cts.map +1 -1
  103. package/dist/server.d.ts +4 -0
  104. package/dist/server.d.ts.map +1 -1
  105. package/dist/server.js +164 -22
  106. package/dist/server.js.map +1 -1
  107. package/dist/stream-collapse.cjs +300 -74
  108. package/dist/stream-collapse.cjs.map +1 -1
  109. package/dist/stream-collapse.d.cts +17 -1
  110. package/dist/stream-collapse.d.cts.map +1 -1
  111. package/dist/stream-collapse.d.ts +17 -1
  112. package/dist/stream-collapse.d.ts.map +1 -1
  113. package/dist/stream-collapse.js +300 -74
  114. package/dist/stream-collapse.js.map +1 -1
  115. package/dist/types.d.cts +104 -7
  116. package/dist/types.d.cts.map +1 -1
  117. package/dist/types.d.ts +104 -7
  118. package/dist/types.d.ts.map +1 -1
  119. package/dist/vector-types.d.cts.map +1 -1
  120. package/dist/vector-types.d.ts.map +1 -1
  121. package/dist/veo-video.cjs +794 -0
  122. package/dist/veo-video.cjs.map +1 -0
  123. package/dist/veo-video.d.cts +50 -0
  124. package/dist/veo-video.d.cts.map +1 -0
  125. package/dist/veo-video.d.ts +50 -0
  126. package/dist/veo-video.d.ts.map +1 -0
  127. package/dist/veo-video.js +789 -0
  128. package/dist/veo-video.js.map +1 -0
  129. package/dist/video-proxy-shared.cjs +145 -0
  130. package/dist/video-proxy-shared.cjs.map +1 -0
  131. package/dist/video-proxy-shared.js +140 -0
  132. package/dist/video-proxy-shared.js.map +1 -0
  133. package/dist/ws-gemini-live.cjs +96 -3
  134. package/dist/ws-gemini-live.cjs.map +1 -1
  135. package/dist/ws-gemini-live.d.cts.map +1 -1
  136. package/dist/ws-gemini-live.d.ts.map +1 -1
  137. package/dist/ws-gemini-live.js +97 -4
  138. package/dist/ws-gemini-live.js.map +1 -1
  139. package/dist/ws-realtime.cjs +281 -4
  140. package/dist/ws-realtime.cjs.map +1 -1
  141. package/dist/ws-realtime.d.cts.map +1 -1
  142. package/dist/ws-realtime.d.ts.map +1 -1
  143. package/dist/ws-realtime.js +282 -5
  144. package/dist/ws-realtime.js.map +1 -1
  145. package/dist/ws-responses.cjs +1 -1
  146. package/dist/ws-responses.cjs.map +1 -1
  147. package/dist/ws-responses.js +1 -1
  148. package/dist/ws-responses.js.map +1 -1
  149. package/fixtures/examples/llm/blocks-tool-first.json +13 -0
  150. package/package.json +1 -1
  151. package/skills/write-fixtures/SKILL.md +19 -1
@@ -1 +1 @@
1
- {"version":3,"file":"stream-collapse.cjs","names":["isHarmonyContent","parseHarmonyContent"],"sources":["../src/stream-collapse.ts"],"sourcesContent":["/**\n * Stream collapsing functions for record-and-replay.\n *\n * Each function takes a raw streaming response body (SSE, NDJSON, or binary\n * EventStream) and collapses it into a non-streaming fixture response\n * containing `{ content }`, `{ toolCalls }`, or both when the stream includes\n * text followed by tool calls.\n */\n\nimport { crc32 } from \"node:zlib\";\nimport type { RecordProviderKey, ToolCall } from \"./types.js\";\nimport type { Logger } from \"./logger.js\";\nimport { isHarmonyContent, parseHarmonyContent } from \"./harmony.js\";\n\n// ---------------------------------------------------------------------------\n// Result type shared by all collapse functions\n// ---------------------------------------------------------------------------\n\nexport interface CollapseResult {\n content?: string;\n reasoning?: string;\n /**\n * The real cryptographic `signature` value captured from an Anthropic\n * `signature_delta`. Carried so a recorded real-provider thinking turn can\n * replay its ACTUAL signature instead of aimock's placeholder. Absent when the\n * stream carried no signature. Single-signature assumption: a turn with\n * MULTIPLE thinking blocks collapses to one merged `reasoning` string carrying\n * only the FINAL block's signature (last-signature-wins) — per-block fidelity\n * is not preserved. The recorder persists this only alongside a non-empty\n * `reasoning` (a bare signature has nothing to attach to on replay); see\n * `TextResponse.reasoningSignature` in types.ts.\n */\n reasoningSignature?: string;\n /**\n * The opaque `data` payload(s) of any Anthropic `redacted_thinking` blocks, in\n * stream order. Captured so a recorded redacted-thinking turn round-trips its\n * encrypted reasoning faithfully. Absent when none present.\n */\n redactedThinking?: string[];\n webSearches?: string[];\n toolCalls?: ToolCall[];\n droppedChunks?: number;\n firstDroppedSample?: string;\n truncated?: boolean;\n audioB64?: string;\n audioMimeType?: string;\n /**\n * Set when harmony channel tokens were present in the accumulated content but\n * could NOT be parsed into a complete, valid harmony structure. The content\n * is preserved VERBATIM, so this is NOT transport loss — it is distinct from\n * `droppedChunks` / `truncated`, which are reserved for genuine transport loss\n * (malformed SSE/NDJSON frames, CRC mismatch). The caller surfaces this as a\n * dedicated warning rather than a dropped/truncated-chunk warning.\n */\n harmonyUnparsed?: true;\n /** Short human-readable note accompanying {@link harmonyUnparsed}. */\n harmonyNote?: string;\n}\n\n/**\n * The opaque `data` of a non-empty Anthropic `redacted_thinking` block, or\n * `undefined` if `block` is not a redacted_thinking block or carries empty/no\n * data. NON-EMPTY is required: the replay-side validator rejects a leading\n * empty-data redacted_thinking block, so recording `data: \"\"` would yield a\n * fixture that 400s under strict replay. Shared by every capture site (SSE,\n * Anthropic-native binary, non-streaming recorder) so the rule stays in one\n * place.\n */\nexport function capturedRedactedData(\n block: Record<string, unknown> | undefined,\n): string | undefined {\n if (\n block?.type === \"redacted_thinking\" &&\n typeof block.data === \"string\" &&\n block.data.length > 0\n ) {\n return block.data;\n }\n return undefined;\n}\n\n/**\n * Slice the first `max` UTF-16 code units of `s` for a diagnostic sample,\n * trimming a trailing lone high-surrogate so the resulting sample never ends on\n * a lone high surrogate (i.e. never mid-surrogate-pair).\n */\nfunction surrogateSafeSlice(s: string, max: number): string {\n let out = s.slice(0, max);\n if (out.length > 0) {\n const last = out.charCodeAt(out.length - 1);\n // A high surrogate (U+D800..U+DBFF) at the end is the lead of a split pair.\n if (last >= 0xd800 && last <= 0xdbff) {\n out = out.slice(0, -1);\n }\n }\n return out;\n}\n\n/**\n * Split a raw SSE body into per-event blocks.\n *\n * Events are delimited by a blank line. Real HTTP/SSE transports use CRLF\n * (`\\r\\n`) line endings, so the inter-event delimiter is `\\r\\n\\r\\n` (which\n * contains no `\\n\\n` substring) and each line ends with a trailing `\\r`.\n * Splitting on `/\\r?\\n\\r?\\n/` handles LF, CRLF, and mixed streams; per-line\n * `\\r` trimming happens in {@link splitSSELines}. Blank blocks are dropped.\n */\nfunction splitSSEEvents(body: string): string[] {\n return body.split(/\\r?\\n\\r?\\n/).filter((block) => block.trim().length > 0);\n}\n\n/**\n * Split a single SSE event block into its lines, trimming a trailing `\\r` so\n * CRLF streams parse identically to LF streams.\n */\nfunction splitSSELines(block: string): string[] {\n return block.split(\"\\n\").map((line) => (line.endsWith(\"\\r\") ? line.slice(0, -1) : line));\n}\n\n/**\n * Extract the SSE `data` field from a single event block's lines.\n *\n * Per the SSE spec a single event may carry MULTIPLE `data:` lines; the field\n * value is every data line's content joined with \"\\n\". Collecting only the\n * first `data:` line (e.g. via `.find`) corrupts payloads that a server split\n * across lines. Callers MUST pass lines produced by {@link splitSSELines} so\n * any trailing `\\r` is already stripped. Returns the joined payload (with the\n * leading \"data:\" prefix and one optional leading space stripped per line), or\n * `undefined` when the block contains no `data:` line.\n */\nfunction extractSSEData(lines: string[]): string | undefined {\n const dataParts: string[] = [];\n for (const line of lines) {\n if (!line.startsWith(\"data:\")) continue;\n // Strip \"data:\" then a single optional leading space, per the SSE spec.\n let part = line.slice(5);\n if (part.startsWith(\" \")) part = part.slice(1);\n dataParts.push(part);\n }\n if (dataParts.length === 0) return undefined;\n return dataParts.join(\"\\n\");\n}\n\n// ---------------------------------------------------------------------------\n// 1. OpenAI SSE\n// ---------------------------------------------------------------------------\n\n/**\n * Collapse OpenAI Chat Completions SSE stream into a single response.\n *\n * Format:\n * data: {\"id\":\"chatcmpl-123\",\"choices\":[{\"delta\":{\"content\":\"Hello\"}}]}\\n\\n\n * data: [DONE]\\n\\n\n */\nexport function collapseOpenAISSE(body: string): CollapseResult {\n const lines = splitSSEEvents(body);\n let content = \"\";\n let reasoning = \"\";\n const webSearchQueries: string[] = [];\n let droppedChunks = 0;\n let firstDroppedSample: string | undefined;\n let harmonyUnparsed = false;\n let harmonyNote: string | undefined;\n const toolCallMap = new Map<number, { id: string; name: string; arguments: string }>();\n // Fallback keying for deltas that OMIT `index`. Without this, every\n // index-less delta collapses under one `undefined`/NaN key, merging distinct\n // tool calls and corrupting arguments. Index-less fragments that share an\n // `id` correlate via `idKeyMap`; otherwise each gets a fresh synthetic key\n // assigned from a counter kept above any real index so sort order is stable.\n // The 1_000_000 sentinel assumes real provider tool-call indices stay below\n // it (they are small per-stream counters), so synthetic keys never collide.\n let nextSyntheticIndex = 1_000_000;\n const idKeyMap = new Map<string, number>();\n\n for (const line of lines) {\n const data = extractSSEData(splitSSELines(line));\n if (data === undefined) continue;\n\n const payload = data.trim();\n if (payload === \"[DONE]\") continue;\n\n let parsed: Record<string, unknown>;\n try {\n parsed = JSON.parse(payload) as Record<string, unknown>;\n } catch (err) {\n droppedChunks++;\n if (droppedChunks === 1) {\n const msg = err instanceof Error ? err.message : \"unknown\";\n firstDroppedSample = `parse failed (${msg}): ${surrogateSafeSlice(payload, 200)}`;\n }\n continue;\n }\n\n // Responses API reasoning events\n if (\n parsed.type === \"response.reasoning_summary_text.delta\" &&\n typeof parsed.delta === \"string\"\n ) {\n reasoning += parsed.delta;\n continue;\n }\n\n // Responses API web search events\n if (parsed.type === \"response.output_item.done\") {\n const item = parsed.item as Record<string, unknown> | undefined;\n if (item?.type === \"web_search_call\") {\n const action = item.action as Record<string, unknown> | undefined;\n if (action && typeof action.query === \"string\") {\n webSearchQueries.push(action.query);\n continue;\n }\n }\n }\n\n // Responses API text content events\n if (parsed.type === \"response.output_text.delta\" && typeof parsed.delta === \"string\") {\n content += parsed.delta;\n continue;\n }\n\n // Skip other Responses API structural events\n if (typeof parsed.type === \"string\" && parsed.type.startsWith(\"response.\")) {\n continue;\n }\n\n const choices = parsed.choices as Array<Record<string, unknown>> | undefined;\n if (!choices || choices.length === 0) continue;\n\n const delta = choices[0].delta as Record<string, unknown> | undefined;\n if (!delta) continue;\n\n // Reasoning content (OpenRouter / chat completions format)\n if (typeof delta.reasoning_content === \"string\") {\n reasoning += delta.reasoning_content;\n }\n\n // Text content\n if (typeof delta.content === \"string\") {\n content += delta.content;\n }\n\n // Tool calls\n const toolCalls = delta.tool_calls as Array<Record<string, unknown>> | undefined;\n if (toolCalls) {\n for (const tc of toolCalls) {\n const fn = tc.function as Record<string, unknown> | undefined;\n const rawId = typeof tc.id === \"string\" ? tc.id : undefined;\n\n // Resolve a stable map key. Prefer the streamed `index`; when it is\n // absent, correlate by `id` if present, else mint a fresh synthetic\n // key so distinct index-less calls never merge.\n let index: number;\n if (typeof tc.index === \"number\") {\n index = tc.index;\n } else if (rawId !== undefined) {\n const existing = idKeyMap.get(rawId);\n if (existing !== undefined) {\n index = existing;\n } else {\n index = nextSyntheticIndex++;\n idKeyMap.set(rawId, index);\n }\n } else {\n index = nextSyntheticIndex++;\n }\n\n if (!toolCallMap.has(index)) {\n toolCallMap.set(index, {\n id: rawId ?? \"\",\n name: (fn?.name as string) ?? \"\",\n arguments: \"\",\n });\n }\n\n const entry = toolCallMap.get(index)!;\n if (fn?.name && typeof fn.name === \"string\" && !entry.name) {\n entry.name = fn.name;\n }\n if (tc.id && typeof tc.id === \"string\" && !entry.id) {\n entry.id = tc.id;\n }\n if (fn?.arguments && typeof fn.arguments === \"string\") {\n entry.arguments += fn.arguments;\n }\n }\n }\n }\n\n // Open-weight gpt-oss models (Ollama / vLLM / OpenRouter) stream tool calls\n // as raw harmony channel tokens inside delta.content rather than structured\n // delta.tool_calls. Harmony parsing is FALLBACK-ONLY: attempt it ONLY when\n // there are NO structured delta.tool_calls. If structured tool calls exist,\n // any harmony-looking content is prose — never merged (no phantom tool call),\n // never stamped as truncated/dropped. When harmony IS the only source, a\n // successful parse routes channels (content/reasoning/toolCalls); a failure\n // preserves content VERBATIM and surfaces the distinct `harmonyUnparsed`\n // signal (NOT droppedChunks/truncated — the bytes are not lost).\n const harmonyToolCalls: ToolCall[] = [];\n if (toolCallMap.size === 0 && isHarmonyContent(content)) {\n const parsed = parseHarmonyContent(content);\n if (parsed.failed) {\n harmonyUnparsed = true;\n harmonyNote = `harmony tokens present but unparseable; content preserved verbatim: ${surrogateSafeSlice(content, 200)}`;\n } else {\n content = parsed.content;\n if (parsed.reasoning) {\n reasoning += parsed.reasoning;\n }\n harmonyToolCalls.push(...parsed.toolCalls);\n }\n }\n\n if (toolCallMap.size > 0 || harmonyToolCalls.length > 0) {\n const sorted = Array.from(toolCallMap.entries()).sort(([a], [b]) => a - b);\n return {\n ...(content ? { content } : {}),\n // Fallback-only: harmonyToolCalls are populated ONLY in the\n // no-structured-calls branch, so this is never a merge of both sources.\n toolCalls: [\n ...sorted.map(([, tc]) => ({\n name: tc.name,\n arguments: tc.arguments,\n ...(tc.id ? { id: tc.id } : {}),\n })),\n ...harmonyToolCalls,\n ],\n // Reasoning is preserved alongside tool calls for ALL structured streams\n // (DeepSeek/OpenRouter reasoning_content, harmony analysis channel), at\n // parity with every other collapser and the non-streaming path.\n ...(reasoning ? { reasoning } : {}),\n // webSearches parity with the text-only return branch.\n ...(webSearchQueries.length > 0 ? { webSearches: webSearchQueries } : {}),\n ...(droppedChunks > 0 ? { droppedChunks } : {}),\n ...(firstDroppedSample ? { firstDroppedSample } : {}),\n ...(harmonyUnparsed ? { harmonyUnparsed: true } : {}),\n ...(harmonyNote ? { harmonyNote } : {}),\n };\n }\n\n return {\n content,\n ...(reasoning ? { reasoning } : {}),\n ...(webSearchQueries.length > 0 ? { webSearches: webSearchQueries } : {}),\n ...(droppedChunks > 0 ? { droppedChunks } : {}),\n ...(firstDroppedSample ? { firstDroppedSample } : {}),\n ...(harmonyUnparsed ? { harmonyUnparsed: true } : {}),\n ...(harmonyNote ? { harmonyNote } : {}),\n };\n}\n\n// ---------------------------------------------------------------------------\n// 2. Anthropic SSE\n// ---------------------------------------------------------------------------\n\n/**\n * Collapse Anthropic Claude Messages SSE stream into a single response.\n *\n * Format:\n * event: message_start\\ndata: {...}\\n\\n\n * event: content_block_delta\\ndata: {\"delta\":{\"type\":\"text_delta\",\"text\":\"Hello\"}}\\n\\n\n */\nexport function collapseAnthropicSSE(body: string): CollapseResult {\n const blocks = splitSSEEvents(body);\n let content = \"\";\n let reasoning = \"\";\n // Real cryptographic signature captured from a `signature_delta`; stays\n // undefined when the stream carried none (e.g. aimock's own placeholder turns\n // or non-thinking turns). Carried so a recorded real-provider thinking turn\n // can replay its ACTUAL signature instead of aimock's placeholder.\n let reasoningSignature: string | undefined;\n // Opaque `data` payloads of any `redacted_thinking` content blocks, in stream\n // order. Stays empty when none present. Carried so a recorded redacted-thinking\n // turn round-trips its encrypted reasoning faithfully on replay.\n const redactedThinking: string[] = [];\n let droppedChunks = 0;\n let firstDroppedSample: string | undefined;\n const toolCallMap = new Map<number, { id: string; name: string; arguments: string }>();\n // Fallback keying for content blocks that OMIT `index` (mirrors the OpenAI /\n // Cohere / Bedrock guards). Without it, every index-less block collapses\n // under one `undefined` key, merging distinct tool_use blocks. Index-less\n // starts mint a fresh synthetic key (kept above any real index so sort order\n // is stable). Despite its name, `lastSyntheticIndex` tracks whichever\n // tool_use start most recently opened REGARDLESS of whether its index was\n // real or synthetic (it is set on every tool_use start; thinking /\n // redacted_thinking starts do not touch it), so an index-less delta\n // correlates to the most-recent tool_use start — not just to the last\n // synthetic one. The 1_000_000 sentinel assumes real provider indices stay\n // below it.\n let nextSyntheticIndex = 1_000_000;\n let lastSyntheticIndex: number | undefined;\n\n for (const block of blocks) {\n const lines = splitSSELines(block);\n const eventLine = lines.find((l) => l.startsWith(\"event:\"));\n const data = extractSSEData(lines);\n if (data === undefined) continue;\n\n const eventType = eventLine ? eventLine.slice(6).trim() : \"\";\n const payload = data.trim();\n\n let parsed: Record<string, unknown>;\n try {\n parsed = JSON.parse(payload) as Record<string, unknown>;\n } catch (err) {\n droppedChunks++;\n if (droppedChunks === 1) {\n const msg = err instanceof Error ? err.message : \"unknown\";\n firstDroppedSample = `parse failed (${msg}): ${surrogateSafeSlice(payload, 200)}`;\n }\n continue;\n }\n\n if (eventType === \"content_block_start\") {\n const contentBlock = parsed.content_block as Record<string, unknown> | undefined;\n // A `redacted_thinking` block carries its encrypted reasoning in an opaque\n // `data` string on the start event (no deltas follow). Capture it so the\n // recorded turn can replay the redacted block faithfully\n // (see capturedRedactedData for the non-empty rule).\n const redactedData = capturedRedactedData(contentBlock);\n if (redactedData !== undefined) {\n redactedThinking.push(redactedData);\n }\n if (contentBlock?.type === \"tool_use\") {\n // Prefer the streamed `index`; when absent, mint a fresh synthetic key\n // so distinct index-less tool_use blocks never merge.\n let index: number;\n if (typeof parsed.index === \"number\") {\n index = parsed.index;\n } else {\n index = nextSyntheticIndex++;\n }\n lastSyntheticIndex = index;\n toolCallMap.set(index, {\n id: (contentBlock.id as string) ?? \"\",\n name: (contentBlock.name as string) ?? \"\",\n arguments: \"\",\n });\n }\n }\n\n if (eventType === \"content_block_delta\") {\n const delta = parsed.delta as Record<string, unknown> | undefined;\n if (!delta) continue;\n\n if (delta.type === \"text_delta\" && typeof delta.text === \"string\") {\n content += delta.text;\n }\n\n if (delta.type === \"thinking_delta\" && typeof delta.thinking === \"string\") {\n reasoning += delta.thinking;\n }\n\n // The real cryptographic signature arrives via a trailing\n // `signature_delta` (the `content_block_start` carried \"\"). Capture the\n // last one seen so a recorded thinking turn replays its actual signature.\n // Last-signature-wins: a turn with MULTIPLE thinking blocks overwrites this\n // on each block, so the merged `reasoning` string ends up bound only to the\n // FINAL block's signature — per-block signatures are not preserved.\n if (delta.type === \"signature_delta\" && typeof delta.signature === \"string\") {\n reasoningSignature = delta.signature;\n }\n\n if (delta.type === \"input_json_delta\" && typeof delta.partial_json === \"string\") {\n // Use the streamed `index` when present; otherwise correlate to the\n // most recent tool_use start (mirrors the start-side fallback).\n const index = typeof parsed.index === \"number\" ? parsed.index : lastSyntheticIndex;\n // A delta that cannot correlate to any known start (no streamed index\n // AND no prior start, or a stale index with no entry) would otherwise\n // silently lose its args. Account for it as a dropped chunk instead of\n // vanishing (mirrors the Cohere uncorrelated-delta path).\n const entry = index !== undefined ? toolCallMap.get(index) : undefined;\n if (entry) {\n entry.arguments += delta.partial_json;\n } else {\n droppedChunks++;\n if (droppedChunks === 1) {\n firstDroppedSample = `input_json_delta with no correlating tool_use start: ${surrogateSafeSlice(\n payload,\n 200,\n )}`;\n }\n }\n }\n }\n }\n\n if (toolCallMap.size > 0) {\n const sorted = Array.from(toolCallMap.entries()).sort(([a], [b]) => a - b);\n return {\n ...(content ? { content } : {}),\n toolCalls: sorted.map(([, tc]) => ({\n name: tc.name,\n arguments: tc.arguments,\n ...(tc.id ? { id: tc.id } : {}),\n })),\n ...(reasoning ? { reasoning } : {}),\n ...(reasoningSignature ? { reasoningSignature } : {}),\n ...(redactedThinking.length > 0 ? { redactedThinking } : {}),\n ...(droppedChunks > 0 ? { droppedChunks } : {}),\n ...(firstDroppedSample ? { firstDroppedSample } : {}),\n };\n }\n\n return {\n content,\n ...(reasoning ? { reasoning } : {}),\n ...(reasoningSignature ? { reasoningSignature } : {}),\n ...(redactedThinking.length > 0 ? { redactedThinking } : {}),\n ...(droppedChunks > 0 ? { droppedChunks } : {}),\n ...(firstDroppedSample ? { firstDroppedSample } : {}),\n };\n}\n\n// ---------------------------------------------------------------------------\n// 3. Gemini SSE\n// ---------------------------------------------------------------------------\n\n/**\n * Collapse Gemini SSE stream into a single response.\n *\n * Format (data-only, no event prefix, no [DONE]):\n * data: {\"candidates\":[{\"content\":{\"parts\":[{\"text\":\"Hello\"}]}}]}\\n\\n\n */\nexport function collapseGeminiSSE(body: string): CollapseResult {\n const lines = splitSSEEvents(body);\n let content = \"\";\n let reasoning = \"\";\n let droppedChunks = 0;\n let firstDroppedSample: string | undefined;\n let audioB64 = \"\";\n let audioMimeType: string | undefined;\n const toolCalls: ToolCall[] = [];\n\n for (const line of lines) {\n const data = extractSSEData(splitSSELines(line));\n if (data === undefined) continue;\n\n const payload = data.trim();\n\n let parsed: Record<string, unknown>;\n try {\n parsed = JSON.parse(payload) as Record<string, unknown>;\n } catch (err) {\n droppedChunks++;\n if (droppedChunks === 1) {\n const msg = err instanceof Error ? err.message : \"unknown\";\n firstDroppedSample = `parse failed (${msg}): ${surrogateSafeSlice(payload, 200)}`;\n }\n continue;\n }\n\n const candidates = parsed.candidates as Array<Record<string, unknown>> | undefined;\n if (!candidates || candidates.length === 0) continue;\n\n const candidateContent = candidates[0].content as Record<string, unknown> | undefined;\n if (!candidateContent) continue;\n\n const parts = candidateContent.parts as Array<Record<string, unknown>> | undefined;\n if (!parts || parts.length === 0) continue;\n\n for (const part of parts) {\n if (part.functionCall) {\n const fc = part.functionCall as Record<string, unknown>;\n toolCalls.push({\n name: String(fc.name ?? \"\"),\n // Default undefined/object args to a JSON object string (matches\n // collapseGeminiInteractionsSSE / Ollama). JSON.stringify(undefined)\n // would otherwise yield the VALUE undefined, violating the\n // ToolCall.arguments:string contract.\n arguments:\n typeof fc.args === \"string\" ? (fc.args as string) : JSON.stringify(fc.args ?? {}),\n });\n } else if (\n part.inlineData &&\n typeof (part.inlineData as Record<string, unknown>).mimeType === \"string\" &&\n ((part.inlineData as Record<string, unknown>).mimeType as string).startsWith(\"audio/\")\n ) {\n const inlineData = part.inlineData as Record<string, unknown>;\n if (!audioMimeType) {\n audioMimeType = inlineData.mimeType as string;\n }\n if (typeof inlineData.data === \"string\") {\n audioB64 += inlineData.data;\n }\n } else if (typeof part.text === \"string\") {\n if (part.thought) {\n reasoning += part.text;\n } else {\n content += part.text;\n }\n }\n }\n }\n\n if (audioB64) {\n // Preserve any content / reasoning / tool calls accumulated in the same\n // stream — a Gemini turn can interleave audio with text and functionCall\n // parts, and the early return must not silently drop them.\n return {\n audioB64,\n audioMimeType,\n ...(content ? { content } : {}),\n ...(reasoning ? { reasoning } : {}),\n ...(toolCalls.length > 0 ? { toolCalls } : {}),\n ...(droppedChunks > 0 ? { droppedChunks } : {}),\n ...(firstDroppedSample ? { firstDroppedSample } : {}),\n };\n }\n\n if (toolCalls.length > 0) {\n return {\n ...(content ? { content } : {}),\n toolCalls,\n ...(reasoning ? { reasoning } : {}),\n ...(droppedChunks > 0 ? { droppedChunks } : {}),\n ...(firstDroppedSample ? { firstDroppedSample } : {}),\n };\n }\n\n return {\n content,\n ...(reasoning ? { reasoning } : {}),\n ...(droppedChunks > 0 ? { droppedChunks } : {}),\n ...(firstDroppedSample ? { firstDroppedSample } : {}),\n };\n}\n\n// ---------------------------------------------------------------------------\n// 4. Ollama NDJSON\n// ---------------------------------------------------------------------------\n\n/**\n * Collapse Ollama NDJSON stream into a single response.\n *\n * /api/chat format:\n * {\"model\":\"llama3\",\"message\":{\"role\":\"assistant\",\"content\":\"Hello\"},\"done\":false}\\n\n *\n * /api/generate format:\n * {\"model\":\"llama3\",\"response\":\"Hello\",\"done\":false}\\n\n *\n * Open-weight gpt-oss served via Ollama streams harmony channel tokens inside\n * `message.content` (just like the OpenAI SSE path), so after accumulation the\n * content is run through the same fail-safe {@link parseHarmonyContent} gate to\n * capture structured tool calls / reasoning instead of leaking raw tokens.\n */\nexport function collapseOllamaNDJSON(body: string): CollapseResult {\n const lines = body.split(\"\\n\").filter((l) => l.trim().length > 0);\n let content = \"\";\n let reasoning = \"\";\n let droppedChunks = 0;\n let firstDroppedSample: string | undefined;\n let harmonyUnparsed = false;\n let harmonyNote: string | undefined;\n const toolCalls: ToolCall[] = [];\n\n for (const line of lines) {\n let parsed: Record<string, unknown>;\n try {\n parsed = JSON.parse(line.trim()) as Record<string, unknown>;\n } catch (err) {\n droppedChunks++;\n if (droppedChunks === 1) {\n const msg = err instanceof Error ? err.message : \"unknown\";\n firstDroppedSample = `parse failed (${msg}): ${surrogateSafeSlice(line.trim(), 200)}`;\n }\n continue;\n }\n\n // /api/chat format\n const message = parsed.message as Record<string, unknown> | undefined;\n if (message) {\n if (typeof message.content === \"string\") {\n content += message.content;\n }\n\n // Tool calls\n if (Array.isArray(message.tool_calls)) {\n for (const tc of message.tool_calls as Array<Record<string, unknown>>) {\n const fn = tc.function as Record<string, unknown> | undefined;\n if (fn) {\n toolCalls.push({\n name: String(fn.name ?? \"\"),\n // Default undefined/object args to a JSON object (matching\n // collapseGeminiInteractionsSSE) — JSON.stringify(undefined)\n // would otherwise yield the literal string \"undefined\".\n arguments:\n typeof fn.arguments === \"string\"\n ? fn.arguments\n : JSON.stringify(fn.arguments ?? {}),\n });\n }\n }\n }\n }\n\n // /api/generate format\n else if (typeof parsed.response === \"string\") {\n content += parsed.response;\n }\n }\n\n // Open-weight gpt-oss served via Ollama streams harmony channel tokens inside\n // message.content (same as the OpenAI SSE path). Harmony parsing is\n // FALLBACK-ONLY: attempt it ONLY when there are NO structured message\n // tool_calls. If structured tool calls exist, harmony-looking content is\n // prose — never merged (no phantom), never stamped truncated/dropped. On a\n // harmony failure the content is preserved VERBATIM and surfaced via the\n // distinct `harmonyUnparsed` signal (NOT droppedChunks/truncated).\n if (toolCalls.length === 0 && isHarmonyContent(content)) {\n const parsedHarmony = parseHarmonyContent(content);\n if (parsedHarmony.failed) {\n harmonyUnparsed = true;\n harmonyNote = `harmony tokens present but unparseable; content preserved verbatim: ${surrogateSafeSlice(content, 200)}`;\n } else {\n content = parsedHarmony.content;\n if (parsedHarmony.reasoning) {\n reasoning += parsedHarmony.reasoning;\n }\n toolCalls.push(...parsedHarmony.toolCalls);\n }\n }\n\n if (toolCalls.length > 0) {\n return {\n ...(content ? { content } : {}),\n toolCalls,\n ...(reasoning ? { reasoning } : {}),\n ...(droppedChunks > 0 ? { droppedChunks } : {}),\n ...(firstDroppedSample ? { firstDroppedSample } : {}),\n ...(harmonyUnparsed ? { harmonyUnparsed: true } : {}),\n ...(harmonyNote ? { harmonyNote } : {}),\n };\n }\n\n return {\n content,\n ...(reasoning ? { reasoning } : {}),\n ...(droppedChunks > 0 ? { droppedChunks } : {}),\n ...(firstDroppedSample ? { firstDroppedSample } : {}),\n ...(harmonyUnparsed ? { harmonyUnparsed: true } : {}),\n ...(harmonyNote ? { harmonyNote } : {}),\n };\n}\n\n// ---------------------------------------------------------------------------\n// 5. Cohere SSE\n// ---------------------------------------------------------------------------\n\n/**\n * Collapse Cohere SSE stream into a single response.\n *\n * Format:\n * event: content-delta\\ndata: {\"type\":\"content-delta\",\"delta\":{\"message\":{\"content\":{\"text\":\"Hello\"}}}}\\n\\n\n */\nexport function collapseCohereSSE(body: string): CollapseResult {\n const blocks = splitSSEEvents(body);\n let content = \"\";\n // Reasoning text assembled from `thinking` content-delta blocks. Cohere's\n // reasoning models stream a `content.type === \"thinking\"` block carrying a\n // `thinking` string before the `text` block; capture it so a recorded\n // reasoning turn round-trips its reasoning instead of dropping it.\n let reasoning = \"\";\n let droppedChunks = 0;\n let firstDroppedSample: string | undefined;\n const toolCallMap = new Map<number, { id: string; name: string; arguments: string }>();\n // Fallback keying for tool-call events that OMIT `index` (mirrors the\n // OpenAI guard). Without it, every index-less tool-call-start collapses\n // under one `undefined`/NaN key, merging distinct calls. Index-less starts\n // mint a fresh synthetic key. `lastStartKey` tracks the most-recent\n // tool-call-start key REGARDLESS of whether it was real or synthetic, so an\n // index-less tool-call-delta correlates to whichever start most recently\n // opened — not just to the last synthetic one. The 1_000_000 sentinel\n // assumes real provider indices stay below it.\n let nextSyntheticIndex = 1_000_000;\n let lastStartKey: number | undefined;\n\n for (const block of blocks) {\n const lines = splitSSELines(block);\n const eventLine = lines.find((l) => l.startsWith(\"event:\"));\n const data = extractSSEData(lines);\n if (data === undefined) continue;\n\n const eventType = eventLine ? eventLine.slice(6).trim() : \"\";\n const payload = data.trim();\n\n let parsed: Record<string, unknown>;\n try {\n parsed = JSON.parse(payload) as Record<string, unknown>;\n } catch (err) {\n droppedChunks++;\n if (droppedChunks === 1) {\n const msg = err instanceof Error ? err.message : \"unknown\";\n firstDroppedSample = `parse failed (${msg}): ${surrogateSafeSlice(payload, 200)}`;\n }\n continue;\n }\n\n if (eventType === \"content-delta\") {\n const delta = parsed.delta as Record<string, unknown> | undefined;\n const message = delta?.message as Record<string, unknown> | undefined;\n const contentObj = message?.content as Record<string, unknown> | undefined;\n if (contentObj && contentObj.type === \"thinking\" && typeof contentObj.thinking === \"string\") {\n reasoning += contentObj.thinking;\n } else if (contentObj && typeof contentObj.text === \"string\") {\n content += contentObj.text;\n }\n }\n\n if (eventType === \"tool-call-start\") {\n let index: number;\n if (typeof parsed.index === \"number\") {\n index = parsed.index;\n } else {\n index = nextSyntheticIndex++;\n }\n // Track the most-recent start key (real OR synthetic) so a following\n // index-less delta correlates to whichever call just opened.\n lastStartKey = index;\n const delta = parsed.delta as Record<string, unknown> | undefined;\n const message = delta?.message as Record<string, unknown> | undefined;\n const toolCalls = message?.tool_calls as Record<string, unknown> | undefined;\n if (toolCalls) {\n const fn = toolCalls.function as Record<string, unknown> | undefined;\n toolCallMap.set(index, {\n id: (toolCalls.id as string) ?? \"\",\n name: (fn?.name as string) ?? \"\",\n arguments: \"\",\n });\n }\n }\n\n if (eventType === \"tool-call-delta\") {\n // Use the streamed `index` when present; otherwise correlate to the most\n // recent tool-call-start (real or synthetic key).\n const index = typeof parsed.index === \"number\" ? parsed.index : lastStartKey;\n const delta = parsed.delta as Record<string, unknown> | undefined;\n const message = delta?.message as Record<string, unknown> | undefined;\n const toolCalls = message?.tool_calls as Record<string, unknown> | undefined;\n if (toolCalls) {\n const fn = toolCalls.function as Record<string, unknown> | undefined;\n if (fn && typeof fn.arguments === \"string\") {\n // A delta that cannot correlate to any known start (no streamed\n // index AND no prior start) would otherwise silently lose its args.\n // Account for it as a dropped chunk instead of vanishing.\n const entry = index !== undefined ? toolCallMap.get(index) : undefined;\n if (entry) {\n entry.arguments += fn.arguments;\n } else {\n droppedChunks++;\n if (droppedChunks === 1) {\n firstDroppedSample = `tool-call-delta with no correlating start: ${surrogateSafeSlice(\n payload,\n 200,\n )}`;\n }\n }\n }\n }\n }\n }\n\n if (toolCallMap.size > 0) {\n const sorted = Array.from(toolCallMap.entries()).sort(([a], [b]) => a - b);\n return {\n ...(content ? { content } : {}),\n toolCalls: sorted.map(([, tc]) => ({\n name: tc.name,\n arguments: tc.arguments,\n ...(tc.id ? { id: tc.id } : {}),\n })),\n ...(reasoning ? { reasoning } : {}),\n ...(droppedChunks > 0 ? { droppedChunks } : {}),\n ...(firstDroppedSample ? { firstDroppedSample } : {}),\n };\n }\n\n return {\n content,\n ...(reasoning ? { reasoning } : {}),\n ...(droppedChunks > 0 ? { droppedChunks } : {}),\n ...(firstDroppedSample ? { firstDroppedSample } : {}),\n };\n}\n\n// ---------------------------------------------------------------------------\n// 6. Bedrock EventStream (binary)\n// ---------------------------------------------------------------------------\n\n/**\n * Decode AWS Event Stream binary frames and extract JSON payloads.\n *\n * Binary frame layout:\n * [total_length: 4B uint32-BE]\n * [headers_length: 4B uint32-BE]\n * [prelude_crc32: 4B]\n * [headers: variable]\n * [payload: variable]\n * [message_crc32: 4B]\n */\nfunction decodeEventStreamFrames(buf: Buffer): {\n frames: Array<{ headers: Record<string, string>; payload: Buffer }>;\n truncated: boolean;\n} {\n const frames: Array<{ headers: Record<string, string>; payload: Buffer }> = [];\n let offset = 0;\n\n while (offset < buf.length) {\n if (offset + 12 > buf.length) break;\n\n const totalLength = buf.readUInt32BE(offset);\n const headersLength = buf.readUInt32BE(offset + 4);\n\n // Validate bounds: ensure the full frame is within the buffer\n if (totalLength < 12 || offset + totalLength > buf.length) {\n return { frames, truncated: true };\n }\n\n // Validate prelude CRC\n const preludeCrc = buf.readUInt32BE(offset + 8);\n const computedPreludeCrc = crc32(buf.subarray(offset, offset + 8));\n if (preludeCrc >>> 0 !== computedPreludeCrc >>> 0) {\n return { frames, truncated: true }; // Prelude CRC mismatch — stop parsing\n }\n\n // Parse headers\n const headersStart = offset + 12;\n const headersEnd = headersStart + headersLength;\n const payloadEnd = offset + totalLength - 4; // minus message CRC\n\n // Validate the headers region fits inside the frame. A frame can carry a\n // valid prelude CRC yet declare a `headersLength` that overruns the payload\n // region (the prelude CRC only covers total/headers length, not the body).\n // Without this guard a per-header read walks off the buffer and throws an\n // uncaught RangeError; treat it as truncation instead.\n if (headersEnd > payloadEnd || headersEnd > buf.length) {\n return { frames, truncated: true };\n }\n\n const headers: Record<string, string> = {};\n let hOffset = headersStart;\n let headerOverrun = false;\n\n while (hOffset < headersEnd) {\n // Each read must stay within the declared headers region. Bail out\n // (truncated) on any overrun rather than reading past the boundary.\n if (hOffset + 1 > headersEnd) {\n headerOverrun = true;\n break;\n }\n const nameLen = buf.readUInt8(hOffset);\n hOffset += 1;\n if (hOffset + nameLen + 1 + 2 > headersEnd) {\n headerOverrun = true;\n break;\n }\n const name = buf.subarray(hOffset, hOffset + nameLen).toString(\"utf8\");\n hOffset += nameLen;\n // Skip header type byte (type 7 = STRING)\n hOffset += 1;\n const valueLen = buf.readUInt16BE(hOffset);\n hOffset += 2;\n if (hOffset + valueLen > headersEnd) {\n headerOverrun = true;\n break;\n }\n const value = buf.subarray(hOffset, hOffset + valueLen).toString(\"utf8\");\n hOffset += valueLen;\n headers[name] = value;\n }\n\n if (headerOverrun) {\n return { frames, truncated: true };\n }\n\n // Extract payload\n const payloadStart = headersEnd;\n const payload = buf.subarray(payloadStart, payloadEnd);\n\n // Validate message CRC (covers entire frame minus last 4 bytes)\n const messageCrc = buf.readUInt32BE(offset + totalLength - 4);\n const computedMessageCrc = crc32(buf.subarray(offset, offset + totalLength - 4));\n if (messageCrc >>> 0 !== computedMessageCrc >>> 0) {\n return { frames, truncated: true }; // Message CRC mismatch — stop parsing\n }\n\n frames.push({ headers, payload });\n offset += totalLength;\n }\n\n return { frames, truncated: false };\n}\n\n/**\n * Collapse Bedrock binary Event Stream into a single response.\n *\n * Each frame contains a JSON payload with event types like:\n * contentBlockDelta, contentBlockStart, etc.\n */\nexport function collapseBedrockEventStream(body: Buffer): CollapseResult {\n const { frames, truncated } = decodeEventStreamFrames(body);\n let content = \"\";\n // Reasoning text assembled from Converse `reasoningContent.text` deltas. The\n // Bedrock Converse stream interleaves a `delta.reasoningContent` block carrying\n // the model's reasoning; capture it so a recorded reasoning turn round-trips\n // its reasoning instead of dropping it.\n let reasoning = \"\";\n // Anthropic-native extended-thinking fields (invoke-with-response-stream).\n // The native binary branch carries the same thinking/signature/redacted\n // channel as the Anthropic SSE collapser; it mirrors that path's\n // thinking/signature/redacted capture rules so a recorded reasoning turn\n // round-trips instead of silently dropping it. Unlike the SSE path, binary\n // tool_use correlation has no index-less fallback and relies on an explicit\n // `index`.\n let reasoningSignature: string | undefined;\n const redactedThinking: string[] = [];\n let droppedChunks = 0;\n let firstDroppedSample: string | undefined;\n const toolCallMap = new Map<number, { id: string; name: string; arguments: string }>();\n\n for (const frame of frames) {\n const frameStr = frame.payload.toString(\"utf8\");\n let parsed: Record<string, unknown>;\n try {\n parsed = JSON.parse(frameStr) as Record<string, unknown>;\n } catch (err) {\n droppedChunks++;\n if (droppedChunks === 1) {\n const msg = err instanceof Error ? err.message : \"unknown\";\n firstDroppedSample = `parse failed (${msg}): ${surrogateSafeSlice(frameStr, 200)}`;\n }\n continue;\n }\n\n // Anthropic Messages format (invoke-with-response-stream): flat payload with \"type\" field\n if (parsed.type === \"content_block_delta\") {\n const delta = parsed.delta as Record<string, unknown> | undefined;\n if (delta?.type === \"text_delta\" && typeof delta.text === \"string\") {\n content += delta.text;\n }\n if (delta?.type === \"thinking_delta\" && typeof delta.thinking === \"string\") {\n reasoning += delta.thinking;\n }\n // Last-signature-wins: a turn with MULTIPLE thinking blocks overwrites\n // this on each block, so the merged `reasoning` ends up bound only to the\n // FINAL block's signature (mirrors collapseAnthropicSSE).\n if (delta?.type === \"signature_delta\" && typeof delta.signature === \"string\") {\n reasoningSignature = delta.signature;\n }\n if (delta?.type === \"input_json_delta\" && typeof delta.partial_json === \"string\") {\n const index = parsed.index as number | undefined;\n // An arg delta that cannot correlate to a known tool_use start would\n // otherwise silently lose its args. Account for it as a dropped chunk\n // instead of vanishing (mirrors the Cohere uncorrelated-delta path).\n const entry = index !== undefined ? toolCallMap.get(index) : undefined;\n if (entry) {\n entry.arguments += delta.partial_json;\n } else {\n droppedChunks++;\n if (droppedChunks === 1) {\n firstDroppedSample = `input_json_delta with no correlating tool_use start: ${surrogateSafeSlice(\n frameStr,\n 200,\n )}`;\n }\n }\n }\n continue;\n }\n if (parsed.type === \"content_block_start\") {\n const block = parsed.content_block as Record<string, unknown> | undefined;\n const index = parsed.index as number | undefined;\n // A `redacted_thinking` block carries its encrypted reasoning in an\n // opaque `data` string on the start event (no deltas follow). Capture it\n // so the recorded turn replays the redacted block faithfully\n // (mirrors collapseAnthropicSSE; see capturedRedactedData).\n const redactedData = capturedRedactedData(block);\n if (redactedData !== undefined) {\n redactedThinking.push(redactedData);\n }\n if (block?.type === \"tool_use\" && index !== undefined) {\n toolCallMap.set(index, {\n id: (block.id as string) ?? \"\",\n name: (block.name as string) ?? \"\",\n arguments: \"\",\n });\n }\n continue;\n }\n\n // Converse format (converse-stream): camelCase wrapper keys\n // contentBlockStart — may initiate a tool_use block\n if (parsed.contentBlockStart) {\n const blockStart = parsed.contentBlockStart as Record<string, unknown>;\n const index = (parsed.contentBlockIndex ?? blockStart.contentBlockIndex) as\n | number\n | undefined;\n const start = blockStart.start as Record<string, unknown> | undefined;\n if (start?.toolUse && index !== undefined) {\n const toolUse = start.toolUse as Record<string, unknown>;\n toolCallMap.set(index, {\n id: (toolUse.toolUseId as string) ?? \"\",\n name: (toolUse.name as string) ?? \"\",\n arguments: \"\",\n });\n }\n }\n\n // contentBlockDelta\n if (parsed.contentBlockDelta) {\n const blockDelta = parsed.contentBlockDelta as Record<string, unknown>;\n const index = (parsed.contentBlockIndex ?? blockDelta.contentBlockIndex) as\n | number\n | undefined;\n const delta = blockDelta.delta as Record<string, unknown> | undefined;\n if (!delta) continue;\n\n // Text delta\n if (typeof delta.text === \"string\") {\n content += delta.text;\n }\n\n // Reasoning delta — Converse carries reasoning in `reasoningContent.text`.\n // The Converse branch intentionally captures no signature/redacted channel:\n // Converse has no `signature_delta`/`redacted_thinking` wire shape, so the\n // asymmetry with the Anthropic-native branch above is by design, not a gap\n // to be \"fixed\" later.\n if (typeof delta.reasoningContent === \"object\" && delta.reasoningContent !== null) {\n const reasoningDelta = delta.reasoningContent as Record<string, unknown>;\n if (typeof reasoningDelta.text === \"string\") {\n reasoning += reasoningDelta.text;\n }\n }\n\n // Tool use input JSON delta\n if (typeof delta.toolUse === \"object\" && delta.toolUse !== null) {\n const toolUseDelta = delta.toolUse as Record<string, unknown>;\n if (typeof toolUseDelta.input === \"string\") {\n // An arg delta that cannot correlate to a known tool_use start would\n // otherwise silently lose its args. Account for it as a dropped chunk\n // instead of vanishing (mirrors the Cohere uncorrelated-delta path).\n const entry = index !== undefined ? toolCallMap.get(index) : undefined;\n if (entry) {\n entry.arguments += toolUseDelta.input;\n } else {\n droppedChunks++;\n if (droppedChunks === 1) {\n firstDroppedSample = `toolUse.input delta with no correlating tool_use start: ${surrogateSafeSlice(\n frameStr,\n 200,\n )}`;\n }\n }\n }\n }\n }\n }\n\n if (toolCallMap.size > 0) {\n const sorted = Array.from(toolCallMap.entries()).sort(([a], [b]) => a - b);\n return {\n toolCalls: sorted.map(([, tc]) => ({\n name: tc.name,\n arguments: tc.arguments,\n ...(tc.id ? { id: tc.id } : {}),\n })),\n ...(reasoning ? { reasoning } : {}),\n ...(reasoningSignature ? { reasoningSignature } : {}),\n ...(redactedThinking.length > 0 ? { redactedThinking } : {}),\n ...(droppedChunks > 0 ? { droppedChunks } : {}),\n ...(firstDroppedSample ? { firstDroppedSample } : {}),\n ...(truncated ? { truncated } : {}),\n };\n }\n\n return {\n content,\n ...(reasoning ? { reasoning } : {}),\n ...(reasoningSignature ? { reasoningSignature } : {}),\n ...(redactedThinking.length > 0 ? { redactedThinking } : {}),\n ...(droppedChunks > 0 ? { droppedChunks } : {}),\n ...(firstDroppedSample ? { firstDroppedSample } : {}),\n ...(truncated ? { truncated } : {}),\n };\n}\n\n// ---------------------------------------------------------------------------\n// 7. Gemini Interactions SSE\n// ---------------------------------------------------------------------------\n\n/**\n * Collapse Gemini Interactions SSE stream into a single response.\n *\n * Handles the SDK 2.x event protocol (the \"Interactions breaking changes,\n * May 2026\" shapes):\n * data: {\"event_type\":\"step.start\",\"index\":1,\"step\":{\"type\":\"function_call\",\"id\":\"call_1\",\"name\":\"fn\",\"arguments\":{}}}\n * data: {\"event_type\":\"step.delta\",\"index\":0,\"delta\":{\"type\":\"text\",\"text\":\"Hello\"}}\n * data: {\"event_type\":\"step.delta\",\"index\":1,\"delta\":{\"type\":\"arguments_delta\",\"arguments\":\"{\\\"x\\\":1}\"}}\n * data: {\"event_type\":\"interaction.completed\",\"interaction\":{\"id\":\"...\",\"usage\":{...}}}\n *\n * The legacy SDK 1.x shapes (`content.delta` with an inline `function_call`\n * delta) are still accepted for backward compatibility with previously\n * recorded fixtures.\n */\nexport function collapseGeminiInteractionsSSE(body: string): CollapseResult {\n const lines = splitSSEEvents(body);\n let content = \"\";\n let reasoning = \"\";\n let droppedChunks = 0;\n let firstDroppedSample: string | undefined;\n // Legacy 1.x tool calls arrive fully formed in a single content.delta.\n const toolCalls: ToolCall[] = [];\n // 2.x tool calls are assembled across step.start (identity) + arguments_delta\n // (string fragments), keyed by step index.\n const stepToolCalls = new Map<\n number,\n { id?: string; name: string; argsObj?: unknown; argsStr: string }\n >();\n // Synthetic keys for function_call step.start events that arrive without an\n // index (matches the sibling collapsers); seeded high to avoid colliding with\n // real step indices.\n let nextSyntheticStepIndex = 1_000_000;\n\n for (const line of lines) {\n const data = extractSSEData(splitSSELines(line));\n if (data === undefined) continue;\n\n const payload = data.trim();\n\n let parsed: Record<string, unknown>;\n try {\n parsed = JSON.parse(payload) as Record<string, unknown>;\n } catch (err) {\n droppedChunks++;\n if (droppedChunks === 1) {\n const msg = err instanceof Error ? err.message : \"unknown\";\n firstDroppedSample = `parse failed (${msg}): ${surrogateSafeSlice(payload, 200)}`;\n }\n continue;\n }\n\n const eventType = parsed.event_type as string | undefined;\n if (!eventType) continue;\n\n const index = typeof parsed.index === \"number\" ? parsed.index : undefined;\n\n if (eventType === \"step.start\") {\n // 2.x — tool-call identity lives on step.start, not in a delta.\n const step = parsed.step as Record<string, unknown> | undefined;\n if (step && step.type === \"function_call\") {\n // An index-less start can't correlate later arguments_delta fragments,\n // but minting a synthetic key preserves the call's identity instead of\n // dropping it silently.\n const key = index ?? nextSyntheticStepIndex++;\n stepToolCalls.set(key, {\n id: step.id ? String(step.id) : undefined,\n name: String(step.name ?? \"\"),\n // step.start may carry a fully-populated `arguments` object (non-\n // streamed calls) or an empty `{}` placeholder (streamed calls).\n argsObj: step.arguments,\n argsStr: \"\",\n });\n }\n } else if (eventType === \"step.delta\" || eventType === \"content.delta\") {\n const delta = parsed.delta as Record<string, unknown> | undefined;\n if (!delta) continue;\n\n if (delta.type === \"text\" && typeof delta.text === \"string\") {\n content += delta.text;\n } else if (delta.type === \"arguments_delta\") {\n // 2.x — argument fragment (a JSON string) keyed by step index.\n const entry = index !== undefined ? stepToolCalls.get(index) : undefined;\n if (entry) {\n if (typeof delta.arguments === \"string\") {\n entry.argsStr += delta.arguments;\n }\n } else {\n droppedChunks++;\n if (droppedChunks === 1) {\n firstDroppedSample = `arguments_delta with no correlating step.start: ${surrogateSafeSlice(\n payload,\n 200,\n )}`;\n }\n }\n } else if (delta.type === \"function_call\") {\n // Legacy 1.x — full tool call inline in a content.delta.\n toolCalls.push({\n name: String(delta.name ?? \"\"),\n arguments:\n typeof delta.arguments === \"string\"\n ? delta.arguments\n : JSON.stringify(delta.arguments ?? {}),\n ...(delta.id ? { id: String(delta.id) } : {}),\n });\n } else if (delta.type === \"thought_summary\") {\n // 2.x nests the text under `content.text`; 1.x used a flat `text`.\n const summaryContent = delta.content as Record<string, unknown> | undefined;\n if (summaryContent && typeof summaryContent.text === \"string\") {\n reasoning += summaryContent.text;\n } else if (typeof delta.text === \"string\") {\n reasoning += delta.text;\n }\n }\n }\n }\n\n // Finalize 2.x tool calls in step-index order.\n for (const [, tc] of Array.from(stepToolCalls.entries()).sort(([a], [b]) => a - b)) {\n let args: string;\n if (tc.argsStr !== \"\") {\n args = tc.argsStr;\n // The arguments_delta fragments must concatenate into valid JSON by\n // step.stop. A truncated/interrupted stream can leave them malformed;\n // surface that via droppedChunks rather than writing a corrupt fixture\n // silently (mirrors the per-chunk parse guard above).\n try {\n JSON.parse(args);\n } catch {\n droppedChunks++;\n if (droppedChunks === 1) {\n firstDroppedSample = `assembled arguments_delta not valid JSON for \"${tc.name}\": ${surrogateSafeSlice(\n args,\n 200,\n )}`;\n }\n }\n } else {\n args = typeof tc.argsObj === \"string\" ? tc.argsObj : JSON.stringify(tc.argsObj ?? {});\n }\n toolCalls.push({ name: tc.name, arguments: args, ...(tc.id ? { id: tc.id } : {}) });\n }\n\n if (toolCalls.length > 0) {\n return {\n ...(content ? { content } : {}),\n toolCalls,\n ...(reasoning ? { reasoning } : {}),\n ...(droppedChunks > 0 ? { droppedChunks } : {}),\n ...(firstDroppedSample ? { firstDroppedSample } : {}),\n };\n }\n\n return {\n content,\n ...(reasoning ? { reasoning } : {}),\n ...(droppedChunks > 0 ? { droppedChunks } : {}),\n ...(firstDroppedSample ? { firstDroppedSample } : {}),\n };\n}\n\n// ---------------------------------------------------------------------------\n// Dispatch helper — pick the right collapse function by provider\n// ---------------------------------------------------------------------------\n\n/**\n * Collapse a streaming response body into a non-streaming fixture response.\n * Returns null if the content type is not a known streaming format.\n * Falls back to OpenAI SSE parsing for unrecognized provider keys with text/event-stream.\n */\nexport function collapseStreamingResponse(\n contentType: string,\n providerKey: RecordProviderKey,\n body: string | Buffer,\n logger?: Logger,\n): CollapseResult | null {\n const ct = contentType.toLowerCase();\n\n if (ct.includes(\"application/vnd.amazon.eventstream\")) {\n const buf = typeof body === \"string\" ? Buffer.from(body, \"binary\") : body;\n return collapseBedrockEventStream(buf);\n }\n\n if (ct.includes(\"application/x-ndjson\")) {\n const str = typeof body === \"string\" ? body : body.toString(\"utf8\");\n return collapseOllamaNDJSON(str);\n }\n\n if (ct.includes(\"text/event-stream\")) {\n const str = typeof body === \"string\" ? body : body.toString(\"utf8\");\n switch (providerKey) {\n case \"openai\":\n case \"azure\":\n return collapseOpenAISSE(str);\n case \"anthropic\":\n return collapseAnthropicSSE(str);\n case \"gemini\":\n case \"vertexai\":\n return collapseGeminiSSE(str);\n case \"gemini-interactions\":\n return collapseGeminiInteractionsSSE(str);\n case \"cohere\":\n return collapseCohereSSE(str);\n case \"bedrock\":\n return collapseAnthropicSSE(str);\n default:\n logger?.warn(\n `[stream-collapse] unknown SSE provider \"${providerKey}\", falling back to OpenAI SSE format`,\n );\n return collapseOpenAISSE(str);\n }\n }\n\n return null;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"stream-collapse.cjs","names":["isHarmonyContent","parseHarmonyContent"],"sources":["../src/stream-collapse.ts"],"sourcesContent":["/**\n * Stream collapsing functions for record-and-replay.\n *\n * Each function takes a raw streaming response body (SSE, NDJSON, or binary\n * EventStream) and collapses it into a non-streaming fixture response\n * containing `{ content }`, `{ toolCalls }`, or both when the stream includes\n * text followed by tool calls.\n */\n\nimport { crc32 } from \"node:zlib\";\nimport type { FixtureBlock, RecordProviderKey, ToolCall } from \"./types.js\";\nimport type { Logger } from \"./logger.js\";\nimport { isHarmonyContent, parseHarmonyContent } from \"./harmony.js\";\n\n// ---------------------------------------------------------------------------\n// Result type shared by all collapse functions\n// ---------------------------------------------------------------------------\n\nexport interface CollapseResult {\n content?: string;\n reasoning?: string;\n /**\n * The real cryptographic `signature` value captured from an Anthropic\n * `signature_delta`. Carried so a recorded real-provider thinking turn can\n * replay its ACTUAL signature instead of aimock's placeholder. Absent when the\n * stream carried no signature. Single-signature assumption: a turn with\n * MULTIPLE thinking blocks collapses to one merged `reasoning` string carrying\n * only the FINAL block's signature (last-signature-wins) — per-block fidelity\n * is not preserved. The recorder persists this only alongside a non-empty\n * `reasoning` (a bare signature has nothing to attach to on replay); see\n * `TextResponse.reasoningSignature` in types.ts.\n */\n reasoningSignature?: string;\n /**\n * The opaque `data` payload(s) of any Anthropic `redacted_thinking` blocks, in\n * stream order. Captured so a recorded redacted-thinking turn round-trips its\n * encrypted reasoning faithfully. Absent when none present.\n */\n redactedThinking?: string[];\n webSearches?: string[];\n toolCalls?: ToolCall[];\n droppedChunks?: number;\n firstDroppedSample?: string;\n truncated?: boolean;\n audioB64?: string;\n audioMimeType?: string;\n /**\n * Set when harmony channel tokens were present in the accumulated content but\n * could NOT be parsed into a complete, valid harmony structure. The content\n * is preserved VERBATIM, so this is NOT transport loss — it is distinct from\n * `droppedChunks` / `truncated`, which are reserved for genuine transport loss\n * (malformed SSE/NDJSON frames, CRC mismatch). The caller surfaces this as a\n * dedicated warning rather than a dropped/truncated-chunk warning.\n */\n harmonyUnparsed?: true;\n /** Short human-readable note accompanying {@link harmonyUnparsed}. */\n harmonyNote?: string;\n /**\n * Ordered cross-channel block list, in STREAM order, populated ONLY when the\n * stream is \"interleaved\" — i.e. a tool-call delta appeared STRICTLY BEFORE\n * the first content delta, OR a content delta appeared AFTER any tool-call\n * delta. The flat `content` / `toolCalls` fields stay populated UNCHANGED for\n * replay back-compat and non-block consumers; `blocks` is purely additive\n * positional instrumentation the recorder consults to decide whether to\n * persist the ordered shape. Absent (undefined) for text-first, text-only,\n * and tool-only streams — i.e. anything NOT interleaved — so the recorder\n * keeps the legacy `{ content, toolCalls }` shape byte-identical.\n *\n * Each text block coalesces all contiguous content deltas between tool\n * atoms; each toolCall block carries the fully-assembled name/arguments/id\n * for one tool call in the position its FIRST delta arrived.\n */\n blocks?: FixtureBlock[];\n}\n\n// ---------------------------------------------------------------------------\n// Cross-channel block-order instrumentation (#274)\n// ---------------------------------------------------------------------------\n\n/**\n * Atom recorded during a collapse pass, in stream arrival order. A `text` atom\n * carries one content delta's text (contiguous text atoms are coalesced when\n * building blocks); a `toolCall` atom is a stable reference to a tool-call\n * accumulator whose name/arguments/id are filled in across later deltas. The\n * `ref` is the SAME object stored in the collapser's `toolCallMap` (or pushed\n * to a flat `toolCalls` array), so block identity is reconciled with the flat\n * representation at finalize time — see {@link buildOrderedBlocks}.\n */\ntype OrderAtom =\n | { kind: \"text\"; text: string }\n | { kind: \"toolCall\"; ref: { name: string; arguments: string; id?: string } };\n\n/**\n * Normalize a tool call's accumulated `arguments` into valid JSON exactly like\n * the flat-`toolCalls` recorder path: an empty / whitespace-only / missing\n * value becomes `\"{}\"`, never `\"\"`. Mirrors `recorder.ts` `toToolCallArguments`\n * so a `blocks[].arguments` value is always parseable JSON and never disagrees\n * with the flat `toolCalls` entry for the same call.\n */\nfunction normalizeToolArguments(args: string | undefined): string {\n if (args === undefined || args.trim() === \"\") return \"{}\";\n return args;\n}\n\n/**\n * Build a finalized {@link FixtureBlock.toolCall} from a tool-call accumulator,\n * normalizing `arguments` so the block agrees byte-for-byte with the flat\n * `toolCalls` entry built from the SAME accumulator object.\n */\nfunction toToolCallBlock(ref: { name: string; arguments: string; id?: string }): FixtureBlock {\n return {\n type: \"toolCall\",\n name: ref.name,\n arguments: normalizeToolArguments(ref.arguments),\n ...(ref.id ? { id: ref.id } : {}),\n };\n}\n\n/**\n * Decide whether a recorded atom sequence is \"interleaved\" and, if so, build\n * the ordered {@link FixtureBlock} list. Returns `undefined` when NOT\n * interleaved (text-first, text-only, or tool-only) so callers leave\n * `CollapseResult.blocks` unset and the recorder keeps the legacy shape.\n *\n * Interleaved ⇔ (a tool atom appears strictly before the first text atom) OR\n * (a text atom appears after any tool atom). A stream with no tool atoms, or\n * with no text atoms, is never interleaved. Text-first-then-tools is the common\n * legacy case and is explicitly NOT interleaved.\n *\n * CONSISTENCY (#274): each toolCall block is derived from the SAME accumulator\n * object referenced by its atom and normalized identically to the flat\n * `toolCalls` path ({@link toToolCallBlock} / {@link normalizeToolArguments}).\n * Because the atom `ref` is the very object the flat list is built from, the\n * block and its flat counterpart describe the same call by identity — even when\n * upstream tool-call indices do not match stream-arrival order. Empty/missing\n * arguments normalize to `\"{}\"` in BOTH representations, never `\"\"`.\n */\nfunction buildOrderedBlocks(atoms: OrderAtom[]): FixtureBlock[] | undefined {\n let firstTextIndex = -1;\n let firstToolIndex = -1;\n let textAfterTool = false;\n let sawTool = false;\n let sawText = false;\n for (let i = 0; i < atoms.length; i++) {\n const a = atoms[i];\n if (a.kind === \"text\") {\n sawText = true;\n if (firstTextIndex === -1) firstTextIndex = i;\n if (sawTool) textAfterTool = true;\n } else {\n sawTool = true;\n if (firstToolIndex === -1) firstToolIndex = i;\n }\n }\n // No cross-channel ordering to express unless BOTH channels appear.\n if (!sawTool || !sawText) return undefined;\n const toolBeforeText = firstToolIndex < firstTextIndex;\n if (!toolBeforeText && !textAfterTool) return undefined;\n\n // Coalesce contiguous text atoms into one text block; emit each tool atom as\n // a toolCall block reflecting its fully-assembled, normalized accumulator.\n const blocks: FixtureBlock[] = [];\n let pendingText = \"\";\n let hasPendingText = false;\n const flushText = () => {\n if (hasPendingText) {\n blocks.push({ type: \"text\", text: pendingText });\n pendingText = \"\";\n hasPendingText = false;\n }\n };\n for (const a of atoms) {\n if (a.kind === \"text\") {\n pendingText += a.text;\n hasPendingText = true;\n } else {\n flushText();\n blocks.push(toToolCallBlock(a.ref));\n }\n }\n flushText();\n return blocks;\n}\n\n/**\n * The opaque `data` of a non-empty Anthropic `redacted_thinking` block, or\n * `undefined` if `block` is not a redacted_thinking block or carries empty/no\n * data. NON-EMPTY is required: the replay-side validator rejects a leading\n * empty-data redacted_thinking block, so recording `data: \"\"` would yield a\n * fixture that 400s under strict replay. Shared by every capture site (SSE,\n * Anthropic-native binary, non-streaming recorder) so the rule stays in one\n * place.\n */\nexport function capturedRedactedData(\n block: Record<string, unknown> | undefined,\n): string | undefined {\n if (\n block?.type === \"redacted_thinking\" &&\n typeof block.data === \"string\" &&\n block.data.length > 0\n ) {\n return block.data;\n }\n return undefined;\n}\n\n/**\n * Slice the first `max` UTF-16 code units of `s` for a diagnostic sample,\n * trimming a trailing lone high-surrogate so the resulting sample never ends on\n * a lone high surrogate (i.e. never mid-surrogate-pair).\n */\nfunction surrogateSafeSlice(s: string, max: number): string {\n let out = s.slice(0, max);\n if (out.length > 0) {\n const last = out.charCodeAt(out.length - 1);\n // A high surrogate (U+D800..U+DBFF) at the end is the lead of a split pair.\n if (last >= 0xd800 && last <= 0xdbff) {\n out = out.slice(0, -1);\n }\n }\n return out;\n}\n\n/**\n * Split a raw SSE body into per-event blocks.\n *\n * Events are delimited by a blank line. Real HTTP/SSE transports use CRLF\n * (`\\r\\n`) line endings, so the inter-event delimiter is `\\r\\n\\r\\n` (which\n * contains no `\\n\\n` substring) and each line ends with a trailing `\\r`.\n * Splitting on `/\\r?\\n\\r?\\n/` handles LF, CRLF, and mixed streams; per-line\n * `\\r` trimming happens in {@link splitSSELines}. Blank blocks are dropped.\n */\nfunction splitSSEEvents(body: string): string[] {\n return body.split(/\\r?\\n\\r?\\n/).filter((block) => block.trim().length > 0);\n}\n\n/**\n * Split a single SSE event block into its lines, trimming a trailing `\\r` so\n * CRLF streams parse identically to LF streams.\n */\nfunction splitSSELines(block: string): string[] {\n return block.split(\"\\n\").map((line) => (line.endsWith(\"\\r\") ? line.slice(0, -1) : line));\n}\n\n/**\n * Extract the SSE `data` field from a single event block's lines.\n *\n * Per the SSE spec a single event may carry MULTIPLE `data:` lines; the field\n * value is every data line's content joined with \"\\n\". Collecting only the\n * first `data:` line (e.g. via `.find`) corrupts payloads that a server split\n * across lines. Callers MUST pass lines produced by {@link splitSSELines} so\n * any trailing `\\r` is already stripped. Returns the joined payload (with the\n * leading \"data:\" prefix and one optional leading space stripped per line), or\n * `undefined` when the block contains no `data:` line.\n */\nfunction extractSSEData(lines: string[]): string | undefined {\n const dataParts: string[] = [];\n for (const line of lines) {\n if (!line.startsWith(\"data:\")) continue;\n // Strip \"data:\" then a single optional leading space, per the SSE spec.\n let part = line.slice(5);\n if (part.startsWith(\" \")) part = part.slice(1);\n dataParts.push(part);\n }\n if (dataParts.length === 0) return undefined;\n return dataParts.join(\"\\n\");\n}\n\n// ---------------------------------------------------------------------------\n// 1. OpenAI SSE\n// ---------------------------------------------------------------------------\n\n/**\n * Collapse OpenAI Chat Completions SSE stream into a single response.\n *\n * Format:\n * data: {\"id\":\"chatcmpl-123\",\"choices\":[{\"delta\":{\"content\":\"Hello\"}}]}\\n\\n\n * data: [DONE]\\n\\n\n */\nexport function collapseOpenAISSE(body: string): CollapseResult {\n const lines = splitSSEEvents(body);\n let content = \"\";\n let reasoning = \"\";\n const webSearchQueries: string[] = [];\n let droppedChunks = 0;\n let firstDroppedSample: string | undefined;\n let harmonyUnparsed = false;\n let harmonyNote: string | undefined;\n const toolCallMap = new Map<number, { id: string; name: string; arguments: string }>();\n // Fallback keying for deltas that OMIT `index`. Without this, every\n // index-less delta collapses under one `undefined`/NaN key, merging distinct\n // tool calls and corrupting arguments. Index-less fragments that share an\n // `id` correlate via `idKeyMap`; otherwise each gets a fresh synthetic key\n // assigned from a counter kept above any real index so sort order is stable.\n // The 1_000_000 sentinel assumes real provider tool-call indices stay below\n // it (they are small per-stream counters), so synthetic keys never collide.\n let nextSyntheticIndex = 1_000_000;\n const idKeyMap = new Map<string, number>();\n // Cross-channel order atoms (#274), in stream arrival order. A toolCall atom\n // references the same accumulator object stored in toolCallMap, so later arg\n // deltas mutate the block in place.\n const orderAtoms: OrderAtom[] = [];\n\n for (const line of lines) {\n const data = extractSSEData(splitSSELines(line));\n if (data === undefined) continue;\n\n const payload = data.trim();\n if (payload === \"[DONE]\") continue;\n\n let parsed: Record<string, unknown>;\n try {\n parsed = JSON.parse(payload) as Record<string, unknown>;\n } catch (err) {\n droppedChunks++;\n if (droppedChunks === 1) {\n const msg = err instanceof Error ? err.message : \"unknown\";\n firstDroppedSample = `parse failed (${msg}): ${surrogateSafeSlice(payload, 200)}`;\n }\n continue;\n }\n\n // Responses API reasoning events\n if (\n parsed.type === \"response.reasoning_summary_text.delta\" &&\n typeof parsed.delta === \"string\"\n ) {\n reasoning += parsed.delta;\n continue;\n }\n\n // Responses API web search events\n if (parsed.type === \"response.output_item.done\") {\n const item = parsed.item as Record<string, unknown> | undefined;\n if (item?.type === \"web_search_call\") {\n const action = item.action as Record<string, unknown> | undefined;\n if (action && typeof action.query === \"string\") {\n webSearchQueries.push(action.query);\n continue;\n }\n }\n }\n\n // Responses API text content events\n if (parsed.type === \"response.output_text.delta\" && typeof parsed.delta === \"string\") {\n content += parsed.delta;\n continue;\n }\n\n // Skip other Responses API structural events\n if (typeof parsed.type === \"string\" && parsed.type.startsWith(\"response.\")) {\n continue;\n }\n\n const choices = parsed.choices as Array<Record<string, unknown>> | undefined;\n if (!choices || choices.length === 0) continue;\n\n const delta = choices[0].delta as Record<string, unknown> | undefined;\n if (!delta) continue;\n\n // Reasoning content (OpenRouter / chat completions format)\n if (typeof delta.reasoning_content === \"string\") {\n reasoning += delta.reasoning_content;\n }\n\n // Text content\n if (typeof delta.content === \"string\") {\n content += delta.content;\n if (delta.content.length > 0) {\n orderAtoms.push({ kind: \"text\", text: delta.content });\n }\n }\n\n // Tool calls\n const toolCalls = delta.tool_calls as Array<Record<string, unknown>> | undefined;\n if (toolCalls) {\n for (const tc of toolCalls) {\n const fn = tc.function as Record<string, unknown> | undefined;\n const rawId = typeof tc.id === \"string\" ? tc.id : undefined;\n\n // Resolve a stable map key. Prefer the streamed `index`; when it is\n // absent, correlate by `id` if present, else mint a fresh synthetic\n // key so distinct index-less calls never merge.\n let index: number;\n if (typeof tc.index === \"number\") {\n index = tc.index;\n } else if (rawId !== undefined) {\n const existing = idKeyMap.get(rawId);\n if (existing !== undefined) {\n index = existing;\n } else {\n index = nextSyntheticIndex++;\n idKeyMap.set(rawId, index);\n }\n } else {\n index = nextSyntheticIndex++;\n }\n\n if (!toolCallMap.has(index)) {\n const created = {\n id: rawId ?? \"\",\n name: (fn?.name as string) ?? \"\",\n arguments: \"\",\n };\n toolCallMap.set(index, created);\n // Record the tool atom at the position its FIRST delta arrived; it\n // references `created` so later name/arg deltas fill it in place.\n orderAtoms.push({ kind: \"toolCall\", ref: created });\n }\n\n const entry = toolCallMap.get(index)!;\n if (fn?.name && typeof fn.name === \"string\" && !entry.name) {\n entry.name = fn.name;\n }\n if (tc.id && typeof tc.id === \"string\" && !entry.id) {\n entry.id = tc.id;\n }\n if (fn?.arguments && typeof fn.arguments === \"string\") {\n entry.arguments += fn.arguments;\n }\n }\n }\n }\n\n // Open-weight gpt-oss models (Ollama / vLLM / OpenRouter) stream tool calls\n // as raw harmony channel tokens inside delta.content rather than structured\n // delta.tool_calls. Harmony parsing is FALLBACK-ONLY: attempt it ONLY when\n // there are NO structured delta.tool_calls. If structured tool calls exist,\n // any harmony-looking content is prose — never merged (no phantom tool call),\n // never stamped as truncated/dropped. When harmony IS the only source, a\n // successful parse routes channels (content/reasoning/toolCalls); a failure\n // preserves content VERBATIM and surfaces the distinct `harmonyUnparsed`\n // signal (NOT droppedChunks/truncated — the bytes are not lost).\n const harmonyToolCalls: ToolCall[] = [];\n if (toolCallMap.size === 0 && isHarmonyContent(content)) {\n const parsed = parseHarmonyContent(content);\n if (parsed.failed) {\n harmonyUnparsed = true;\n harmonyNote = `harmony tokens present but unparseable; content preserved verbatim: ${surrogateSafeSlice(content, 200)}`;\n } else {\n content = parsed.content;\n if (parsed.reasoning) {\n reasoning += parsed.reasoning;\n }\n harmonyToolCalls.push(...parsed.toolCalls);\n }\n }\n\n if (toolCallMap.size > 0 || harmonyToolCalls.length > 0) {\n const blocks = buildOrderedBlocks(orderAtoms);\n // When the stream is interleaved we persist ordered `blocks`; the flat\n // `toolCalls` MUST then describe the same calls in the same order so the two\n // representations never disagree (#274). The toolCall atoms reference the\n // same accumulator objects as `toolCallMap`, so derive the flat list from\n // those atoms (stream-arrival order, matching blocks) when blocks exist;\n // otherwise keep the legacy index-sorted order for byte-identical fixtures.\n const orderedToolCalls = orderAtoms\n .filter(\n (a): a is { kind: \"toolCall\"; ref: { name: string; arguments: string; id?: string } } =>\n a.kind === \"toolCall\",\n )\n .map((a) => ({\n name: a.ref.name,\n arguments: normalizeToolArguments(a.ref.arguments),\n ...(a.ref.id ? { id: a.ref.id } : {}),\n }));\n const indexSortedToolCalls = Array.from(toolCallMap.entries())\n .sort(([a], [b]) => a - b)\n .map(([, tc]) => ({\n name: tc.name,\n arguments: normalizeToolArguments(tc.arguments),\n ...(tc.id ? { id: tc.id } : {}),\n }));\n return {\n ...(blocks ? { blocks } : {}),\n ...(content ? { content } : {}),\n // Fallback-only: harmonyToolCalls are populated ONLY in the\n // no-structured-calls branch, so this is never a merge of both sources.\n toolCalls: [...(blocks ? orderedToolCalls : indexSortedToolCalls), ...harmonyToolCalls],\n // Reasoning is preserved alongside tool calls for ALL structured streams\n // (DeepSeek/OpenRouter reasoning_content, harmony analysis channel), at\n // parity with every other collapser and the non-streaming path.\n ...(reasoning ? { reasoning } : {}),\n // webSearches parity with the text-only return branch.\n ...(webSearchQueries.length > 0 ? { webSearches: webSearchQueries } : {}),\n ...(droppedChunks > 0 ? { droppedChunks } : {}),\n ...(firstDroppedSample ? { firstDroppedSample } : {}),\n ...(harmonyUnparsed ? { harmonyUnparsed: true } : {}),\n ...(harmonyNote ? { harmonyNote } : {}),\n };\n }\n\n return {\n content,\n ...(reasoning ? { reasoning } : {}),\n ...(webSearchQueries.length > 0 ? { webSearches: webSearchQueries } : {}),\n ...(droppedChunks > 0 ? { droppedChunks } : {}),\n ...(firstDroppedSample ? { firstDroppedSample } : {}),\n ...(harmonyUnparsed ? { harmonyUnparsed: true } : {}),\n ...(harmonyNote ? { harmonyNote } : {}),\n };\n}\n\n// ---------------------------------------------------------------------------\n// 2. Anthropic SSE\n// ---------------------------------------------------------------------------\n\n/**\n * Collapse Anthropic Claude Messages SSE stream into a single response.\n *\n * Format:\n * event: message_start\\ndata: {...}\\n\\n\n * event: content_block_delta\\ndata: {\"delta\":{\"type\":\"text_delta\",\"text\":\"Hello\"}}\\n\\n\n */\nexport function collapseAnthropicSSE(body: string): CollapseResult {\n const blocks = splitSSEEvents(body);\n let content = \"\";\n let reasoning = \"\";\n // Real cryptographic signature captured from a `signature_delta`; stays\n // undefined when the stream carried none (e.g. aimock's own placeholder turns\n // or non-thinking turns). Carried so a recorded real-provider thinking turn\n // can replay its ACTUAL signature instead of aimock's placeholder.\n let reasoningSignature: string | undefined;\n // Opaque `data` payloads of any `redacted_thinking` content blocks, in stream\n // order. Stays empty when none present. Carried so a recorded redacted-thinking\n // turn round-trips its encrypted reasoning faithfully on replay.\n const redactedThinking: string[] = [];\n let droppedChunks = 0;\n let firstDroppedSample: string | undefined;\n const toolCallMap = new Map<number, { id: string; name: string; arguments: string }>();\n // Fallback keying for content blocks that OMIT `index` (mirrors the OpenAI /\n // Cohere / Bedrock guards). Without it, every index-less block collapses\n // under one `undefined` key, merging distinct tool_use blocks. Index-less\n // starts mint a fresh synthetic key (kept above any real index so sort order\n // is stable). Despite its name, `lastSyntheticIndex` tracks whichever\n // tool_use start most recently opened REGARDLESS of whether its index was\n // real or synthetic (it is set on every tool_use start; thinking /\n // redacted_thinking starts do not touch it), so an index-less delta\n // correlates to the most-recent tool_use start — not just to the last\n // synthetic one. The 1_000_000 sentinel assumes real provider indices stay\n // below it.\n let nextSyntheticIndex = 1_000_000;\n let lastSyntheticIndex: number | undefined;\n // Cross-channel order atoms (#274), in stream arrival order.\n const orderAtoms: OrderAtom[] = [];\n\n for (const block of blocks) {\n const lines = splitSSELines(block);\n const eventLine = lines.find((l) => l.startsWith(\"event:\"));\n const data = extractSSEData(lines);\n if (data === undefined) continue;\n\n const eventType = eventLine ? eventLine.slice(6).trim() : \"\";\n const payload = data.trim();\n\n let parsed: Record<string, unknown>;\n try {\n parsed = JSON.parse(payload) as Record<string, unknown>;\n } catch (err) {\n droppedChunks++;\n if (droppedChunks === 1) {\n const msg = err instanceof Error ? err.message : \"unknown\";\n firstDroppedSample = `parse failed (${msg}): ${surrogateSafeSlice(payload, 200)}`;\n }\n continue;\n }\n\n if (eventType === \"content_block_start\") {\n const contentBlock = parsed.content_block as Record<string, unknown> | undefined;\n // A `redacted_thinking` block carries its encrypted reasoning in an opaque\n // `data` string on the start event (no deltas follow). Capture it so the\n // recorded turn can replay the redacted block faithfully\n // (see capturedRedactedData for the non-empty rule).\n const redactedData = capturedRedactedData(contentBlock);\n if (redactedData !== undefined) {\n redactedThinking.push(redactedData);\n }\n if (contentBlock?.type === \"tool_use\") {\n // Prefer the streamed `index`; when absent, mint a fresh synthetic key\n // so distinct index-less tool_use blocks never merge.\n let index: number;\n if (typeof parsed.index === \"number\") {\n index = parsed.index;\n } else {\n index = nextSyntheticIndex++;\n }\n lastSyntheticIndex = index;\n const created = {\n id: (contentBlock.id as string) ?? \"\",\n name: (contentBlock.name as string) ?? \"\",\n arguments: \"\",\n };\n toolCallMap.set(index, created);\n // Record the tool atom at the position the tool_use block opened; it\n // references `created` so later input_json_delta fragments fill it in.\n orderAtoms.push({ kind: \"toolCall\", ref: created });\n }\n }\n\n if (eventType === \"content_block_delta\") {\n const delta = parsed.delta as Record<string, unknown> | undefined;\n if (!delta) continue;\n\n if (delta.type === \"text_delta\" && typeof delta.text === \"string\") {\n content += delta.text;\n if (delta.text.length > 0) {\n orderAtoms.push({ kind: \"text\", text: delta.text });\n }\n }\n\n if (delta.type === \"thinking_delta\" && typeof delta.thinking === \"string\") {\n reasoning += delta.thinking;\n }\n\n // The real cryptographic signature arrives via a trailing\n // `signature_delta` (the `content_block_start` carried \"\"). Capture the\n // last one seen so a recorded thinking turn replays its actual signature.\n // Last-signature-wins: a turn with MULTIPLE thinking blocks overwrites this\n // on each block, so the merged `reasoning` string ends up bound only to the\n // FINAL block's signature — per-block signatures are not preserved.\n if (delta.type === \"signature_delta\" && typeof delta.signature === \"string\") {\n reasoningSignature = delta.signature;\n }\n\n if (delta.type === \"input_json_delta\" && typeof delta.partial_json === \"string\") {\n // Use the streamed `index` when present; otherwise correlate to the\n // most recent tool_use start (mirrors the start-side fallback).\n const index = typeof parsed.index === \"number\" ? parsed.index : lastSyntheticIndex;\n // A delta that cannot correlate to any known start (no streamed index\n // AND no prior start, or a stale index with no entry) would otherwise\n // silently lose its args. Account for it as a dropped chunk instead of\n // vanishing (mirrors the Cohere uncorrelated-delta path).\n const entry = index !== undefined ? toolCallMap.get(index) : undefined;\n if (entry) {\n entry.arguments += delta.partial_json;\n } else {\n droppedChunks++;\n if (droppedChunks === 1) {\n firstDroppedSample = `input_json_delta with no correlating tool_use start: ${surrogateSafeSlice(\n payload,\n 200,\n )}`;\n }\n }\n }\n }\n }\n\n if (toolCallMap.size > 0) {\n const orderedBlocks = buildOrderedBlocks(orderAtoms);\n // When interleaved (`blocks` present) the flat `toolCalls` MUST match the\n // blocks' order/identity (#274). The toolCall atoms reference the same\n // accumulator objects as `toolCallMap`, so derive the flat list from those\n // atoms (stream-arrival order) when blocks exist; otherwise keep the legacy\n // index-sorted order for byte-identical fixtures.\n const orderedToolCalls = orderAtoms\n .filter(\n (a): a is { kind: \"toolCall\"; ref: { name: string; arguments: string; id?: string } } =>\n a.kind === \"toolCall\",\n )\n .map((a) => ({\n name: a.ref.name,\n arguments: normalizeToolArguments(a.ref.arguments),\n ...(a.ref.id ? { id: a.ref.id } : {}),\n }));\n const indexSortedToolCalls = Array.from(toolCallMap.entries())\n .sort(([a], [b]) => a - b)\n .map(([, tc]) => ({\n name: tc.name,\n arguments: normalizeToolArguments(tc.arguments),\n ...(tc.id ? { id: tc.id } : {}),\n }));\n return {\n ...(orderedBlocks ? { blocks: orderedBlocks } : {}),\n ...(content ? { content } : {}),\n toolCalls: orderedBlocks ? orderedToolCalls : indexSortedToolCalls,\n ...(reasoning ? { reasoning } : {}),\n ...(reasoningSignature ? { reasoningSignature } : {}),\n ...(redactedThinking.length > 0 ? { redactedThinking } : {}),\n ...(droppedChunks > 0 ? { droppedChunks } : {}),\n ...(firstDroppedSample ? { firstDroppedSample } : {}),\n };\n }\n\n return {\n content,\n ...(reasoning ? { reasoning } : {}),\n ...(reasoningSignature ? { reasoningSignature } : {}),\n ...(redactedThinking.length > 0 ? { redactedThinking } : {}),\n ...(droppedChunks > 0 ? { droppedChunks } : {}),\n ...(firstDroppedSample ? { firstDroppedSample } : {}),\n };\n}\n\n// ---------------------------------------------------------------------------\n// 3. Gemini SSE\n// ---------------------------------------------------------------------------\n\n/**\n * Collapse Gemini SSE stream into a single response.\n *\n * Format (data-only, no event prefix, no [DONE]):\n * data: {\"candidates\":[{\"content\":{\"parts\":[{\"text\":\"Hello\"}]}}]}\\n\\n\n */\nexport function collapseGeminiSSE(body: string): CollapseResult {\n const lines = splitSSEEvents(body);\n let content = \"\";\n let reasoning = \"\";\n let droppedChunks = 0;\n let firstDroppedSample: string | undefined;\n let audioB64 = \"\";\n let audioMimeType: string | undefined;\n const toolCalls: ToolCall[] = [];\n // Cross-channel order atoms (#274), in stream arrival order.\n const orderAtoms: OrderAtom[] = [];\n\n for (const line of lines) {\n const data = extractSSEData(splitSSELines(line));\n if (data === undefined) continue;\n\n const payload = data.trim();\n\n let parsed: Record<string, unknown>;\n try {\n parsed = JSON.parse(payload) as Record<string, unknown>;\n } catch (err) {\n droppedChunks++;\n if (droppedChunks === 1) {\n const msg = err instanceof Error ? err.message : \"unknown\";\n firstDroppedSample = `parse failed (${msg}): ${surrogateSafeSlice(payload, 200)}`;\n }\n continue;\n }\n\n const candidates = parsed.candidates as Array<Record<string, unknown>> | undefined;\n if (!candidates || candidates.length === 0) continue;\n\n const candidateContent = candidates[0].content as Record<string, unknown> | undefined;\n if (!candidateContent) continue;\n\n const parts = candidateContent.parts as Array<Record<string, unknown>> | undefined;\n if (!parts || parts.length === 0) continue;\n\n for (const part of parts) {\n if (part.functionCall) {\n const fc = part.functionCall as Record<string, unknown>;\n const created: ToolCall = {\n name: String(fc.name ?? \"\"),\n // Default undefined/object args to a JSON object string (matches\n // collapseGeminiInteractionsSSE / Ollama). JSON.stringify(undefined)\n // would otherwise yield the VALUE undefined, violating the\n // ToolCall.arguments:string contract.\n arguments:\n typeof fc.args === \"string\" ? (fc.args as string) : JSON.stringify(fc.args ?? {}),\n };\n toolCalls.push(created);\n // Record the tool atom at the position this functionCall part arrived.\n orderAtoms.push({ kind: \"toolCall\", ref: created });\n } else if (\n part.inlineData &&\n typeof (part.inlineData as Record<string, unknown>).mimeType === \"string\" &&\n ((part.inlineData as Record<string, unknown>).mimeType as string).startsWith(\"audio/\")\n ) {\n const inlineData = part.inlineData as Record<string, unknown>;\n if (!audioMimeType) {\n audioMimeType = inlineData.mimeType as string;\n }\n if (typeof inlineData.data === \"string\") {\n audioB64 += inlineData.data;\n }\n } else if (typeof part.text === \"string\") {\n if (part.thought) {\n reasoning += part.text;\n } else {\n content += part.text;\n if (part.text.length > 0) {\n orderAtoms.push({ kind: \"text\", text: part.text });\n }\n }\n }\n }\n }\n\n // Normalize the flat tool calls' arguments identically to the block path so\n // the two representations never disagree (#274). The toolCall atoms reference\n // the same `created` objects pushed here, so blocks and flat describe the same\n // calls in the same order; this only reconciles empty/missing → \"{}\".\n const normalizedToolCalls = toolCalls.map((tc) => ({\n ...tc,\n arguments: normalizeToolArguments(tc.arguments),\n }));\n\n if (audioB64) {\n // Preserve any content / reasoning / tool calls accumulated in the same\n // stream — a Gemini turn can interleave audio with text and functionCall\n // parts, and the early return must not silently drop them.\n //\n // Deliberately do NOT build ordered `blocks` here (#274, R2-N2): the audio\n // collapse shape maps to AudioResponse, which has no `blocks` slot, and the\n // recorder's audio branch never persists `collapsed.blocks`. Producing block\n // ordering on this path would be silently produced-then-dropped, advertising\n // a field this result shape can't carry. Block ordering is built only on the\n // content+toolCalls path below, which can actually carry it.\n return {\n audioB64,\n audioMimeType,\n ...(content ? { content } : {}),\n ...(reasoning ? { reasoning } : {}),\n ...(normalizedToolCalls.length > 0 ? { toolCalls: normalizedToolCalls } : {}),\n ...(droppedChunks > 0 ? { droppedChunks } : {}),\n ...(firstDroppedSample ? { firstDroppedSample } : {}),\n };\n }\n\n if (toolCalls.length > 0) {\n const blocks = buildOrderedBlocks(orderAtoms);\n return {\n ...(blocks ? { blocks } : {}),\n ...(content ? { content } : {}),\n toolCalls: normalizedToolCalls,\n ...(reasoning ? { reasoning } : {}),\n ...(droppedChunks > 0 ? { droppedChunks } : {}),\n ...(firstDroppedSample ? { firstDroppedSample } : {}),\n };\n }\n\n return {\n content,\n ...(reasoning ? { reasoning } : {}),\n ...(droppedChunks > 0 ? { droppedChunks } : {}),\n ...(firstDroppedSample ? { firstDroppedSample } : {}),\n };\n}\n\n// ---------------------------------------------------------------------------\n// 4. Ollama NDJSON\n// ---------------------------------------------------------------------------\n\n/**\n * Collapse Ollama NDJSON stream into a single response.\n *\n * /api/chat format:\n * {\"model\":\"llama3\",\"message\":{\"role\":\"assistant\",\"content\":\"Hello\"},\"done\":false}\\n\n *\n * /api/generate format:\n * {\"model\":\"llama3\",\"response\":\"Hello\",\"done\":false}\\n\n *\n * Open-weight gpt-oss served via Ollama streams harmony channel tokens inside\n * `message.content` (just like the OpenAI SSE path), so after accumulation the\n * content is run through the same fail-safe {@link parseHarmonyContent} gate to\n * capture structured tool calls / reasoning instead of leaking raw tokens.\n */\nexport function collapseOllamaNDJSON(body: string): CollapseResult {\n const lines = body.split(\"\\n\").filter((l) => l.trim().length > 0);\n let content = \"\";\n let reasoning = \"\";\n let droppedChunks = 0;\n let firstDroppedSample: string | undefined;\n let harmonyUnparsed = false;\n let harmonyNote: string | undefined;\n const toolCalls: ToolCall[] = [];\n // Cross-channel order atoms (#274), in stream arrival order.\n const orderAtoms: OrderAtom[] = [];\n\n for (const line of lines) {\n let parsed: Record<string, unknown>;\n try {\n parsed = JSON.parse(line.trim()) as Record<string, unknown>;\n } catch (err) {\n droppedChunks++;\n if (droppedChunks === 1) {\n const msg = err instanceof Error ? err.message : \"unknown\";\n firstDroppedSample = `parse failed (${msg}): ${surrogateSafeSlice(line.trim(), 200)}`;\n }\n continue;\n }\n\n // /api/chat format\n const message = parsed.message as Record<string, unknown> | undefined;\n if (message) {\n if (typeof message.content === \"string\") {\n content += message.content;\n if (message.content.length > 0) {\n orderAtoms.push({ kind: \"text\", text: message.content });\n }\n }\n\n // Tool calls\n if (Array.isArray(message.tool_calls)) {\n for (const tc of message.tool_calls as Array<Record<string, unknown>>) {\n const fn = tc.function as Record<string, unknown> | undefined;\n if (fn) {\n const created: ToolCall = {\n name: String(fn.name ?? \"\"),\n // Default undefined/object args to a JSON object (matching\n // collapseGeminiInteractionsSSE) — JSON.stringify(undefined)\n // would otherwise yield the literal string \"undefined\".\n arguments:\n typeof fn.arguments === \"string\"\n ? fn.arguments\n : JSON.stringify(fn.arguments ?? {}),\n };\n toolCalls.push(created);\n orderAtoms.push({ kind: \"toolCall\", ref: created });\n }\n }\n }\n }\n\n // /api/generate format\n else if (typeof parsed.response === \"string\") {\n content += parsed.response;\n if (parsed.response.length > 0) {\n orderAtoms.push({ kind: \"text\", text: parsed.response });\n }\n }\n }\n\n // Open-weight gpt-oss served via Ollama streams harmony channel tokens inside\n // message.content (same as the OpenAI SSE path). Harmony parsing is\n // FALLBACK-ONLY: attempt it ONLY when there are NO structured message\n // tool_calls. If structured tool calls exist, harmony-looking content is\n // prose — never merged (no phantom), never stamped truncated/dropped. On a\n // harmony failure the content is preserved VERBATIM and surfaced via the\n // distinct `harmonyUnparsed` signal (NOT droppedChunks/truncated).\n if (toolCalls.length === 0 && isHarmonyContent(content)) {\n const parsedHarmony = parseHarmonyContent(content);\n if (parsedHarmony.failed) {\n harmonyUnparsed = true;\n harmonyNote = `harmony tokens present but unparseable; content preserved verbatim: ${surrogateSafeSlice(content, 200)}`;\n } else {\n content = parsedHarmony.content;\n if (parsedHarmony.reasoning) {\n reasoning += parsedHarmony.reasoning;\n }\n toolCalls.push(...parsedHarmony.toolCalls);\n }\n }\n\n if (toolCalls.length > 0) {\n const blocks = buildOrderedBlocks(orderAtoms);\n // Normalize flat arguments identically to the block path so the two\n // representations never disagree (#274); same `created` refs, same order.\n const normalizedToolCalls = toolCalls.map((tc) => ({\n ...tc,\n arguments: normalizeToolArguments(tc.arguments),\n }));\n return {\n ...(blocks ? { blocks } : {}),\n ...(content ? { content } : {}),\n toolCalls: normalizedToolCalls,\n ...(reasoning ? { reasoning } : {}),\n ...(droppedChunks > 0 ? { droppedChunks } : {}),\n ...(firstDroppedSample ? { firstDroppedSample } : {}),\n ...(harmonyUnparsed ? { harmonyUnparsed: true } : {}),\n ...(harmonyNote ? { harmonyNote } : {}),\n };\n }\n\n return {\n content,\n ...(reasoning ? { reasoning } : {}),\n ...(droppedChunks > 0 ? { droppedChunks } : {}),\n ...(firstDroppedSample ? { firstDroppedSample } : {}),\n ...(harmonyUnparsed ? { harmonyUnparsed: true } : {}),\n ...(harmonyNote ? { harmonyNote } : {}),\n };\n}\n\n// ---------------------------------------------------------------------------\n// 5. Cohere SSE\n// ---------------------------------------------------------------------------\n\n/**\n * Collapse Cohere SSE stream into a single response.\n *\n * Format:\n * event: content-delta\\ndata: {\"type\":\"content-delta\",\"delta\":{\"message\":{\"content\":{\"text\":\"Hello\"}}}}\\n\\n\n */\nexport function collapseCohereSSE(body: string): CollapseResult {\n const blocks = splitSSEEvents(body);\n let content = \"\";\n // Reasoning text assembled from `thinking` content-delta blocks. Cohere's\n // reasoning models stream a `content.type === \"thinking\"` block carrying a\n // `thinking` string before the `text` block; capture it so a recorded\n // reasoning turn round-trips its reasoning instead of dropping it.\n let reasoning = \"\";\n let droppedChunks = 0;\n let firstDroppedSample: string | undefined;\n const toolCallMap = new Map<number, { id: string; name: string; arguments: string }>();\n // Fallback keying for tool-call events that OMIT `index` (mirrors the\n // OpenAI guard). Without it, every index-less tool-call-start collapses\n // under one `undefined`/NaN key, merging distinct calls. Index-less starts\n // mint a fresh synthetic key. `lastStartKey` tracks the most-recent\n // tool-call-start key REGARDLESS of whether it was real or synthetic, so an\n // index-less tool-call-delta correlates to whichever start most recently\n // opened — not just to the last synthetic one. The 1_000_000 sentinel\n // assumes real provider indices stay below it.\n let nextSyntheticIndex = 1_000_000;\n let lastStartKey: number | undefined;\n // Cross-channel order atoms (#274), in stream arrival order. Cohere v2 SSE is\n // a single ordered stream (content-delta / tool-call-start interleave on the\n // wire), so a toolCall atom references the same accumulator object stored in\n // toolCallMap and later tool-call-delta fragments mutate it in place.\n const orderAtoms: OrderAtom[] = [];\n\n for (const block of blocks) {\n const lines = splitSSELines(block);\n const eventLine = lines.find((l) => l.startsWith(\"event:\"));\n const data = extractSSEData(lines);\n if (data === undefined) continue;\n\n const eventType = eventLine ? eventLine.slice(6).trim() : \"\";\n const payload = data.trim();\n\n let parsed: Record<string, unknown>;\n try {\n parsed = JSON.parse(payload) as Record<string, unknown>;\n } catch (err) {\n droppedChunks++;\n if (droppedChunks === 1) {\n const msg = err instanceof Error ? err.message : \"unknown\";\n firstDroppedSample = `parse failed (${msg}): ${surrogateSafeSlice(payload, 200)}`;\n }\n continue;\n }\n\n if (eventType === \"content-delta\") {\n const delta = parsed.delta as Record<string, unknown> | undefined;\n const message = delta?.message as Record<string, unknown> | undefined;\n const contentObj = message?.content as Record<string, unknown> | undefined;\n if (contentObj && contentObj.type === \"thinking\" && typeof contentObj.thinking === \"string\") {\n reasoning += contentObj.thinking;\n } else if (contentObj && typeof contentObj.text === \"string\") {\n content += contentObj.text;\n // Reasoning (`thinking`) is intentionally NOT pushed as an order atom —\n // it is not a block channel (mirrors the other collapsers).\n if (contentObj.text.length > 0) {\n orderAtoms.push({ kind: \"text\", text: contentObj.text });\n }\n }\n }\n\n if (eventType === \"tool-call-start\") {\n let index: number;\n if (typeof parsed.index === \"number\") {\n index = parsed.index;\n } else {\n index = nextSyntheticIndex++;\n }\n // Track the most-recent start key (real OR synthetic) so a following\n // index-less delta correlates to whichever call just opened.\n lastStartKey = index;\n const delta = parsed.delta as Record<string, unknown> | undefined;\n const message = delta?.message as Record<string, unknown> | undefined;\n const toolCalls = message?.tool_calls as Record<string, unknown> | undefined;\n if (toolCalls) {\n const fn = toolCalls.function as Record<string, unknown> | undefined;\n const created = {\n id: (toolCalls.id as string) ?? \"\",\n name: (fn?.name as string) ?? \"\",\n arguments: \"\",\n };\n toolCallMap.set(index, created);\n // Record the tool atom at the position its tool-call-start arrived; it\n // references `created` so later tool-call-delta args fill it in place.\n orderAtoms.push({ kind: \"toolCall\", ref: created });\n }\n }\n\n if (eventType === \"tool-call-delta\") {\n // Use the streamed `index` when present; otherwise correlate to the most\n // recent tool-call-start (real or synthetic key).\n const index = typeof parsed.index === \"number\" ? parsed.index : lastStartKey;\n const delta = parsed.delta as Record<string, unknown> | undefined;\n const message = delta?.message as Record<string, unknown> | undefined;\n const toolCalls = message?.tool_calls as Record<string, unknown> | undefined;\n if (toolCalls) {\n const fn = toolCalls.function as Record<string, unknown> | undefined;\n if (fn && typeof fn.arguments === \"string\") {\n // A delta that cannot correlate to any known start (no streamed\n // index AND no prior start) would otherwise silently lose its args.\n // Account for it as a dropped chunk instead of vanishing.\n const entry = index !== undefined ? toolCallMap.get(index) : undefined;\n if (entry) {\n entry.arguments += fn.arguments;\n } else {\n droppedChunks++;\n if (droppedChunks === 1) {\n firstDroppedSample = `tool-call-delta with no correlating start: ${surrogateSafeSlice(\n payload,\n 200,\n )}`;\n }\n }\n }\n }\n }\n }\n\n if (toolCallMap.size > 0) {\n const orderedBlocks = buildOrderedBlocks(orderAtoms);\n // When interleaved, persist ordered `blocks` and derive the flat `toolCalls`\n // from the SAME tool atoms (stream-arrival order) so the two never disagree\n // (#274 F4/F5). Otherwise keep the legacy index-sorted order so non-\n // interleaved fixtures stay byte-identical. Both paths normalize arguments\n // so a zero-arg call persists \"{}\" rather than \"\".\n const orderedToolCalls = orderAtoms\n .filter(\n (a): a is { kind: \"toolCall\"; ref: { name: string; arguments: string; id?: string } } =>\n a.kind === \"toolCall\",\n )\n .map((a) => ({\n name: a.ref.name,\n arguments: normalizeToolArguments(a.ref.arguments),\n ...(a.ref.id ? { id: a.ref.id } : {}),\n }));\n const indexSortedToolCalls = Array.from(toolCallMap.entries())\n .sort(([a], [b]) => a - b)\n .map(([, tc]) => ({\n name: tc.name,\n arguments: normalizeToolArguments(tc.arguments),\n ...(tc.id ? { id: tc.id } : {}),\n }));\n return {\n ...(orderedBlocks ? { blocks: orderedBlocks } : {}),\n ...(content ? { content } : {}),\n toolCalls: orderedBlocks ? orderedToolCalls : indexSortedToolCalls,\n ...(reasoning ? { reasoning } : {}),\n ...(droppedChunks > 0 ? { droppedChunks } : {}),\n ...(firstDroppedSample ? { firstDroppedSample } : {}),\n };\n }\n\n return {\n content,\n ...(reasoning ? { reasoning } : {}),\n ...(droppedChunks > 0 ? { droppedChunks } : {}),\n ...(firstDroppedSample ? { firstDroppedSample } : {}),\n };\n}\n\n// ---------------------------------------------------------------------------\n// 6. Bedrock EventStream (binary)\n// ---------------------------------------------------------------------------\n\n/**\n * Decode AWS Event Stream binary frames and extract JSON payloads.\n *\n * Binary frame layout:\n * [total_length: 4B uint32-BE]\n * [headers_length: 4B uint32-BE]\n * [prelude_crc32: 4B]\n * [headers: variable]\n * [payload: variable]\n * [message_crc32: 4B]\n */\nfunction decodeEventStreamFrames(buf: Buffer): {\n frames: Array<{ headers: Record<string, string>; payload: Buffer }>;\n truncated: boolean;\n} {\n const frames: Array<{ headers: Record<string, string>; payload: Buffer }> = [];\n let offset = 0;\n\n while (offset < buf.length) {\n if (offset + 12 > buf.length) break;\n\n const totalLength = buf.readUInt32BE(offset);\n const headersLength = buf.readUInt32BE(offset + 4);\n\n // Validate bounds: ensure the full frame is within the buffer\n if (totalLength < 12 || offset + totalLength > buf.length) {\n return { frames, truncated: true };\n }\n\n // Validate prelude CRC\n const preludeCrc = buf.readUInt32BE(offset + 8);\n const computedPreludeCrc = crc32(buf.subarray(offset, offset + 8));\n if (preludeCrc >>> 0 !== computedPreludeCrc >>> 0) {\n return { frames, truncated: true }; // Prelude CRC mismatch — stop parsing\n }\n\n // Parse headers\n const headersStart = offset + 12;\n const headersEnd = headersStart + headersLength;\n const payloadEnd = offset + totalLength - 4; // minus message CRC\n\n // Validate the headers region fits inside the frame. A frame can carry a\n // valid prelude CRC yet declare a `headersLength` that overruns the payload\n // region (the prelude CRC only covers total/headers length, not the body).\n // Without this guard a per-header read walks off the buffer and throws an\n // uncaught RangeError; treat it as truncation instead.\n if (headersEnd > payloadEnd || headersEnd > buf.length) {\n return { frames, truncated: true };\n }\n\n const headers: Record<string, string> = {};\n let hOffset = headersStart;\n let headerOverrun = false;\n\n while (hOffset < headersEnd) {\n // Each read must stay within the declared headers region. Bail out\n // (truncated) on any overrun rather than reading past the boundary.\n if (hOffset + 1 > headersEnd) {\n headerOverrun = true;\n break;\n }\n const nameLen = buf.readUInt8(hOffset);\n hOffset += 1;\n if (hOffset + nameLen + 1 + 2 > headersEnd) {\n headerOverrun = true;\n break;\n }\n const name = buf.subarray(hOffset, hOffset + nameLen).toString(\"utf8\");\n hOffset += nameLen;\n // Skip header type byte (type 7 = STRING)\n hOffset += 1;\n const valueLen = buf.readUInt16BE(hOffset);\n hOffset += 2;\n if (hOffset + valueLen > headersEnd) {\n headerOverrun = true;\n break;\n }\n const value = buf.subarray(hOffset, hOffset + valueLen).toString(\"utf8\");\n hOffset += valueLen;\n headers[name] = value;\n }\n\n if (headerOverrun) {\n return { frames, truncated: true };\n }\n\n // Extract payload\n const payloadStart = headersEnd;\n const payload = buf.subarray(payloadStart, payloadEnd);\n\n // Validate message CRC (covers entire frame minus last 4 bytes)\n const messageCrc = buf.readUInt32BE(offset + totalLength - 4);\n const computedMessageCrc = crc32(buf.subarray(offset, offset + totalLength - 4));\n if (messageCrc >>> 0 !== computedMessageCrc >>> 0) {\n return { frames, truncated: true }; // Message CRC mismatch — stop parsing\n }\n\n frames.push({ headers, payload });\n offset += totalLength;\n }\n\n return { frames, truncated: false };\n}\n\n/**\n * Collapse Bedrock binary Event Stream into a single response.\n *\n * Each frame contains a JSON payload with event types like:\n * contentBlockDelta, contentBlockStart, etc.\n */\nexport function collapseBedrockEventStream(body: Buffer): CollapseResult {\n const { frames, truncated } = decodeEventStreamFrames(body);\n let content = \"\";\n // Reasoning text assembled from Converse `reasoningContent.text` deltas. The\n // Bedrock Converse stream interleaves a `delta.reasoningContent` block carrying\n // the model's reasoning; capture it so a recorded reasoning turn round-trips\n // its reasoning instead of dropping it.\n let reasoning = \"\";\n // Anthropic-native extended-thinking fields (invoke-with-response-stream).\n // The native binary branch carries the same thinking/signature/redacted\n // channel as the Anthropic SSE collapser; it mirrors that path's\n // thinking/signature/redacted capture rules so a recorded reasoning turn\n // round-trips instead of silently dropping it. Unlike the SSE path, binary\n // tool_use correlation has no index-less fallback and relies on an explicit\n // `index`.\n let reasoningSignature: string | undefined;\n const redactedThinking: string[] = [];\n let droppedChunks = 0;\n let firstDroppedSample: string | undefined;\n const toolCallMap = new Map<number, { id: string; name: string; arguments: string }>();\n // Cross-channel order atoms (#274), in frame (stream) arrival order. Both the\n // Anthropic-native and Converse sub-protocols append text and tool deltas in\n // frame order, so a toolCall atom references the same accumulator object stored\n // in toolCallMap and later arg deltas mutate it in place.\n const orderAtoms: OrderAtom[] = [];\n\n for (const frame of frames) {\n const frameStr = frame.payload.toString(\"utf8\");\n let parsed: Record<string, unknown>;\n try {\n parsed = JSON.parse(frameStr) as Record<string, unknown>;\n } catch (err) {\n droppedChunks++;\n if (droppedChunks === 1) {\n const msg = err instanceof Error ? err.message : \"unknown\";\n firstDroppedSample = `parse failed (${msg}): ${surrogateSafeSlice(frameStr, 200)}`;\n }\n continue;\n }\n\n // Anthropic Messages format (invoke-with-response-stream): flat payload with \"type\" field\n if (parsed.type === \"content_block_delta\") {\n const delta = parsed.delta as Record<string, unknown> | undefined;\n if (delta?.type === \"text_delta\" && typeof delta.text === \"string\") {\n content += delta.text;\n if (delta.text.length > 0) {\n orderAtoms.push({ kind: \"text\", text: delta.text });\n }\n }\n if (delta?.type === \"thinking_delta\" && typeof delta.thinking === \"string\") {\n reasoning += delta.thinking;\n }\n // Last-signature-wins: a turn with MULTIPLE thinking blocks overwrites\n // this on each block, so the merged `reasoning` ends up bound only to the\n // FINAL block's signature (mirrors collapseAnthropicSSE).\n if (delta?.type === \"signature_delta\" && typeof delta.signature === \"string\") {\n reasoningSignature = delta.signature;\n }\n if (delta?.type === \"input_json_delta\" && typeof delta.partial_json === \"string\") {\n const index = parsed.index as number | undefined;\n // An arg delta that cannot correlate to a known tool_use start would\n // otherwise silently lose its args. Account for it as a dropped chunk\n // instead of vanishing (mirrors the Cohere uncorrelated-delta path).\n const entry = index !== undefined ? toolCallMap.get(index) : undefined;\n if (entry) {\n entry.arguments += delta.partial_json;\n } else {\n droppedChunks++;\n if (droppedChunks === 1) {\n firstDroppedSample = `input_json_delta with no correlating tool_use start: ${surrogateSafeSlice(\n frameStr,\n 200,\n )}`;\n }\n }\n }\n continue;\n }\n if (parsed.type === \"content_block_start\") {\n const block = parsed.content_block as Record<string, unknown> | undefined;\n const index = parsed.index as number | undefined;\n // A `redacted_thinking` block carries its encrypted reasoning in an\n // opaque `data` string on the start event (no deltas follow). Capture it\n // so the recorded turn replays the redacted block faithfully\n // (mirrors collapseAnthropicSSE; see capturedRedactedData).\n const redactedData = capturedRedactedData(block);\n if (redactedData !== undefined) {\n redactedThinking.push(redactedData);\n }\n if (block?.type === \"tool_use\" && index !== undefined) {\n const created = {\n id: (block.id as string) ?? \"\",\n name: (block.name as string) ?? \"\",\n arguments: \"\",\n };\n toolCallMap.set(index, created);\n orderAtoms.push({ kind: \"toolCall\", ref: created });\n }\n continue;\n }\n\n // Converse format (converse-stream): camelCase wrapper keys\n // contentBlockStart — may initiate a tool_use block\n if (parsed.contentBlockStart) {\n const blockStart = parsed.contentBlockStart as Record<string, unknown>;\n const index = (parsed.contentBlockIndex ?? blockStart.contentBlockIndex) as\n | number\n | undefined;\n const start = blockStart.start as Record<string, unknown> | undefined;\n if (start?.toolUse && index !== undefined) {\n const toolUse = start.toolUse as Record<string, unknown>;\n const created = {\n id: (toolUse.toolUseId as string) ?? \"\",\n name: (toolUse.name as string) ?? \"\",\n arguments: \"\",\n };\n toolCallMap.set(index, created);\n orderAtoms.push({ kind: \"toolCall\", ref: created });\n }\n }\n\n // contentBlockDelta\n if (parsed.contentBlockDelta) {\n const blockDelta = parsed.contentBlockDelta as Record<string, unknown>;\n const index = (parsed.contentBlockIndex ?? blockDelta.contentBlockIndex) as\n | number\n | undefined;\n const delta = blockDelta.delta as Record<string, unknown> | undefined;\n if (!delta) continue;\n\n // Text delta\n if (typeof delta.text === \"string\") {\n content += delta.text;\n if (delta.text.length > 0) {\n orderAtoms.push({ kind: \"text\", text: delta.text });\n }\n }\n\n // Reasoning delta — Converse carries reasoning in `reasoningContent.text`.\n // The Converse branch intentionally captures no signature/redacted channel:\n // Converse has no `signature_delta`/`redacted_thinking` wire shape, so the\n // asymmetry with the Anthropic-native branch above is by design, not a gap\n // to be \"fixed\" later.\n if (typeof delta.reasoningContent === \"object\" && delta.reasoningContent !== null) {\n const reasoningDelta = delta.reasoningContent as Record<string, unknown>;\n if (typeof reasoningDelta.text === \"string\") {\n reasoning += reasoningDelta.text;\n }\n }\n\n // Tool use input JSON delta\n if (typeof delta.toolUse === \"object\" && delta.toolUse !== null) {\n const toolUseDelta = delta.toolUse as Record<string, unknown>;\n if (typeof toolUseDelta.input === \"string\") {\n // An arg delta that cannot correlate to a known tool_use start would\n // otherwise silently lose its args. Account for it as a dropped chunk\n // instead of vanishing (mirrors the Cohere uncorrelated-delta path).\n const entry = index !== undefined ? toolCallMap.get(index) : undefined;\n if (entry) {\n entry.arguments += toolUseDelta.input;\n } else {\n droppedChunks++;\n if (droppedChunks === 1) {\n firstDroppedSample = `toolUse.input delta with no correlating tool_use start: ${surrogateSafeSlice(\n frameStr,\n 200,\n )}`;\n }\n }\n }\n }\n }\n }\n\n if (toolCallMap.size > 0) {\n const orderedBlocks = buildOrderedBlocks(orderAtoms);\n // When interleaved, persist ordered `blocks` and derive the flat `toolCalls`\n // from the SAME tool atoms (frame-arrival order) so the two never disagree\n // (#274 F4/F5); otherwise keep the legacy index-sorted order so non-\n // interleaved fixtures stay byte-identical. Both paths normalize arguments\n // so a zero-arg tool_use persists \"{}\" rather than \"\".\n const orderedToolCalls = orderAtoms\n .filter(\n (a): a is { kind: \"toolCall\"; ref: { name: string; arguments: string; id?: string } } =>\n a.kind === \"toolCall\",\n )\n .map((a) => ({\n name: a.ref.name,\n arguments: normalizeToolArguments(a.ref.arguments),\n ...(a.ref.id ? { id: a.ref.id } : {}),\n }));\n const indexSortedToolCalls = Array.from(toolCallMap.entries())\n .sort(([a], [b]) => a - b)\n .map(([, tc]) => ({\n name: tc.name,\n arguments: normalizeToolArguments(tc.arguments),\n ...(tc.id ? { id: tc.id } : {}),\n }));\n return {\n ...(orderedBlocks ? { blocks: orderedBlocks } : {}),\n // A tool-bearing turn that ALSO streamed text previously DROPPED `content`\n // here. Spread it when present so the interleaved blocks are persistable\n // (the recorder only emits blocks when content+toolCalls coexist) and the\n // text is no longer silently lost (#274).\n ...(content ? { content } : {}),\n toolCalls: orderedBlocks ? orderedToolCalls : indexSortedToolCalls,\n ...(reasoning ? { reasoning } : {}),\n ...(reasoningSignature ? { reasoningSignature } : {}),\n ...(redactedThinking.length > 0 ? { redactedThinking } : {}),\n ...(droppedChunks > 0 ? { droppedChunks } : {}),\n ...(firstDroppedSample ? { firstDroppedSample } : {}),\n ...(truncated ? { truncated } : {}),\n };\n }\n\n return {\n content,\n ...(reasoning ? { reasoning } : {}),\n ...(reasoningSignature ? { reasoningSignature } : {}),\n ...(redactedThinking.length > 0 ? { redactedThinking } : {}),\n ...(droppedChunks > 0 ? { droppedChunks } : {}),\n ...(firstDroppedSample ? { firstDroppedSample } : {}),\n ...(truncated ? { truncated } : {}),\n };\n}\n\n// ---------------------------------------------------------------------------\n// 7. Gemini Interactions SSE\n// ---------------------------------------------------------------------------\n\n/**\n * Collapse Gemini Interactions SSE stream into a single response.\n *\n * Handles the SDK 2.x event protocol (the \"Interactions breaking changes,\n * May 2026\" shapes):\n * data: {\"event_type\":\"step.start\",\"index\":1,\"step\":{\"type\":\"function_call\",\"id\":\"call_1\",\"name\":\"fn\",\"arguments\":{}}}\n * data: {\"event_type\":\"step.delta\",\"index\":0,\"delta\":{\"type\":\"text\",\"text\":\"Hello\"}}\n * data: {\"event_type\":\"step.delta\",\"index\":1,\"delta\":{\"type\":\"arguments_delta\",\"arguments\":\"{\\\"x\\\":1}\"}}\n * data: {\"event_type\":\"interaction.completed\",\"interaction\":{\"id\":\"...\",\"usage\":{...}}}\n *\n * The legacy SDK 1.x shapes (`content.delta` with an inline `function_call`\n * delta) are still accepted for backward compatibility with previously\n * recorded fixtures.\n */\nexport function collapseGeminiInteractionsSSE(body: string): CollapseResult {\n const lines = splitSSEEvents(body);\n let content = \"\";\n let reasoning = \"\";\n let droppedChunks = 0;\n let firstDroppedSample: string | undefined;\n // Legacy 1.x tool calls arrive fully formed in a single content.delta.\n const toolCalls: ToolCall[] = [];\n // 2.x tool calls are assembled across step.start (identity) + arguments_delta\n // (string fragments), keyed by step index.\n const stepToolCalls = new Map<\n number,\n { id?: string; name: string; argsObj?: unknown; argsStr: string }\n >();\n // Synthetic keys for function_call step.start events that arrive without an\n // index (matches the sibling collapsers); seeded high to avoid colliding with\n // real step indices.\n let nextSyntheticStepIndex = 1_000_000;\n\n for (const line of lines) {\n const data = extractSSEData(splitSSELines(line));\n if (data === undefined) continue;\n\n const payload = data.trim();\n\n let parsed: Record<string, unknown>;\n try {\n parsed = JSON.parse(payload) as Record<string, unknown>;\n } catch (err) {\n droppedChunks++;\n if (droppedChunks === 1) {\n const msg = err instanceof Error ? err.message : \"unknown\";\n firstDroppedSample = `parse failed (${msg}): ${surrogateSafeSlice(payload, 200)}`;\n }\n continue;\n }\n\n const eventType = parsed.event_type as string | undefined;\n if (!eventType) continue;\n\n const index = typeof parsed.index === \"number\" ? parsed.index : undefined;\n\n if (eventType === \"step.start\") {\n // 2.x — tool-call identity lives on step.start, not in a delta.\n const step = parsed.step as Record<string, unknown> | undefined;\n if (step && step.type === \"function_call\") {\n // An index-less start can't correlate later arguments_delta fragments,\n // but minting a synthetic key preserves the call's identity instead of\n // dropping it silently.\n const key = index ?? nextSyntheticStepIndex++;\n stepToolCalls.set(key, {\n id: step.id ? String(step.id) : undefined,\n name: String(step.name ?? \"\"),\n // step.start may carry a fully-populated `arguments` object (non-\n // streamed calls) or an empty `{}` placeholder (streamed calls).\n argsObj: step.arguments,\n argsStr: \"\",\n });\n }\n } else if (eventType === \"step.delta\" || eventType === \"content.delta\") {\n const delta = parsed.delta as Record<string, unknown> | undefined;\n if (!delta) continue;\n\n if (delta.type === \"text\" && typeof delta.text === \"string\") {\n content += delta.text;\n } else if (delta.type === \"arguments_delta\") {\n // 2.x — argument fragment (a JSON string) keyed by step index.\n const entry = index !== undefined ? stepToolCalls.get(index) : undefined;\n if (entry) {\n if (typeof delta.arguments === \"string\") {\n entry.argsStr += delta.arguments;\n }\n } else {\n droppedChunks++;\n if (droppedChunks === 1) {\n firstDroppedSample = `arguments_delta with no correlating step.start: ${surrogateSafeSlice(\n payload,\n 200,\n )}`;\n }\n }\n } else if (delta.type === \"function_call\") {\n // Legacy 1.x — full tool call inline in a content.delta. Normalize the\n // string branch so a literal `arguments: \"\"` persists \"{}\" not \"\".\n toolCalls.push({\n name: String(delta.name ?? \"\"),\n arguments:\n typeof delta.arguments === \"string\"\n ? normalizeToolArguments(delta.arguments)\n : JSON.stringify(delta.arguments ?? {}),\n ...(delta.id ? { id: String(delta.id) } : {}),\n });\n } else if (delta.type === \"thought_summary\") {\n // 2.x nests the text under `content.text`; 1.x used a flat `text`.\n const summaryContent = delta.content as Record<string, unknown> | undefined;\n if (summaryContent && typeof summaryContent.text === \"string\") {\n reasoning += summaryContent.text;\n } else if (typeof delta.text === \"string\") {\n reasoning += delta.text;\n }\n }\n }\n }\n\n // BLOCK-CAPTURE DECISION (#274): this collapser is ARGS-ONLY — it intentionally\n // does NOT emit `blocks`. Unlike the delta-stream collapsers, the 2.x protocol\n // is step/index-addressed: tool identity arrives on `step.start` and its args\n // on indexed `arguments_delta` fragments, finalized here in SORTED step-index\n // order — interleaved with legacy 1.x calls that are arrival-pushed above. That\n // hybrid flat order cannot be reconciled with arrival-order OrderAtoms by\n // identity, so any emitted `blocks` would risk disagreeing with the flat\n // `toolCalls` (violating the #274 F4/F5 byte-consistency invariant). We\n // therefore only normalize arguments and leave `blocks` unset. Revisit only if\n // the finalizer is reworked to a single arrival-ordered pass.\n\n // Finalize 2.x tool calls in step-index order.\n for (const [, tc] of Array.from(stepToolCalls.entries()).sort(([a], [b]) => a - b)) {\n let args: string;\n if (tc.argsStr !== \"\") {\n args = tc.argsStr;\n // The arguments_delta fragments must concatenate into valid JSON by\n // step.stop. A truncated/interrupted stream can leave them malformed;\n // surface that via droppedChunks rather than writing a corrupt fixture\n // silently (mirrors the per-chunk parse guard above). On failure we MUST\n // NOT persist the invalid-JSON string — it would fail `validateFixtures`\n // on reload (the loader does `JSON.parse(tc.arguments)`). Fall back to\n // \"{}\" so the persisted `arguments` is always valid JSON, exactly like\n // the empty/unusable-args path below (mirrors the OpenAI/Anthropic\n // sibling collapsers, which never persist unparseable tool args).\n try {\n JSON.parse(args);\n } catch {\n droppedChunks++;\n if (droppedChunks === 1) {\n firstDroppedSample = `assembled arguments_delta not valid JSON for \"${tc.name}\": ${surrogateSafeSlice(\n args,\n 200,\n )}`;\n }\n args = \"{}\";\n }\n } else {\n args = typeof tc.argsObj === \"string\" ? tc.argsObj : JSON.stringify(tc.argsObj ?? {});\n }\n // Normalize so a step.start with no arguments / a whitespace-only argsStr\n // persists \"{}\" rather than \"\" (a valid-JSON non-empty argsStr is unchanged).\n toolCalls.push({\n name: tc.name,\n arguments: normalizeToolArguments(args),\n ...(tc.id ? { id: tc.id } : {}),\n });\n }\n\n if (toolCalls.length > 0) {\n return {\n ...(content ? { content } : {}),\n toolCalls,\n ...(reasoning ? { reasoning } : {}),\n ...(droppedChunks > 0 ? { droppedChunks } : {}),\n ...(firstDroppedSample ? { firstDroppedSample } : {}),\n };\n }\n\n return {\n content,\n ...(reasoning ? { reasoning } : {}),\n ...(droppedChunks > 0 ? { droppedChunks } : {}),\n ...(firstDroppedSample ? { firstDroppedSample } : {}),\n };\n}\n\n// ---------------------------------------------------------------------------\n// Dispatch helper — pick the right collapse function by provider\n// ---------------------------------------------------------------------------\n\n/**\n * Collapse a streaming response body into a non-streaming fixture response.\n * Returns null if the content type is not a known streaming format.\n * Falls back to OpenAI SSE parsing for unrecognized provider keys with text/event-stream.\n */\nexport function collapseStreamingResponse(\n contentType: string,\n providerKey: RecordProviderKey,\n body: string | Buffer,\n logger?: Logger,\n): CollapseResult | null {\n const ct = contentType.toLowerCase();\n\n if (ct.includes(\"application/vnd.amazon.eventstream\")) {\n const buf = typeof body === \"string\" ? Buffer.from(body, \"binary\") : body;\n return collapseBedrockEventStream(buf);\n }\n\n if (ct.includes(\"application/x-ndjson\")) {\n const str = typeof body === \"string\" ? body : body.toString(\"utf8\");\n return collapseOllamaNDJSON(str);\n }\n\n if (ct.includes(\"text/event-stream\")) {\n const str = typeof body === \"string\" ? body : body.toString(\"utf8\");\n switch (providerKey) {\n case \"openai\":\n case \"azure\":\n return collapseOpenAISSE(str);\n case \"anthropic\":\n return collapseAnthropicSSE(str);\n case \"gemini\":\n case \"vertexai\":\n return collapseGeminiSSE(str);\n case \"gemini-interactions\":\n return collapseGeminiInteractionsSSE(str);\n case \"cohere\":\n return collapseCohereSSE(str);\n case \"bedrock\":\n return collapseAnthropicSSE(str);\n default:\n logger?.warn(\n `[stream-collapse] unknown SSE provider \"${providerKey}\", falling back to OpenAI SSE format`,\n );\n return collapseOpenAISSE(str);\n }\n }\n\n return null;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAmGA,SAAS,uBAAuB,MAAkC;AAChE,KAAI,SAAS,UAAa,KAAK,MAAM,KAAK,GAAI,QAAO;AACrD,QAAO;;;;;;;AAQT,SAAS,gBAAgB,KAAqE;AAC5F,QAAO;EACL,MAAM;EACN,MAAM,IAAI;EACV,WAAW,uBAAuB,IAAI,UAAU;EAChD,GAAI,IAAI,KAAK,EAAE,IAAI,IAAI,IAAI,GAAG,EAAE;EACjC;;;;;;;;;;;;;;;;;;;;;AAsBH,SAAS,mBAAmB,OAAgD;CAC1E,IAAI,iBAAiB;CACrB,IAAI,iBAAiB;CACrB,IAAI,gBAAgB;CACpB,IAAI,UAAU;CACd,IAAI,UAAU;AACd,MAAK,IAAI,IAAI,GAAG,IAAI,MAAM,QAAQ,IAEhC,KADU,MAAM,GACV,SAAS,QAAQ;AACrB,YAAU;AACV,MAAI,mBAAmB,GAAI,kBAAiB;AAC5C,MAAI,QAAS,iBAAgB;QACxB;AACL,YAAU;AACV,MAAI,mBAAmB,GAAI,kBAAiB;;AAIhD,KAAI,CAAC,WAAW,CAAC,QAAS,QAAO;AAEjC,KAAI,EADmB,iBAAiB,mBACjB,CAAC,cAAe,QAAO;CAI9C,MAAM,SAAyB,EAAE;CACjC,IAAI,cAAc;CAClB,IAAI,iBAAiB;CACrB,MAAM,kBAAkB;AACtB,MAAI,gBAAgB;AAClB,UAAO,KAAK;IAAE,MAAM;IAAQ,MAAM;IAAa,CAAC;AAChD,iBAAc;AACd,oBAAiB;;;AAGrB,MAAK,MAAM,KAAK,MACd,KAAI,EAAE,SAAS,QAAQ;AACrB,iBAAe,EAAE;AACjB,mBAAiB;QACZ;AACL,aAAW;AACX,SAAO,KAAK,gBAAgB,EAAE,IAAI,CAAC;;AAGvC,YAAW;AACX,QAAO;;;;;;;;;;;AAYT,SAAgB,qBACd,OACoB;AACpB,KACE,OAAO,SAAS,uBAChB,OAAO,MAAM,SAAS,YACtB,MAAM,KAAK,SAAS,EAEpB,QAAO,MAAM;;;;;;;AAUjB,SAAS,mBAAmB,GAAW,KAAqB;CAC1D,IAAI,MAAM,EAAE,MAAM,GAAG,IAAI;AACzB,KAAI,IAAI,SAAS,GAAG;EAClB,MAAM,OAAO,IAAI,WAAW,IAAI,SAAS,EAAE;AAE3C,MAAI,QAAQ,SAAU,QAAQ,MAC5B,OAAM,IAAI,MAAM,GAAG,GAAG;;AAG1B,QAAO;;;;;;;;;;;AAYT,SAAS,eAAe,MAAwB;AAC9C,QAAO,KAAK,MAAM,aAAa,CAAC,QAAQ,UAAU,MAAM,MAAM,CAAC,SAAS,EAAE;;;;;;AAO5E,SAAS,cAAc,OAAyB;AAC9C,QAAO,MAAM,MAAM,KAAK,CAAC,KAAK,SAAU,KAAK,SAAS,KAAK,GAAG,KAAK,MAAM,GAAG,GAAG,GAAG,KAAM;;;;;;;;;;;;;AAc1F,SAAS,eAAe,OAAqC;CAC3D,MAAM,YAAsB,EAAE;AAC9B,MAAK,MAAM,QAAQ,OAAO;AACxB,MAAI,CAAC,KAAK,WAAW,QAAQ,CAAE;EAE/B,IAAI,OAAO,KAAK,MAAM,EAAE;AACxB,MAAI,KAAK,WAAW,IAAI,CAAE,QAAO,KAAK,MAAM,EAAE;AAC9C,YAAU,KAAK,KAAK;;AAEtB,KAAI,UAAU,WAAW,EAAG,QAAO;AACnC,QAAO,UAAU,KAAK,KAAK;;;;;;;;;AAc7B,SAAgB,kBAAkB,MAA8B;CAC9D,MAAM,QAAQ,eAAe,KAAK;CAClC,IAAI,UAAU;CACd,IAAI,YAAY;CAChB,MAAM,mBAA6B,EAAE;CACrC,IAAI,gBAAgB;CACpB,IAAI;CACJ,IAAI,kBAAkB;CACtB,IAAI;CACJ,MAAM,8BAAc,IAAI,KAA8D;CAQtF,IAAI,qBAAqB;CACzB,MAAM,2BAAW,IAAI,KAAqB;CAI1C,MAAM,aAA0B,EAAE;AAElC,MAAK,MAAM,QAAQ,OAAO;EACxB,MAAM,OAAO,eAAe,cAAc,KAAK,CAAC;AAChD,MAAI,SAAS,OAAW;EAExB,MAAM,UAAU,KAAK,MAAM;AAC3B,MAAI,YAAY,SAAU;EAE1B,IAAI;AACJ,MAAI;AACF,YAAS,KAAK,MAAM,QAAQ;WACrB,KAAK;AACZ;AACA,OAAI,kBAAkB,EAEpB,sBAAqB,iBADT,eAAe,QAAQ,IAAI,UAAU,UACP,KAAK,mBAAmB,SAAS,IAAI;AAEjF;;AAIF,MACE,OAAO,SAAS,2CAChB,OAAO,OAAO,UAAU,UACxB;AACA,gBAAa,OAAO;AACpB;;AAIF,MAAI,OAAO,SAAS,6BAA6B;GAC/C,MAAM,OAAO,OAAO;AACpB,OAAI,MAAM,SAAS,mBAAmB;IACpC,MAAM,SAAS,KAAK;AACpB,QAAI,UAAU,OAAO,OAAO,UAAU,UAAU;AAC9C,sBAAiB,KAAK,OAAO,MAAM;AACnC;;;;AAMN,MAAI,OAAO,SAAS,gCAAgC,OAAO,OAAO,UAAU,UAAU;AACpF,cAAW,OAAO;AAClB;;AAIF,MAAI,OAAO,OAAO,SAAS,YAAY,OAAO,KAAK,WAAW,YAAY,CACxE;EAGF,MAAM,UAAU,OAAO;AACvB,MAAI,CAAC,WAAW,QAAQ,WAAW,EAAG;EAEtC,MAAM,QAAQ,QAAQ,GAAG;AACzB,MAAI,CAAC,MAAO;AAGZ,MAAI,OAAO,MAAM,sBAAsB,SACrC,cAAa,MAAM;AAIrB,MAAI,OAAO,MAAM,YAAY,UAAU;AACrC,cAAW,MAAM;AACjB,OAAI,MAAM,QAAQ,SAAS,EACzB,YAAW,KAAK;IAAE,MAAM;IAAQ,MAAM,MAAM;IAAS,CAAC;;EAK1D,MAAM,YAAY,MAAM;AACxB,MAAI,UACF,MAAK,MAAM,MAAM,WAAW;GAC1B,MAAM,KAAK,GAAG;GACd,MAAM,QAAQ,OAAO,GAAG,OAAO,WAAW,GAAG,KAAK;GAKlD,IAAI;AACJ,OAAI,OAAO,GAAG,UAAU,SACtB,SAAQ,GAAG;YACF,UAAU,QAAW;IAC9B,MAAM,WAAW,SAAS,IAAI,MAAM;AACpC,QAAI,aAAa,OACf,SAAQ;SACH;AACL,aAAQ;AACR,cAAS,IAAI,OAAO,MAAM;;SAG5B,SAAQ;AAGV,OAAI,CAAC,YAAY,IAAI,MAAM,EAAE;IAC3B,MAAM,UAAU;KACd,IAAI,SAAS;KACb,MAAO,IAAI,QAAmB;KAC9B,WAAW;KACZ;AACD,gBAAY,IAAI,OAAO,QAAQ;AAG/B,eAAW,KAAK;KAAE,MAAM;KAAY,KAAK;KAAS,CAAC;;GAGrD,MAAM,QAAQ,YAAY,IAAI,MAAM;AACpC,OAAI,IAAI,QAAQ,OAAO,GAAG,SAAS,YAAY,CAAC,MAAM,KACpD,OAAM,OAAO,GAAG;AAElB,OAAI,GAAG,MAAM,OAAO,GAAG,OAAO,YAAY,CAAC,MAAM,GAC/C,OAAM,KAAK,GAAG;AAEhB,OAAI,IAAI,aAAa,OAAO,GAAG,cAAc,SAC3C,OAAM,aAAa,GAAG;;;CAe9B,MAAM,mBAA+B,EAAE;AACvC,KAAI,YAAY,SAAS,KAAKA,iCAAiB,QAAQ,EAAE;EACvD,MAAM,SAASC,oCAAoB,QAAQ;AAC3C,MAAI,OAAO,QAAQ;AACjB,qBAAkB;AAClB,iBAAc,uEAAuE,mBAAmB,SAAS,IAAI;SAChH;AACL,aAAU,OAAO;AACjB,OAAI,OAAO,UACT,cAAa,OAAO;AAEtB,oBAAiB,KAAK,GAAG,OAAO,UAAU;;;AAI9C,KAAI,YAAY,OAAO,KAAK,iBAAiB,SAAS,GAAG;EACvD,MAAM,SAAS,mBAAmB,WAAW;EAO7C,MAAM,mBAAmB,WACtB,QACE,MACC,EAAE,SAAS,WACd,CACA,KAAK,OAAO;GACX,MAAM,EAAE,IAAI;GACZ,WAAW,uBAAuB,EAAE,IAAI,UAAU;GAClD,GAAI,EAAE,IAAI,KAAK,EAAE,IAAI,EAAE,IAAI,IAAI,GAAG,EAAE;GACrC,EAAE;EACL,MAAM,uBAAuB,MAAM,KAAK,YAAY,SAAS,CAAC,CAC3D,MAAM,CAAC,IAAI,CAAC,OAAO,IAAI,EAAE,CACzB,KAAK,GAAG,SAAS;GAChB,MAAM,GAAG;GACT,WAAW,uBAAuB,GAAG,UAAU;GAC/C,GAAI,GAAG,KAAK,EAAE,IAAI,GAAG,IAAI,GAAG,EAAE;GAC/B,EAAE;AACL,SAAO;GACL,GAAI,SAAS,EAAE,QAAQ,GAAG,EAAE;GAC5B,GAAI,UAAU,EAAE,SAAS,GAAG,EAAE;GAG9B,WAAW,CAAC,GAAI,SAAS,mBAAmB,sBAAuB,GAAG,iBAAiB;GAIvF,GAAI,YAAY,EAAE,WAAW,GAAG,EAAE;GAElC,GAAI,iBAAiB,SAAS,IAAI,EAAE,aAAa,kBAAkB,GAAG,EAAE;GACxE,GAAI,gBAAgB,IAAI,EAAE,eAAe,GAAG,EAAE;GAC9C,GAAI,qBAAqB,EAAE,oBAAoB,GAAG,EAAE;GACpD,GAAI,kBAAkB,EAAE,iBAAiB,MAAM,GAAG,EAAE;GACpD,GAAI,cAAc,EAAE,aAAa,GAAG,EAAE;GACvC;;AAGH,QAAO;EACL;EACA,GAAI,YAAY,EAAE,WAAW,GAAG,EAAE;EAClC,GAAI,iBAAiB,SAAS,IAAI,EAAE,aAAa,kBAAkB,GAAG,EAAE;EACxE,GAAI,gBAAgB,IAAI,EAAE,eAAe,GAAG,EAAE;EAC9C,GAAI,qBAAqB,EAAE,oBAAoB,GAAG,EAAE;EACpD,GAAI,kBAAkB,EAAE,iBAAiB,MAAM,GAAG,EAAE;EACpD,GAAI,cAAc,EAAE,aAAa,GAAG,EAAE;EACvC;;;;;;;;;AAcH,SAAgB,qBAAqB,MAA8B;CACjE,MAAM,SAAS,eAAe,KAAK;CACnC,IAAI,UAAU;CACd,IAAI,YAAY;CAKhB,IAAI;CAIJ,MAAM,mBAA6B,EAAE;CACrC,IAAI,gBAAgB;CACpB,IAAI;CACJ,MAAM,8BAAc,IAAI,KAA8D;CAYtF,IAAI,qBAAqB;CACzB,IAAI;CAEJ,MAAM,aAA0B,EAAE;AAElC,MAAK,MAAM,SAAS,QAAQ;EAC1B,MAAM,QAAQ,cAAc,MAAM;EAClC,MAAM,YAAY,MAAM,MAAM,MAAM,EAAE,WAAW,SAAS,CAAC;EAC3D,MAAM,OAAO,eAAe,MAAM;AAClC,MAAI,SAAS,OAAW;EAExB,MAAM,YAAY,YAAY,UAAU,MAAM,EAAE,CAAC,MAAM,GAAG;EAC1D,MAAM,UAAU,KAAK,MAAM;EAE3B,IAAI;AACJ,MAAI;AACF,YAAS,KAAK,MAAM,QAAQ;WACrB,KAAK;AACZ;AACA,OAAI,kBAAkB,EAEpB,sBAAqB,iBADT,eAAe,QAAQ,IAAI,UAAU,UACP,KAAK,mBAAmB,SAAS,IAAI;AAEjF;;AAGF,MAAI,cAAc,uBAAuB;GACvC,MAAM,eAAe,OAAO;GAK5B,MAAM,eAAe,qBAAqB,aAAa;AACvD,OAAI,iBAAiB,OACnB,kBAAiB,KAAK,aAAa;AAErC,OAAI,cAAc,SAAS,YAAY;IAGrC,IAAI;AACJ,QAAI,OAAO,OAAO,UAAU,SAC1B,SAAQ,OAAO;QAEf,SAAQ;AAEV,yBAAqB;IACrB,MAAM,UAAU;KACd,IAAK,aAAa,MAAiB;KACnC,MAAO,aAAa,QAAmB;KACvC,WAAW;KACZ;AACD,gBAAY,IAAI,OAAO,QAAQ;AAG/B,eAAW,KAAK;KAAE,MAAM;KAAY,KAAK;KAAS,CAAC;;;AAIvD,MAAI,cAAc,uBAAuB;GACvC,MAAM,QAAQ,OAAO;AACrB,OAAI,CAAC,MAAO;AAEZ,OAAI,MAAM,SAAS,gBAAgB,OAAO,MAAM,SAAS,UAAU;AACjE,eAAW,MAAM;AACjB,QAAI,MAAM,KAAK,SAAS,EACtB,YAAW,KAAK;KAAE,MAAM;KAAQ,MAAM,MAAM;KAAM,CAAC;;AAIvD,OAAI,MAAM,SAAS,oBAAoB,OAAO,MAAM,aAAa,SAC/D,cAAa,MAAM;AASrB,OAAI,MAAM,SAAS,qBAAqB,OAAO,MAAM,cAAc,SACjE,sBAAqB,MAAM;AAG7B,OAAI,MAAM,SAAS,sBAAsB,OAAO,MAAM,iBAAiB,UAAU;IAG/E,MAAM,QAAQ,OAAO,OAAO,UAAU,WAAW,OAAO,QAAQ;IAKhE,MAAM,QAAQ,UAAU,SAAY,YAAY,IAAI,MAAM,GAAG;AAC7D,QAAI,MACF,OAAM,aAAa,MAAM;SACpB;AACL;AACA,SAAI,kBAAkB,EACpB,sBAAqB,wDAAwD,mBAC3E,SACA,IACD;;;;;AAOX,KAAI,YAAY,OAAO,GAAG;EACxB,MAAM,gBAAgB,mBAAmB,WAAW;EAMpD,MAAM,mBAAmB,WACtB,QACE,MACC,EAAE,SAAS,WACd,CACA,KAAK,OAAO;GACX,MAAM,EAAE,IAAI;GACZ,WAAW,uBAAuB,EAAE,IAAI,UAAU;GAClD,GAAI,EAAE,IAAI,KAAK,EAAE,IAAI,EAAE,IAAI,IAAI,GAAG,EAAE;GACrC,EAAE;EACL,MAAM,uBAAuB,MAAM,KAAK,YAAY,SAAS,CAAC,CAC3D,MAAM,CAAC,IAAI,CAAC,OAAO,IAAI,EAAE,CACzB,KAAK,GAAG,SAAS;GAChB,MAAM,GAAG;GACT,WAAW,uBAAuB,GAAG,UAAU;GAC/C,GAAI,GAAG,KAAK,EAAE,IAAI,GAAG,IAAI,GAAG,EAAE;GAC/B,EAAE;AACL,SAAO;GACL,GAAI,gBAAgB,EAAE,QAAQ,eAAe,GAAG,EAAE;GAClD,GAAI,UAAU,EAAE,SAAS,GAAG,EAAE;GAC9B,WAAW,gBAAgB,mBAAmB;GAC9C,GAAI,YAAY,EAAE,WAAW,GAAG,EAAE;GAClC,GAAI,qBAAqB,EAAE,oBAAoB,GAAG,EAAE;GACpD,GAAI,iBAAiB,SAAS,IAAI,EAAE,kBAAkB,GAAG,EAAE;GAC3D,GAAI,gBAAgB,IAAI,EAAE,eAAe,GAAG,EAAE;GAC9C,GAAI,qBAAqB,EAAE,oBAAoB,GAAG,EAAE;GACrD;;AAGH,QAAO;EACL;EACA,GAAI,YAAY,EAAE,WAAW,GAAG,EAAE;EAClC,GAAI,qBAAqB,EAAE,oBAAoB,GAAG,EAAE;EACpD,GAAI,iBAAiB,SAAS,IAAI,EAAE,kBAAkB,GAAG,EAAE;EAC3D,GAAI,gBAAgB,IAAI,EAAE,eAAe,GAAG,EAAE;EAC9C,GAAI,qBAAqB,EAAE,oBAAoB,GAAG,EAAE;EACrD;;;;;;;;AAaH,SAAgB,kBAAkB,MAA8B;CAC9D,MAAM,QAAQ,eAAe,KAAK;CAClC,IAAI,UAAU;CACd,IAAI,YAAY;CAChB,IAAI,gBAAgB;CACpB,IAAI;CACJ,IAAI,WAAW;CACf,IAAI;CACJ,MAAM,YAAwB,EAAE;CAEhC,MAAM,aAA0B,EAAE;AAElC,MAAK,MAAM,QAAQ,OAAO;EACxB,MAAM,OAAO,eAAe,cAAc,KAAK,CAAC;AAChD,MAAI,SAAS,OAAW;EAExB,MAAM,UAAU,KAAK,MAAM;EAE3B,IAAI;AACJ,MAAI;AACF,YAAS,KAAK,MAAM,QAAQ;WACrB,KAAK;AACZ;AACA,OAAI,kBAAkB,EAEpB,sBAAqB,iBADT,eAAe,QAAQ,IAAI,UAAU,UACP,KAAK,mBAAmB,SAAS,IAAI;AAEjF;;EAGF,MAAM,aAAa,OAAO;AAC1B,MAAI,CAAC,cAAc,WAAW,WAAW,EAAG;EAE5C,MAAM,mBAAmB,WAAW,GAAG;AACvC,MAAI,CAAC,iBAAkB;EAEvB,MAAM,QAAQ,iBAAiB;AAC/B,MAAI,CAAC,SAAS,MAAM,WAAW,EAAG;AAElC,OAAK,MAAM,QAAQ,MACjB,KAAI,KAAK,cAAc;GACrB,MAAM,KAAK,KAAK;GAChB,MAAM,UAAoB;IACxB,MAAM,OAAO,GAAG,QAAQ,GAAG;IAK3B,WACE,OAAO,GAAG,SAAS,WAAY,GAAG,OAAkB,KAAK,UAAU,GAAG,QAAQ,EAAE,CAAC;IACpF;AACD,aAAU,KAAK,QAAQ;AAEvB,cAAW,KAAK;IAAE,MAAM;IAAY,KAAK;IAAS,CAAC;aAEnD,KAAK,cACL,OAAQ,KAAK,WAAuC,aAAa,YAC/D,KAAK,WAAuC,SAAoB,WAAW,SAAS,EACtF;GACA,MAAM,aAAa,KAAK;AACxB,OAAI,CAAC,cACH,iBAAgB,WAAW;AAE7B,OAAI,OAAO,WAAW,SAAS,SAC7B,aAAY,WAAW;aAEhB,OAAO,KAAK,SAAS,SAC9B,KAAI,KAAK,QACP,cAAa,KAAK;OACb;AACL,cAAW,KAAK;AAChB,OAAI,KAAK,KAAK,SAAS,EACrB,YAAW,KAAK;IAAE,MAAM;IAAQ,MAAM,KAAK;IAAM,CAAC;;;CAW5D,MAAM,sBAAsB,UAAU,KAAK,QAAQ;EACjD,GAAG;EACH,WAAW,uBAAuB,GAAG,UAAU;EAChD,EAAE;AAEH,KAAI,SAWF,QAAO;EACL;EACA;EACA,GAAI,UAAU,EAAE,SAAS,GAAG,EAAE;EAC9B,GAAI,YAAY,EAAE,WAAW,GAAG,EAAE;EAClC,GAAI,oBAAoB,SAAS,IAAI,EAAE,WAAW,qBAAqB,GAAG,EAAE;EAC5E,GAAI,gBAAgB,IAAI,EAAE,eAAe,GAAG,EAAE;EAC9C,GAAI,qBAAqB,EAAE,oBAAoB,GAAG,EAAE;EACrD;AAGH,KAAI,UAAU,SAAS,GAAG;EACxB,MAAM,SAAS,mBAAmB,WAAW;AAC7C,SAAO;GACL,GAAI,SAAS,EAAE,QAAQ,GAAG,EAAE;GAC5B,GAAI,UAAU,EAAE,SAAS,GAAG,EAAE;GAC9B,WAAW;GACX,GAAI,YAAY,EAAE,WAAW,GAAG,EAAE;GAClC,GAAI,gBAAgB,IAAI,EAAE,eAAe,GAAG,EAAE;GAC9C,GAAI,qBAAqB,EAAE,oBAAoB,GAAG,EAAE;GACrD;;AAGH,QAAO;EACL;EACA,GAAI,YAAY,EAAE,WAAW,GAAG,EAAE;EAClC,GAAI,gBAAgB,IAAI,EAAE,eAAe,GAAG,EAAE;EAC9C,GAAI,qBAAqB,EAAE,oBAAoB,GAAG,EAAE;EACrD;;;;;;;;;;;;;;;;AAqBH,SAAgB,qBAAqB,MAA8B;CACjE,MAAM,QAAQ,KAAK,MAAM,KAAK,CAAC,QAAQ,MAAM,EAAE,MAAM,CAAC,SAAS,EAAE;CACjE,IAAI,UAAU;CACd,IAAI,YAAY;CAChB,IAAI,gBAAgB;CACpB,IAAI;CACJ,IAAI,kBAAkB;CACtB,IAAI;CACJ,MAAM,YAAwB,EAAE;CAEhC,MAAM,aAA0B,EAAE;AAElC,MAAK,MAAM,QAAQ,OAAO;EACxB,IAAI;AACJ,MAAI;AACF,YAAS,KAAK,MAAM,KAAK,MAAM,CAAC;WACzB,KAAK;AACZ;AACA,OAAI,kBAAkB,EAEpB,sBAAqB,iBADT,eAAe,QAAQ,IAAI,UAAU,UACP,KAAK,mBAAmB,KAAK,MAAM,EAAE,IAAI;AAErF;;EAIF,MAAM,UAAU,OAAO;AACvB,MAAI,SAAS;AACX,OAAI,OAAO,QAAQ,YAAY,UAAU;AACvC,eAAW,QAAQ;AACnB,QAAI,QAAQ,QAAQ,SAAS,EAC3B,YAAW,KAAK;KAAE,MAAM;KAAQ,MAAM,QAAQ;KAAS,CAAC;;AAK5D,OAAI,MAAM,QAAQ,QAAQ,WAAW,CACnC,MAAK,MAAM,MAAM,QAAQ,YAA8C;IACrE,MAAM,KAAK,GAAG;AACd,QAAI,IAAI;KACN,MAAM,UAAoB;MACxB,MAAM,OAAO,GAAG,QAAQ,GAAG;MAI3B,WACE,OAAO,GAAG,cAAc,WACpB,GAAG,YACH,KAAK,UAAU,GAAG,aAAa,EAAE,CAAC;MACzC;AACD,eAAU,KAAK,QAAQ;AACvB,gBAAW,KAAK;MAAE,MAAM;MAAY,KAAK;MAAS,CAAC;;;aAOlD,OAAO,OAAO,aAAa,UAAU;AAC5C,cAAW,OAAO;AAClB,OAAI,OAAO,SAAS,SAAS,EAC3B,YAAW,KAAK;IAAE,MAAM;IAAQ,MAAM,OAAO;IAAU,CAAC;;;AAY9D,KAAI,UAAU,WAAW,KAAKD,iCAAiB,QAAQ,EAAE;EACvD,MAAM,gBAAgBC,oCAAoB,QAAQ;AAClD,MAAI,cAAc,QAAQ;AACxB,qBAAkB;AAClB,iBAAc,uEAAuE,mBAAmB,SAAS,IAAI;SAChH;AACL,aAAU,cAAc;AACxB,OAAI,cAAc,UAChB,cAAa,cAAc;AAE7B,aAAU,KAAK,GAAG,cAAc,UAAU;;;AAI9C,KAAI,UAAU,SAAS,GAAG;EACxB,MAAM,SAAS,mBAAmB,WAAW;EAG7C,MAAM,sBAAsB,UAAU,KAAK,QAAQ;GACjD,GAAG;GACH,WAAW,uBAAuB,GAAG,UAAU;GAChD,EAAE;AACH,SAAO;GACL,GAAI,SAAS,EAAE,QAAQ,GAAG,EAAE;GAC5B,GAAI,UAAU,EAAE,SAAS,GAAG,EAAE;GAC9B,WAAW;GACX,GAAI,YAAY,EAAE,WAAW,GAAG,EAAE;GAClC,GAAI,gBAAgB,IAAI,EAAE,eAAe,GAAG,EAAE;GAC9C,GAAI,qBAAqB,EAAE,oBAAoB,GAAG,EAAE;GACpD,GAAI,kBAAkB,EAAE,iBAAiB,MAAM,GAAG,EAAE;GACpD,GAAI,cAAc,EAAE,aAAa,GAAG,EAAE;GACvC;;AAGH,QAAO;EACL;EACA,GAAI,YAAY,EAAE,WAAW,GAAG,EAAE;EAClC,GAAI,gBAAgB,IAAI,EAAE,eAAe,GAAG,EAAE;EAC9C,GAAI,qBAAqB,EAAE,oBAAoB,GAAG,EAAE;EACpD,GAAI,kBAAkB,EAAE,iBAAiB,MAAM,GAAG,EAAE;EACpD,GAAI,cAAc,EAAE,aAAa,GAAG,EAAE;EACvC;;;;;;;;AAaH,SAAgB,kBAAkB,MAA8B;CAC9D,MAAM,SAAS,eAAe,KAAK;CACnC,IAAI,UAAU;CAKd,IAAI,YAAY;CAChB,IAAI,gBAAgB;CACpB,IAAI;CACJ,MAAM,8BAAc,IAAI,KAA8D;CAStF,IAAI,qBAAqB;CACzB,IAAI;CAKJ,MAAM,aAA0B,EAAE;AAElC,MAAK,MAAM,SAAS,QAAQ;EAC1B,MAAM,QAAQ,cAAc,MAAM;EAClC,MAAM,YAAY,MAAM,MAAM,MAAM,EAAE,WAAW,SAAS,CAAC;EAC3D,MAAM,OAAO,eAAe,MAAM;AAClC,MAAI,SAAS,OAAW;EAExB,MAAM,YAAY,YAAY,UAAU,MAAM,EAAE,CAAC,MAAM,GAAG;EAC1D,MAAM,UAAU,KAAK,MAAM;EAE3B,IAAI;AACJ,MAAI;AACF,YAAS,KAAK,MAAM,QAAQ;WACrB,KAAK;AACZ;AACA,OAAI,kBAAkB,EAEpB,sBAAqB,iBADT,eAAe,QAAQ,IAAI,UAAU,UACP,KAAK,mBAAmB,SAAS,IAAI;AAEjF;;AAGF,MAAI,cAAc,iBAAiB;GAGjC,MAAM,cAFQ,OAAO,OACE,UACK;AAC5B,OAAI,cAAc,WAAW,SAAS,cAAc,OAAO,WAAW,aAAa,SACjF,cAAa,WAAW;YACf,cAAc,OAAO,WAAW,SAAS,UAAU;AAC5D,eAAW,WAAW;AAGtB,QAAI,WAAW,KAAK,SAAS,EAC3B,YAAW,KAAK;KAAE,MAAM;KAAQ,MAAM,WAAW;KAAM,CAAC;;;AAK9D,MAAI,cAAc,mBAAmB;GACnC,IAAI;AACJ,OAAI,OAAO,OAAO,UAAU,SAC1B,SAAQ,OAAO;OAEf,SAAQ;AAIV,kBAAe;GAGf,MAAM,aAFQ,OAAO,OACE,UACI;AAC3B,OAAI,WAAW;IACb,MAAM,KAAK,UAAU;IACrB,MAAM,UAAU;KACd,IAAK,UAAU,MAAiB;KAChC,MAAO,IAAI,QAAmB;KAC9B,WAAW;KACZ;AACD,gBAAY,IAAI,OAAO,QAAQ;AAG/B,eAAW,KAAK;KAAE,MAAM;KAAY,KAAK;KAAS,CAAC;;;AAIvD,MAAI,cAAc,mBAAmB;GAGnC,MAAM,QAAQ,OAAO,OAAO,UAAU,WAAW,OAAO,QAAQ;GAGhE,MAAM,aAFQ,OAAO,OACE,UACI;AAC3B,OAAI,WAAW;IACb,MAAM,KAAK,UAAU;AACrB,QAAI,MAAM,OAAO,GAAG,cAAc,UAAU;KAI1C,MAAM,QAAQ,UAAU,SAAY,YAAY,IAAI,MAAM,GAAG;AAC7D,SAAI,MACF,OAAM,aAAa,GAAG;UACjB;AACL;AACA,UAAI,kBAAkB,EACpB,sBAAqB,8CAA8C,mBACjE,SACA,IACD;;;;;;AAQb,KAAI,YAAY,OAAO,GAAG;EACxB,MAAM,gBAAgB,mBAAmB,WAAW;EAMpD,MAAM,mBAAmB,WACtB,QACE,MACC,EAAE,SAAS,WACd,CACA,KAAK,OAAO;GACX,MAAM,EAAE,IAAI;GACZ,WAAW,uBAAuB,EAAE,IAAI,UAAU;GAClD,GAAI,EAAE,IAAI,KAAK,EAAE,IAAI,EAAE,IAAI,IAAI,GAAG,EAAE;GACrC,EAAE;EACL,MAAM,uBAAuB,MAAM,KAAK,YAAY,SAAS,CAAC,CAC3D,MAAM,CAAC,IAAI,CAAC,OAAO,IAAI,EAAE,CACzB,KAAK,GAAG,SAAS;GAChB,MAAM,GAAG;GACT,WAAW,uBAAuB,GAAG,UAAU;GAC/C,GAAI,GAAG,KAAK,EAAE,IAAI,GAAG,IAAI,GAAG,EAAE;GAC/B,EAAE;AACL,SAAO;GACL,GAAI,gBAAgB,EAAE,QAAQ,eAAe,GAAG,EAAE;GAClD,GAAI,UAAU,EAAE,SAAS,GAAG,EAAE;GAC9B,WAAW,gBAAgB,mBAAmB;GAC9C,GAAI,YAAY,EAAE,WAAW,GAAG,EAAE;GAClC,GAAI,gBAAgB,IAAI,EAAE,eAAe,GAAG,EAAE;GAC9C,GAAI,qBAAqB,EAAE,oBAAoB,GAAG,EAAE;GACrD;;AAGH,QAAO;EACL;EACA,GAAI,YAAY,EAAE,WAAW,GAAG,EAAE;EAClC,GAAI,gBAAgB,IAAI,EAAE,eAAe,GAAG,EAAE;EAC9C,GAAI,qBAAqB,EAAE,oBAAoB,GAAG,EAAE;EACrD;;;;;;;;;;;;;AAkBH,SAAS,wBAAwB,KAG/B;CACA,MAAM,SAAsE,EAAE;CAC9E,IAAI,SAAS;AAEb,QAAO,SAAS,IAAI,QAAQ;AAC1B,MAAI,SAAS,KAAK,IAAI,OAAQ;EAE9B,MAAM,cAAc,IAAI,aAAa,OAAO;EAC5C,MAAM,gBAAgB,IAAI,aAAa,SAAS,EAAE;AAGlD,MAAI,cAAc,MAAM,SAAS,cAAc,IAAI,OACjD,QAAO;GAAE;GAAQ,WAAW;GAAM;EAIpC,MAAM,aAAa,IAAI,aAAa,SAAS,EAAE;EAC/C,MAAM,0CAA2B,IAAI,SAAS,QAAQ,SAAS,EAAE,CAAC;AAClE,MAAI,eAAe,MAAM,uBAAuB,EAC9C,QAAO;GAAE;GAAQ,WAAW;GAAM;EAIpC,MAAM,eAAe,SAAS;EAC9B,MAAM,aAAa,eAAe;EAClC,MAAM,aAAa,SAAS,cAAc;AAO1C,MAAI,aAAa,cAAc,aAAa,IAAI,OAC9C,QAAO;GAAE;GAAQ,WAAW;GAAM;EAGpC,MAAM,UAAkC,EAAE;EAC1C,IAAI,UAAU;EACd,IAAI,gBAAgB;AAEpB,SAAO,UAAU,YAAY;AAG3B,OAAI,UAAU,IAAI,YAAY;AAC5B,oBAAgB;AAChB;;GAEF,MAAM,UAAU,IAAI,UAAU,QAAQ;AACtC,cAAW;AACX,OAAI,UAAU,UAAU,IAAI,IAAI,YAAY;AAC1C,oBAAgB;AAChB;;GAEF,MAAM,OAAO,IAAI,SAAS,SAAS,UAAU,QAAQ,CAAC,SAAS,OAAO;AACtE,cAAW;AAEX,cAAW;GACX,MAAM,WAAW,IAAI,aAAa,QAAQ;AAC1C,cAAW;AACX,OAAI,UAAU,WAAW,YAAY;AACnC,oBAAgB;AAChB;;GAEF,MAAM,QAAQ,IAAI,SAAS,SAAS,UAAU,SAAS,CAAC,SAAS,OAAO;AACxE,cAAW;AACX,WAAQ,QAAQ;;AAGlB,MAAI,cACF,QAAO;GAAE;GAAQ,WAAW;GAAM;EAIpC,MAAM,eAAe;EACrB,MAAM,UAAU,IAAI,SAAS,cAAc,WAAW;EAGtD,MAAM,aAAa,IAAI,aAAa,SAAS,cAAc,EAAE;EAC7D,MAAM,0CAA2B,IAAI,SAAS,QAAQ,SAAS,cAAc,EAAE,CAAC;AAChF,MAAI,eAAe,MAAM,uBAAuB,EAC9C,QAAO;GAAE;GAAQ,WAAW;GAAM;AAGpC,SAAO,KAAK;GAAE;GAAS;GAAS,CAAC;AACjC,YAAU;;AAGZ,QAAO;EAAE;EAAQ,WAAW;EAAO;;;;;;;;AASrC,SAAgB,2BAA2B,MAA8B;CACvE,MAAM,EAAE,QAAQ,cAAc,wBAAwB,KAAK;CAC3D,IAAI,UAAU;CAKd,IAAI,YAAY;CAQhB,IAAI;CACJ,MAAM,mBAA6B,EAAE;CACrC,IAAI,gBAAgB;CACpB,IAAI;CACJ,MAAM,8BAAc,IAAI,KAA8D;CAKtF,MAAM,aAA0B,EAAE;AAElC,MAAK,MAAM,SAAS,QAAQ;EAC1B,MAAM,WAAW,MAAM,QAAQ,SAAS,OAAO;EAC/C,IAAI;AACJ,MAAI;AACF,YAAS,KAAK,MAAM,SAAS;WACtB,KAAK;AACZ;AACA,OAAI,kBAAkB,EAEpB,sBAAqB,iBADT,eAAe,QAAQ,IAAI,UAAU,UACP,KAAK,mBAAmB,UAAU,IAAI;AAElF;;AAIF,MAAI,OAAO,SAAS,uBAAuB;GACzC,MAAM,QAAQ,OAAO;AACrB,OAAI,OAAO,SAAS,gBAAgB,OAAO,MAAM,SAAS,UAAU;AAClE,eAAW,MAAM;AACjB,QAAI,MAAM,KAAK,SAAS,EACtB,YAAW,KAAK;KAAE,MAAM;KAAQ,MAAM,MAAM;KAAM,CAAC;;AAGvD,OAAI,OAAO,SAAS,oBAAoB,OAAO,MAAM,aAAa,SAChE,cAAa,MAAM;AAKrB,OAAI,OAAO,SAAS,qBAAqB,OAAO,MAAM,cAAc,SAClE,sBAAqB,MAAM;AAE7B,OAAI,OAAO,SAAS,sBAAsB,OAAO,MAAM,iBAAiB,UAAU;IAChF,MAAM,QAAQ,OAAO;IAIrB,MAAM,QAAQ,UAAU,SAAY,YAAY,IAAI,MAAM,GAAG;AAC7D,QAAI,MACF,OAAM,aAAa,MAAM;SACpB;AACL;AACA,SAAI,kBAAkB,EACpB,sBAAqB,wDAAwD,mBAC3E,UACA,IACD;;;AAIP;;AAEF,MAAI,OAAO,SAAS,uBAAuB;GACzC,MAAM,QAAQ,OAAO;GACrB,MAAM,QAAQ,OAAO;GAKrB,MAAM,eAAe,qBAAqB,MAAM;AAChD,OAAI,iBAAiB,OACnB,kBAAiB,KAAK,aAAa;AAErC,OAAI,OAAO,SAAS,cAAc,UAAU,QAAW;IACrD,MAAM,UAAU;KACd,IAAK,MAAM,MAAiB;KAC5B,MAAO,MAAM,QAAmB;KAChC,WAAW;KACZ;AACD,gBAAY,IAAI,OAAO,QAAQ;AAC/B,eAAW,KAAK;KAAE,MAAM;KAAY,KAAK;KAAS,CAAC;;AAErD;;AAKF,MAAI,OAAO,mBAAmB;GAC5B,MAAM,aAAa,OAAO;GAC1B,MAAM,QAAS,OAAO,qBAAqB,WAAW;GAGtD,MAAM,QAAQ,WAAW;AACzB,OAAI,OAAO,WAAW,UAAU,QAAW;IACzC,MAAM,UAAU,MAAM;IACtB,MAAM,UAAU;KACd,IAAK,QAAQ,aAAwB;KACrC,MAAO,QAAQ,QAAmB;KAClC,WAAW;KACZ;AACD,gBAAY,IAAI,OAAO,QAAQ;AAC/B,eAAW,KAAK;KAAE,MAAM;KAAY,KAAK;KAAS,CAAC;;;AAKvD,MAAI,OAAO,mBAAmB;GAC5B,MAAM,aAAa,OAAO;GAC1B,MAAM,QAAS,OAAO,qBAAqB,WAAW;GAGtD,MAAM,QAAQ,WAAW;AACzB,OAAI,CAAC,MAAO;AAGZ,OAAI,OAAO,MAAM,SAAS,UAAU;AAClC,eAAW,MAAM;AACjB,QAAI,MAAM,KAAK,SAAS,EACtB,YAAW,KAAK;KAAE,MAAM;KAAQ,MAAM,MAAM;KAAM,CAAC;;AASvD,OAAI,OAAO,MAAM,qBAAqB,YAAY,MAAM,qBAAqB,MAAM;IACjF,MAAM,iBAAiB,MAAM;AAC7B,QAAI,OAAO,eAAe,SAAS,SACjC,cAAa,eAAe;;AAKhC,OAAI,OAAO,MAAM,YAAY,YAAY,MAAM,YAAY,MAAM;IAC/D,MAAM,eAAe,MAAM;AAC3B,QAAI,OAAO,aAAa,UAAU,UAAU;KAI1C,MAAM,QAAQ,UAAU,SAAY,YAAY,IAAI,MAAM,GAAG;AAC7D,SAAI,MACF,OAAM,aAAa,aAAa;UAC3B;AACL;AACA,UAAI,kBAAkB,EACpB,sBAAqB,2DAA2D,mBAC9E,UACA,IACD;;;;;;AAQb,KAAI,YAAY,OAAO,GAAG;EACxB,MAAM,gBAAgB,mBAAmB,WAAW;EAMpD,MAAM,mBAAmB,WACtB,QACE,MACC,EAAE,SAAS,WACd,CACA,KAAK,OAAO;GACX,MAAM,EAAE,IAAI;GACZ,WAAW,uBAAuB,EAAE,IAAI,UAAU;GAClD,GAAI,EAAE,IAAI,KAAK,EAAE,IAAI,EAAE,IAAI,IAAI,GAAG,EAAE;GACrC,EAAE;EACL,MAAM,uBAAuB,MAAM,KAAK,YAAY,SAAS,CAAC,CAC3D,MAAM,CAAC,IAAI,CAAC,OAAO,IAAI,EAAE,CACzB,KAAK,GAAG,SAAS;GAChB,MAAM,GAAG;GACT,WAAW,uBAAuB,GAAG,UAAU;GAC/C,GAAI,GAAG,KAAK,EAAE,IAAI,GAAG,IAAI,GAAG,EAAE;GAC/B,EAAE;AACL,SAAO;GACL,GAAI,gBAAgB,EAAE,QAAQ,eAAe,GAAG,EAAE;GAKlD,GAAI,UAAU,EAAE,SAAS,GAAG,EAAE;GAC9B,WAAW,gBAAgB,mBAAmB;GAC9C,GAAI,YAAY,EAAE,WAAW,GAAG,EAAE;GAClC,GAAI,qBAAqB,EAAE,oBAAoB,GAAG,EAAE;GACpD,GAAI,iBAAiB,SAAS,IAAI,EAAE,kBAAkB,GAAG,EAAE;GAC3D,GAAI,gBAAgB,IAAI,EAAE,eAAe,GAAG,EAAE;GAC9C,GAAI,qBAAqB,EAAE,oBAAoB,GAAG,EAAE;GACpD,GAAI,YAAY,EAAE,WAAW,GAAG,EAAE;GACnC;;AAGH,QAAO;EACL;EACA,GAAI,YAAY,EAAE,WAAW,GAAG,EAAE;EAClC,GAAI,qBAAqB,EAAE,oBAAoB,GAAG,EAAE;EACpD,GAAI,iBAAiB,SAAS,IAAI,EAAE,kBAAkB,GAAG,EAAE;EAC3D,GAAI,gBAAgB,IAAI,EAAE,eAAe,GAAG,EAAE;EAC9C,GAAI,qBAAqB,EAAE,oBAAoB,GAAG,EAAE;EACpD,GAAI,YAAY,EAAE,WAAW,GAAG,EAAE;EACnC;;;;;;;;;;;;;;;;AAqBH,SAAgB,8BAA8B,MAA8B;CAC1E,MAAM,QAAQ,eAAe,KAAK;CAClC,IAAI,UAAU;CACd,IAAI,YAAY;CAChB,IAAI,gBAAgB;CACpB,IAAI;CAEJ,MAAM,YAAwB,EAAE;CAGhC,MAAM,gCAAgB,IAAI,KAGvB;CAIH,IAAI,yBAAyB;AAE7B,MAAK,MAAM,QAAQ,OAAO;EACxB,MAAM,OAAO,eAAe,cAAc,KAAK,CAAC;AAChD,MAAI,SAAS,OAAW;EAExB,MAAM,UAAU,KAAK,MAAM;EAE3B,IAAI;AACJ,MAAI;AACF,YAAS,KAAK,MAAM,QAAQ;WACrB,KAAK;AACZ;AACA,OAAI,kBAAkB,EAEpB,sBAAqB,iBADT,eAAe,QAAQ,IAAI,UAAU,UACP,KAAK,mBAAmB,SAAS,IAAI;AAEjF;;EAGF,MAAM,YAAY,OAAO;AACzB,MAAI,CAAC,UAAW;EAEhB,MAAM,QAAQ,OAAO,OAAO,UAAU,WAAW,OAAO,QAAQ;AAEhE,MAAI,cAAc,cAAc;GAE9B,MAAM,OAAO,OAAO;AACpB,OAAI,QAAQ,KAAK,SAAS,iBAAiB;IAIzC,MAAM,MAAM,SAAS;AACrB,kBAAc,IAAI,KAAK;KACrB,IAAI,KAAK,KAAK,OAAO,KAAK,GAAG,GAAG;KAChC,MAAM,OAAO,KAAK,QAAQ,GAAG;KAG7B,SAAS,KAAK;KACd,SAAS;KACV,CAAC;;aAEK,cAAc,gBAAgB,cAAc,iBAAiB;GACtE,MAAM,QAAQ,OAAO;AACrB,OAAI,CAAC,MAAO;AAEZ,OAAI,MAAM,SAAS,UAAU,OAAO,MAAM,SAAS,SACjD,YAAW,MAAM;YACR,MAAM,SAAS,mBAAmB;IAE3C,MAAM,QAAQ,UAAU,SAAY,cAAc,IAAI,MAAM,GAAG;AAC/D,QAAI,OACF;SAAI,OAAO,MAAM,cAAc,SAC7B,OAAM,WAAW,MAAM;WAEpB;AACL;AACA,SAAI,kBAAkB,EACpB,sBAAqB,mDAAmD,mBACtE,SACA,IACD;;cAGI,MAAM,SAAS,gBAGxB,WAAU,KAAK;IACb,MAAM,OAAO,MAAM,QAAQ,GAAG;IAC9B,WACE,OAAO,MAAM,cAAc,WACvB,uBAAuB,MAAM,UAAU,GACvC,KAAK,UAAU,MAAM,aAAa,EAAE,CAAC;IAC3C,GAAI,MAAM,KAAK,EAAE,IAAI,OAAO,MAAM,GAAG,EAAE,GAAG,EAAE;IAC7C,CAAC;YACO,MAAM,SAAS,mBAAmB;IAE3C,MAAM,iBAAiB,MAAM;AAC7B,QAAI,kBAAkB,OAAO,eAAe,SAAS,SACnD,cAAa,eAAe;aACnB,OAAO,MAAM,SAAS,SAC/B,cAAa,MAAM;;;;AAkB3B,MAAK,MAAM,GAAG,OAAO,MAAM,KAAK,cAAc,SAAS,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,IAAI,EAAE,EAAE;EAClF,IAAI;AACJ,MAAI,GAAG,YAAY,IAAI;AACrB,UAAO,GAAG;AAUV,OAAI;AACF,SAAK,MAAM,KAAK;WACV;AACN;AACA,QAAI,kBAAkB,EACpB,sBAAqB,iDAAiD,GAAG,KAAK,KAAK,mBACjF,MACA,IACD;AAEH,WAAO;;QAGT,QAAO,OAAO,GAAG,YAAY,WAAW,GAAG,UAAU,KAAK,UAAU,GAAG,WAAW,EAAE,CAAC;AAIvF,YAAU,KAAK;GACb,MAAM,GAAG;GACT,WAAW,uBAAuB,KAAK;GACvC,GAAI,GAAG,KAAK,EAAE,IAAI,GAAG,IAAI,GAAG,EAAE;GAC/B,CAAC;;AAGJ,KAAI,UAAU,SAAS,EACrB,QAAO;EACL,GAAI,UAAU,EAAE,SAAS,GAAG,EAAE;EAC9B;EACA,GAAI,YAAY,EAAE,WAAW,GAAG,EAAE;EAClC,GAAI,gBAAgB,IAAI,EAAE,eAAe,GAAG,EAAE;EAC9C,GAAI,qBAAqB,EAAE,oBAAoB,GAAG,EAAE;EACrD;AAGH,QAAO;EACL;EACA,GAAI,YAAY,EAAE,WAAW,GAAG,EAAE;EAClC,GAAI,gBAAgB,IAAI,EAAE,eAAe,GAAG,EAAE;EAC9C,GAAI,qBAAqB,EAAE,oBAAoB,GAAG,EAAE;EACrD;;;;;;;AAYH,SAAgB,0BACd,aACA,aACA,MACA,QACuB;CACvB,MAAM,KAAK,YAAY,aAAa;AAEpC,KAAI,GAAG,SAAS,qCAAqC,CAEnD,QAAO,2BADK,OAAO,SAAS,WAAW,OAAO,KAAK,MAAM,SAAS,GAAG,KAC/B;AAGxC,KAAI,GAAG,SAAS,uBAAuB,CAErC,QAAO,qBADK,OAAO,SAAS,WAAW,OAAO,KAAK,SAAS,OAAO,CACnC;AAGlC,KAAI,GAAG,SAAS,oBAAoB,EAAE;EACpC,MAAM,MAAM,OAAO,SAAS,WAAW,OAAO,KAAK,SAAS,OAAO;AACnE,UAAQ,aAAR;GACE,KAAK;GACL,KAAK,QACH,QAAO,kBAAkB,IAAI;GAC/B,KAAK,YACH,QAAO,qBAAqB,IAAI;GAClC,KAAK;GACL,KAAK,WACH,QAAO,kBAAkB,IAAI;GAC/B,KAAK,sBACH,QAAO,8BAA8B,IAAI;GAC3C,KAAK,SACH,QAAO,kBAAkB,IAAI;GAC/B,KAAK,UACH,QAAO,qBAAqB,IAAI;GAClC;AACE,YAAQ,KACN,2CAA2C,YAAY,sCACxD;AACD,WAAO,kBAAkB,IAAI;;;AAInC,QAAO"}
@@ -1,5 +1,5 @@
1
1
  import { Logger } from "./logger.cjs";
2
- import { RecordProviderKey, ToolCall } from "./types.cjs";
2
+ import { FixtureBlock, RecordProviderKey, ToolCall } from "./types.cjs";
3
3
 
4
4
  //#region src/stream-collapse.d.ts
5
5
 
@@ -42,6 +42,22 @@ interface CollapseResult {
42
42
  harmonyUnparsed?: true;
43
43
  /** Short human-readable note accompanying {@link harmonyUnparsed}. */
44
44
  harmonyNote?: string;
45
+ /**
46
+ * Ordered cross-channel block list, in STREAM order, populated ONLY when the
47
+ * stream is "interleaved" — i.e. a tool-call delta appeared STRICTLY BEFORE
48
+ * the first content delta, OR a content delta appeared AFTER any tool-call
49
+ * delta. The flat `content` / `toolCalls` fields stay populated UNCHANGED for
50
+ * replay back-compat and non-block consumers; `blocks` is purely additive
51
+ * positional instrumentation the recorder consults to decide whether to
52
+ * persist the ordered shape. Absent (undefined) for text-first, text-only,
53
+ * and tool-only streams — i.e. anything NOT interleaved — so the recorder
54
+ * keeps the legacy `{ content, toolCalls }` shape byte-identical.
55
+ *
56
+ * Each text block coalesces all contiguous content deltas between tool
57
+ * atoms; each toolCall block carries the fully-assembled name/arguments/id
58
+ * for one tool call in the position its FIRST delta arrived.
59
+ */
60
+ blocks?: FixtureBlock[];
45
61
  }
46
62
  /**
47
63
  * The opaque `data` of a non-empty Anthropic `redacted_thinking` block, or
@@ -1 +1 @@
1
- {"version":3,"file":"stream-collapse.d.cts","names":[],"sources":["../src/stream-collapse.ts"],"sourcesContent":[],"mappings":";;;;;AAs+BgB,UAp9BC,cAAA,CAo9BD;EAA0B,OAAA,CAAA,EAAA,MAAA;WAAO,CAAA,EAAA,MAAA;;;AA6MjD;AA4JA;;;;;;;;;;;;;;;;cAvyCc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAkHE,iBAAA,gBAAiC;;;;;;;;iBA+MjC,oBAAA,gBAAoC;;;;;;;iBAkKpC,iBAAA,gBAAiC;;;;;;;;;;;;;;;iBA0HjC,oBAAA,gBAAoC;;;;;;;iBA6GpC,iBAAA,gBAAiC;;;;;;;iBAoPjC,0BAAA,OAAiC,SAAS;;;;;;;;;;;;;;;iBA6M1C,6BAAA,gBAA6C;;;;;;iBA4J7C,yBAAA,mCAED,kCACE,iBACN,SACR"}
1
+ {"version":3,"file":"stream-collapse.d.cts","names":[],"sources":["../src/stream-collapse.ts"],"sourcesContent":[],"mappings":";;;;;AAkgBgB,UAhfC,cAAA,CAgfmB;EA8LpB,OAAA,CAAA,EAAA,MAAA;EAoJA,SAAA,CAAA,EAAA,MAAA;EA+HA;AAqRhB;;;;;AAoPA;AAoLA;;;;oBAIW,CAAA,EAAA,MAAA;;;;;;;;cA5mDG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;WAgCH;;;;;;;;;;;;;;;;;;;iBA+MK,iBAAA,gBAAiC;;;;;;;;iBA2OjC,oBAAA,gBAAoC;;;;;;;iBA8LpC,iBAAA,gBAAiC;;;;;;;;;;;;;;;iBAoJjC,oBAAA,gBAAoC;;;;;;;iBA+HpC,iBAAA,gBAAiC;;;;;;;iBAqRjC,0BAAA,OAAiC,SAAS;;;;;;;;;;;;;;;iBAoP1C,6BAAA,gBAA6C;;;;;;iBAoL7C,yBAAA,mCAED,kCACE,iBACN,SACR"}
@@ -1,5 +1,5 @@
1
1
  import { Logger } from "./logger.js";
2
- import { RecordProviderKey, ToolCall } from "./types.js";
2
+ import { FixtureBlock, RecordProviderKey, ToolCall } from "./types.js";
3
3
 
4
4
  //#region src/stream-collapse.d.ts
5
5
 
@@ -42,6 +42,22 @@ interface CollapseResult {
42
42
  harmonyUnparsed?: true;
43
43
  /** Short human-readable note accompanying {@link harmonyUnparsed}. */
44
44
  harmonyNote?: string;
45
+ /**
46
+ * Ordered cross-channel block list, in STREAM order, populated ONLY when the
47
+ * stream is "interleaved" — i.e. a tool-call delta appeared STRICTLY BEFORE
48
+ * the first content delta, OR a content delta appeared AFTER any tool-call
49
+ * delta. The flat `content` / `toolCalls` fields stay populated UNCHANGED for
50
+ * replay back-compat and non-block consumers; `blocks` is purely additive
51
+ * positional instrumentation the recorder consults to decide whether to
52
+ * persist the ordered shape. Absent (undefined) for text-first, text-only,
53
+ * and tool-only streams — i.e. anything NOT interleaved — so the recorder
54
+ * keeps the legacy `{ content, toolCalls }` shape byte-identical.
55
+ *
56
+ * Each text block coalesces all contiguous content deltas between tool
57
+ * atoms; each toolCall block carries the fully-assembled name/arguments/id
58
+ * for one tool call in the position its FIRST delta arrived.
59
+ */
60
+ blocks?: FixtureBlock[];
45
61
  }
46
62
  /**
47
63
  * The opaque `data` of a non-empty Anthropic `redacted_thinking` block, or
@@ -1 +1 @@
1
- {"version":3,"file":"stream-collapse.d.ts","names":[],"sources":["../src/stream-collapse.ts"],"sourcesContent":[],"mappings":";;;;;AAs+BgB,UAp9BC,cAAA,CAo9BD;EAA0B,OAAA,CAAA,EAAA,MAAA;WAAO,CAAA,EAAA,MAAA;;;AA6MjD;AA4JA;;;;;;;;;;;;;;;;cAvyCc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAkHE,iBAAA,gBAAiC;;;;;;;;iBA+MjC,oBAAA,gBAAoC;;;;;;;iBAkKpC,iBAAA,gBAAiC;;;;;;;;;;;;;;;iBA0HjC,oBAAA,gBAAoC;;;;;;;iBA6GpC,iBAAA,gBAAiC;;;;;;;iBAoPjC,0BAAA,OAAiC,SAAS;;;;;;;;;;;;;;;iBA6M1C,6BAAA,gBAA6C;;;;;;iBA4J7C,yBAAA,mCAED,kCACE,iBACN,SACR"}
1
+ {"version":3,"file":"stream-collapse.d.ts","names":[],"sources":["../src/stream-collapse.ts"],"sourcesContent":[],"mappings":";;;;;AAkgBgB,UAhfC,cAAA,CAgfmB;EA8LpB,OAAA,CAAA,EAAA,MAAA;EAoJA,SAAA,CAAA,EAAA,MAAA;EA+HA;AAqRhB;;;;;AAoPA;AAoLA;;;;oBAIW,CAAA,EAAA,MAAA;;;;;;;;cA5mDG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;WAgCH;;;;;;;;;;;;;;;;;;;iBA+MK,iBAAA,gBAAiC;;;;;;;;iBA2OjC,oBAAA,gBAAoC;;;;;;;iBA8LpC,iBAAA,gBAAiC;;;;;;;;;;;;;;;iBAoJjC,oBAAA,gBAAoC;;;;;;;iBA+HpC,iBAAA,gBAAiC;;;;;;;iBAqRjC,0BAAA,OAAiC,SAAS;;;;;;;;;;;;;;;iBAoP1C,6BAAA,gBAA6C;;;;;;iBAoL7C,yBAAA,mCAED,kCACE,iBACN,SACR"}