@copilotkit/aimock 1.40.0 → 1.42.0

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 (251) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/CHANGELOG.md +28 -1
  4. package/README.md +5 -3
  5. package/dist/a2a-mock.cjs +18 -11
  6. package/dist/a2a-mock.cjs.map +1 -1
  7. package/dist/a2a-mock.d.cts +1 -0
  8. package/dist/a2a-mock.d.cts.map +1 -1
  9. package/dist/a2a-mock.d.ts +1 -0
  10. package/dist/a2a-mock.d.ts.map +1 -1
  11. package/dist/a2a-mock.js +18 -11
  12. package/dist/a2a-mock.js.map +1 -1
  13. package/dist/agui-handler.d.cts.map +1 -1
  14. package/dist/agui-handler.d.ts.map +1 -1
  15. package/dist/agui-mock.cjs +2 -0
  16. package/dist/agui-mock.cjs.map +1 -1
  17. package/dist/agui-mock.d.cts.map +1 -1
  18. package/dist/agui-mock.d.ts.map +1 -1
  19. package/dist/agui-mock.js +2 -0
  20. package/dist/agui-mock.js.map +1 -1
  21. package/dist/bedrock-converse.cjs +36 -2
  22. package/dist/bedrock-converse.cjs.map +1 -1
  23. package/dist/bedrock-converse.d.cts.map +1 -1
  24. package/dist/bedrock-converse.d.ts.map +1 -1
  25. package/dist/bedrock-converse.js +37 -3
  26. package/dist/bedrock-converse.js.map +1 -1
  27. package/dist/bedrock.cjs +50 -13
  28. package/dist/bedrock.cjs.map +1 -1
  29. package/dist/bedrock.d.cts.map +1 -1
  30. package/dist/bedrock.d.ts.map +1 -1
  31. package/dist/bedrock.js +51 -14
  32. package/dist/bedrock.js.map +1 -1
  33. package/dist/byteplus-video.cjs +954 -0
  34. package/dist/byteplus-video.cjs.map +1 -0
  35. package/dist/byteplus-video.d.cts +176 -0
  36. package/dist/byteplus-video.d.cts.map +1 -0
  37. package/dist/byteplus-video.d.ts +176 -0
  38. package/dist/byteplus-video.d.ts.map +1 -0
  39. package/dist/byteplus-video.js +947 -0
  40. package/dist/byteplus-video.js.map +1 -0
  41. package/dist/chaos.cjs +36 -7
  42. package/dist/chaos.cjs.map +1 -1
  43. package/dist/chaos.d.cts +8 -3
  44. package/dist/chaos.d.cts.map +1 -1
  45. package/dist/chaos.d.ts +8 -3
  46. package/dist/chaos.d.ts.map +1 -1
  47. package/dist/chaos.js +36 -8
  48. package/dist/chaos.js.map +1 -1
  49. package/dist/cli.cjs +5 -1
  50. package/dist/cli.cjs.map +1 -1
  51. package/dist/cli.js +5 -1
  52. package/dist/cli.js.map +1 -1
  53. package/dist/cohere.cjs +58 -5
  54. package/dist/cohere.cjs.map +1 -1
  55. package/dist/cohere.d.cts.map +1 -1
  56. package/dist/cohere.d.ts.map +1 -1
  57. package/dist/cohere.js +59 -6
  58. package/dist/cohere.js.map +1 -1
  59. package/dist/config-loader.d.cts.map +1 -1
  60. package/dist/elevenlabs-audio.cjs +36 -2
  61. package/dist/elevenlabs-audio.cjs.map +1 -1
  62. package/dist/elevenlabs-audio.d.cts.map +1 -1
  63. package/dist/elevenlabs-audio.d.ts.map +1 -1
  64. package/dist/elevenlabs-audio.js +37 -3
  65. package/dist/elevenlabs-audio.js.map +1 -1
  66. package/dist/embeddings.cjs +58 -3
  67. package/dist/embeddings.cjs.map +1 -1
  68. package/dist/embeddings.d.cts.map +1 -1
  69. package/dist/embeddings.d.ts.map +1 -1
  70. package/dist/embeddings.js +59 -4
  71. package/dist/embeddings.js.map +1 -1
  72. package/dist/fal-audio.cjs +38 -2
  73. package/dist/fal-audio.cjs.map +1 -1
  74. package/dist/fal-audio.d.cts.map +1 -1
  75. package/dist/fal-audio.d.ts.map +1 -1
  76. package/dist/fal-audio.js +39 -3
  77. package/dist/fal-audio.js.map +1 -1
  78. package/dist/fixture-loader.cjs +0 -0
  79. package/dist/fixture-loader.cjs.map +1 -1
  80. package/dist/fixture-loader.d.cts +9 -1
  81. package/dist/fixture-loader.d.cts.map +1 -1
  82. package/dist/fixture-loader.d.ts +9 -1
  83. package/dist/fixture-loader.d.ts.map +1 -1
  84. package/dist/fixture-loader.js +0 -0
  85. package/dist/fixture-loader.js.map +1 -1
  86. package/dist/gemini-embeddings.cjs +19 -1
  87. package/dist/gemini-embeddings.cjs.map +1 -1
  88. package/dist/gemini-embeddings.js +20 -2
  89. package/dist/gemini-embeddings.js.map +1 -1
  90. package/dist/gemini-interactions.cjs +15 -1
  91. package/dist/gemini-interactions.cjs.map +1 -1
  92. package/dist/gemini-interactions.d.cts.map +1 -1
  93. package/dist/gemini-interactions.d.ts.map +1 -1
  94. package/dist/gemini-interactions.js +16 -2
  95. package/dist/gemini-interactions.js.map +1 -1
  96. package/dist/gemini.cjs +70 -6
  97. package/dist/gemini.cjs.map +1 -1
  98. package/dist/gemini.d.cts.map +1 -1
  99. package/dist/gemini.d.ts.map +1 -1
  100. package/dist/gemini.js +71 -7
  101. package/dist/gemini.js.map +1 -1
  102. package/dist/grok-video.cjs +2 -2
  103. package/dist/grok-video.cjs.map +1 -1
  104. package/dist/grok-video.d.cts.map +1 -1
  105. package/dist/grok-video.d.ts.map +1 -1
  106. package/dist/grok-video.js +2 -2
  107. package/dist/grok-video.js.map +1 -1
  108. package/dist/helpers.cjs +160 -5
  109. package/dist/helpers.cjs.map +1 -1
  110. package/dist/helpers.d.cts.map +1 -1
  111. package/dist/helpers.d.ts.map +1 -1
  112. package/dist/helpers.js +152 -6
  113. package/dist/helpers.js.map +1 -1
  114. package/dist/images.cjs +24 -6
  115. package/dist/images.cjs.map +1 -1
  116. package/dist/images.d.cts +11 -3
  117. package/dist/images.d.cts.map +1 -1
  118. package/dist/images.d.ts +11 -3
  119. package/dist/images.d.ts.map +1 -1
  120. package/dist/images.js +25 -7
  121. package/dist/images.js.map +1 -1
  122. package/dist/index.cjs +7 -0
  123. package/dist/index.d.cts +2 -1
  124. package/dist/index.d.ts +2 -1
  125. package/dist/index.js +2 -1
  126. package/dist/llmock.cjs +15 -0
  127. package/dist/llmock.cjs.map +1 -1
  128. package/dist/llmock.d.cts +12 -0
  129. package/dist/llmock.d.cts.map +1 -1
  130. package/dist/llmock.d.ts +12 -0
  131. package/dist/llmock.d.ts.map +1 -1
  132. package/dist/llmock.js +16 -1
  133. package/dist/llmock.js.map +1 -1
  134. package/dist/mcp-mock.cjs +11 -0
  135. package/dist/mcp-mock.cjs.map +1 -1
  136. package/dist/mcp-mock.d.cts.map +1 -1
  137. package/dist/mcp-mock.d.ts.map +1 -1
  138. package/dist/mcp-mock.js +11 -0
  139. package/dist/mcp-mock.js.map +1 -1
  140. package/dist/messages.cjs +18 -1
  141. package/dist/messages.cjs.map +1 -1
  142. package/dist/messages.d.cts.map +1 -1
  143. package/dist/messages.d.ts.map +1 -1
  144. package/dist/messages.js +19 -2
  145. package/dist/messages.js.map +1 -1
  146. package/dist/metrics.cjs +20 -0
  147. package/dist/metrics.cjs.map +1 -1
  148. package/dist/metrics.d.cts.map +1 -1
  149. package/dist/metrics.d.ts.map +1 -1
  150. package/dist/metrics.js +19 -1
  151. package/dist/metrics.js.map +1 -1
  152. package/dist/moderation.cjs +67 -3
  153. package/dist/moderation.cjs.map +1 -1
  154. package/dist/moderation.d.cts +1 -0
  155. package/dist/moderation.d.cts.map +1 -1
  156. package/dist/moderation.d.ts +1 -0
  157. package/dist/moderation.d.ts.map +1 -1
  158. package/dist/moderation.js +68 -4
  159. package/dist/moderation.js.map +1 -1
  160. package/dist/ollama.cjs +75 -5
  161. package/dist/ollama.cjs.map +1 -1
  162. package/dist/ollama.d.cts.map +1 -1
  163. package/dist/ollama.d.ts.map +1 -1
  164. package/dist/ollama.js +76 -6
  165. package/dist/ollama.js.map +1 -1
  166. package/dist/openrouter-video.cjs +4 -4
  167. package/dist/openrouter-video.cjs.map +1 -1
  168. package/dist/openrouter-video.d.cts.map +1 -1
  169. package/dist/openrouter-video.d.ts.map +1 -1
  170. package/dist/openrouter-video.js +4 -4
  171. package/dist/openrouter-video.js.map +1 -1
  172. package/dist/provider-auth.cjs +2 -0
  173. package/dist/provider-auth.cjs.map +1 -1
  174. package/dist/provider-auth.js +2 -0
  175. package/dist/provider-auth.js.map +1 -1
  176. package/dist/rerank.cjs +65 -2
  177. package/dist/rerank.cjs.map +1 -1
  178. package/dist/rerank.d.cts +1 -1
  179. package/dist/rerank.d.cts.map +1 -1
  180. package/dist/rerank.d.ts +1 -1
  181. package/dist/rerank.d.ts.map +1 -1
  182. package/dist/rerank.js +66 -3
  183. package/dist/rerank.js.map +1 -1
  184. package/dist/responses.cjs +18 -1
  185. package/dist/responses.cjs.map +1 -1
  186. package/dist/responses.d.cts.map +1 -1
  187. package/dist/responses.d.ts.map +1 -1
  188. package/dist/responses.js +19 -2
  189. package/dist/responses.js.map +1 -1
  190. package/dist/router.cjs +1 -0
  191. package/dist/router.cjs.map +1 -1
  192. package/dist/router.d.cts.map +1 -1
  193. package/dist/router.d.ts.map +1 -1
  194. package/dist/router.js +1 -0
  195. package/dist/router.js.map +1 -1
  196. package/dist/search.cjs +67 -3
  197. package/dist/search.cjs.map +1 -1
  198. package/dist/search.d.cts +1 -0
  199. package/dist/search.d.cts.map +1 -1
  200. package/dist/search.d.ts +1 -0
  201. package/dist/search.d.ts.map +1 -1
  202. package/dist/search.js +68 -4
  203. package/dist/search.js.map +1 -1
  204. package/dist/server.cjs +406 -17
  205. package/dist/server.cjs.map +1 -1
  206. package/dist/server.d.cts +2 -0
  207. package/dist/server.d.cts.map +1 -1
  208. package/dist/server.d.ts +2 -0
  209. package/dist/server.d.ts.map +1 -1
  210. package/dist/server.js +410 -21
  211. package/dist/server.js.map +1 -1
  212. package/dist/speech.cjs +18 -1
  213. package/dist/speech.cjs.map +1 -1
  214. package/dist/speech.d.cts.map +1 -1
  215. package/dist/speech.d.ts.map +1 -1
  216. package/dist/speech.js +19 -2
  217. package/dist/speech.js.map +1 -1
  218. package/dist/stream-collapse.cjs +2 -1
  219. package/dist/stream-collapse.cjs.map +1 -1
  220. package/dist/stream-collapse.js +2 -1
  221. package/dist/stream-collapse.js.map +1 -1
  222. package/dist/transcription.cjs +21 -2
  223. package/dist/transcription.cjs.map +1 -1
  224. package/dist/transcription.d.cts.map +1 -1
  225. package/dist/transcription.d.ts.map +1 -1
  226. package/dist/transcription.js +21 -2
  227. package/dist/transcription.js.map +1 -1
  228. package/dist/types.d.cts +58 -5
  229. package/dist/types.d.cts.map +1 -1
  230. package/dist/types.d.ts +58 -5
  231. package/dist/types.d.ts.map +1 -1
  232. package/dist/vector-mock.cjs +88 -0
  233. package/dist/vector-mock.cjs.map +1 -1
  234. package/dist/vector-mock.d.cts.map +1 -1
  235. package/dist/vector-mock.d.ts.map +1 -1
  236. package/dist/vector-mock.js +89 -1
  237. package/dist/vector-mock.js.map +1 -1
  238. package/dist/vector-types.d.cts.map +1 -1
  239. package/dist/veo-video.cjs +2 -2
  240. package/dist/veo-video.cjs.map +1 -1
  241. package/dist/veo-video.d.cts.map +1 -1
  242. package/dist/veo-video.d.ts.map +1 -1
  243. package/dist/veo-video.js +2 -2
  244. package/dist/veo-video.js.map +1 -1
  245. package/dist/video.cjs +19 -2
  246. package/dist/video.cjs.map +1 -1
  247. package/dist/video.d.cts.map +1 -1
  248. package/dist/video.d.ts.map +1 -1
  249. package/dist/video.js +20 -3
  250. package/dist/video.js.map +1 -1
  251. package/package.json +2 -2
@@ -1 +1 @@
1
- {"version":3,"file":"messages.js","names":[],"sources":["../src/messages.ts"],"sourcesContent":["/**\n * Anthropic Claude Messages API support.\n *\n * Translates incoming /v1/messages requests into the ChatCompletionRequest\n * format used by the fixture router, and converts fixture responses back into\n * the Claude Messages API streaming (or non-streaming) format.\n */\n\nimport type * as http from \"node:http\";\nimport type {\n ChatCompletionRequest,\n ChatMessage,\n Fixture,\n HandlerDefaults,\n RecordedTimings,\n FixtureBlock,\n ResponseOverrides,\n StreamingProfile,\n ToolCall,\n ToolDefinition,\n} from \"./types.js\";\nimport {\n generateMessageId,\n generateToolUseId,\n extractOverrides,\n isTextResponse,\n isToolCallResponse,\n isContentWithToolCallsResponse,\n isErrorResponse,\n resolveFixtureBlocks,\n flattenHeaders,\n getTestId,\n resolveResponse,\n resolveStrictMode,\n resolveReasoningForModel,\n resolveReasoningArtifactsForModel,\n strictOverrideField,\n getContext,\n strictNoMatchMessage,\n strictNoMatchLogLine,\n} from \"./helpers.js\";\nimport { matchFixtureDiagnostic, recordMatchOptions } from \"./router.js\";\nimport { writeErrorResponse, delay, calculateDelay } from \"./sse-writer.js\";\nimport { createInterruptionSignal } from \"./interruption.js\";\nimport type { Journal } from \"./journal.js\";\nimport type { Logger } from \"./logger.js\";\nimport { applyChaos } from \"./chaos.js\";\nimport { proxyAndRecord } from \"./recorder.js\";\n\n/**\n * Non-empty placeholder signature written into emitted `thinking` blocks.\n *\n * The real Anthropic signature is a cryptographic value aimock cannot\n * reproduce, but extended-thinking invariant (b) requires a non-empty\n * `signature` on the leading thinking block of a tool-loop continuation turn.\n * Emitting \"\" would make a record→replay round-trip of an aimock thinking turn\n * self-trip that invariant under strict mode. A non-empty placeholder keeps\n * round-trips green; the invariant only checks for non-emptiness, not value.\n */\nconst PLACEHOLDER_SIGNATURE = \"aimock-placeholder-signature\";\n\n// ─── Claude Messages API request types ──────────────────────────────────────\n\ninterface ClaudeContentBlock {\n type:\n | \"text\"\n | \"tool_use\"\n | \"tool_result\"\n | \"image\"\n | \"document\"\n | \"thinking\"\n | \"redacted_thinking\";\n text?: string;\n id?: string;\n name?: string;\n input?: unknown;\n tool_use_id?: string;\n content?: string | ClaudeContentBlock[];\n is_error?: boolean;\n // Extended-thinking fields (Anthropic): `thinking` blocks carry the reasoning\n // text plus a cryptographic `signature`; `redacted_thinking` blocks carry an\n // opaque `data` payload instead.\n thinking?: string;\n signature?: string;\n data?: string;\n}\n\ninterface ClaudeMessage {\n role: \"user\" | \"assistant\";\n content: string | ClaudeContentBlock[];\n}\n\ninterface ClaudeToolDef {\n name: string;\n description?: string;\n input_schema?: object;\n}\n\ninterface ClaudeRequest {\n model: string;\n messages: ClaudeMessage[];\n system?: string | ClaudeContentBlock[];\n tools?: ClaudeToolDef[];\n tool_choice?: unknown;\n stream?: boolean;\n max_tokens: number;\n temperature?: number;\n // Extended-thinking config. Explicitly modeled so it is no longer swallowed\n // by the index signature below; read defensively (may be a non-object).\n thinking?: { type?: \"enabled\" | \"disabled\"; budget_tokens?: number };\n [key: string]: unknown;\n}\n\n// ─── Extended-thinking request invariants (Anthropic) ───────────────────────\n\n/**\n * A detected violation of the Anthropic extended-thinking request invariants on\n * a tool-loop continuation turn. `kind` distinguishes the three failure modes\n * so callers can template a per-kind error message and tests can assert without\n * string matching.\n */\ninterface ThinkingViolation {\n kind: \"missing_thinking_first\" | \"missing_signature\" | \"dropped_redacted_thinking\";\n messageIndex: number;\n observedFirstBlockType?: string;\n}\n\n// ─── Input conversion: Claude → ChatCompletions messages ────────────────────\n\nfunction extractClaudeTextContent(content: string | ClaudeContentBlock[]): string {\n if (typeof content === \"string\") return content;\n if (!Array.isArray(content)) return \"\";\n return content\n .filter((b) => b != null && typeof b === \"object\" && b.type === \"text\")\n .map((b) => b.text ?? \"\")\n .join(\"\");\n}\n\nexport function claudeToCompletionRequest(req: ClaudeRequest): ChatCompletionRequest {\n const messages: ChatMessage[] = [];\n\n // system field → system message\n if (req.system) {\n const systemText =\n typeof req.system === \"string\"\n ? req.system\n : Array.isArray(req.system)\n ? req.system\n .filter((b) => b != null && typeof b === \"object\" && b.type === \"text\")\n .map((b) => b.text ?? \"\")\n .join(\"\")\n : \"\";\n if (systemText) {\n messages.push({ role: \"system\", content: systemText });\n }\n }\n\n const reqMessages = Array.isArray(req.messages) ? req.messages : [];\n for (const msg of reqMessages) {\n // `req.messages` is untrusted JSON; entries may be null / non-object.\n if (!msg || typeof msg !== \"object\") continue;\n if (msg.role === \"user\") {\n // Check for tool_result blocks\n if (typeof msg.content !== \"string\" && Array.isArray(msg.content)) {\n // Content blocks are equally untrusted; skip null / non-object entries.\n const blocks = msg.content.filter((b) => b != null && typeof b === \"object\");\n const toolResults = blocks.filter((b) => b.type === \"tool_result\");\n const textBlocks = blocks.filter((b) => b.type === \"text\");\n\n if (toolResults.length > 0) {\n // Anthropic bundles a leg-2 tool_result and any accompanying user text\n // into ONE user-role message. We emit the accompanying text FIRST, then\n // the tool message(s), so the normalized shape is\n // `[..., user(text), tool]` — the tool message trails the last user\n // message and the current-turn `hasToolResult` predicate\n // ({@link currentTurnHasToolResult}) correctly classifies it `true`\n // (this IS a leg-2 turn that carries a tool result). Emitting the tool\n // FIRST would produce `[..., tool, user(text)]`, which is byte-identical\n // to a genuine turn-N leg-1 (a fresh user question after a completed\n // tool turn) and would be misclassified `false`. The source-message\n // grouping — that this text and this tool_result came from the SAME\n // Anthropic message — is only available here, so this ordering is the\n // only place the two cases can be told apart. The common\n // tool_result-only follow-up (no text blocks) is unaffected.\n if (textBlocks.length > 0) {\n messages.push({\n role: \"user\",\n content: textBlocks.map((b) => b.text ?? \"\").join(\"\"),\n });\n }\n // Each tool_result → tool message\n for (const tr of toolResults) {\n const resultContent =\n typeof tr.content === \"string\"\n ? tr.content\n : Array.isArray(tr.content)\n ? tr.content\n .filter((b) => b != null && typeof b === \"object\" && b.type === \"text\")\n .map((b) => b.text ?? \"\")\n .join(\"\")\n : \"\";\n messages.push({\n role: \"tool\",\n content: resultContent,\n tool_call_id: tr.tool_use_id,\n });\n }\n continue;\n }\n }\n // Regular user message\n messages.push({\n role: \"user\",\n content: extractClaudeTextContent(msg.content),\n });\n } else if (msg.role === \"assistant\") {\n if (typeof msg.content === \"string\") {\n messages.push({ role: \"assistant\", content: msg.content });\n } else if (Array.isArray(msg.content)) {\n const toolUseBlocks = msg.content.filter(\n (b) => b != null && typeof b === \"object\" && b.type === \"tool_use\",\n );\n // Only `text` blocks feed fixture matching; `thinking` /\n // `redacted_thinking` blocks are intentionally excluded from the\n // matchable content.\n const textContent = extractClaudeTextContent(msg.content);\n\n if (toolUseBlocks.length > 0) {\n messages.push({\n role: \"assistant\",\n content: textContent || null,\n tool_calls: toolUseBlocks.map((b) => ({\n id: b.id ?? generateToolUseId(),\n type: \"function\" as const,\n function: {\n name: b.name ?? \"\",\n arguments: typeof b.input === \"string\" ? b.input : JSON.stringify(b.input ?? {}),\n },\n })),\n });\n } else {\n messages.push({ role: \"assistant\", content: textContent || null });\n }\n } else {\n // null/undefined content — tool-only assistant turn\n messages.push({ role: \"assistant\", content: null });\n }\n }\n }\n\n // Convert tools\n let tools: ToolDefinition[] | undefined;\n if (req.tools && req.tools.length > 0) {\n tools = req.tools.map((t) => ({\n type: \"function\" as const,\n function: {\n name: t.name,\n description: t.description,\n parameters: t.input_schema,\n },\n }));\n }\n\n return {\n model: req.model,\n messages,\n stream: req.stream,\n temperature: req.temperature,\n max_tokens: req.max_tokens,\n tools,\n _endpointType: \"chat\",\n };\n}\n\n// ─── Extended-thinking invariant validation ─────────────────────────────────\n\n/** True iff `req.thinking` is an object with `type === \"enabled\"`. */\nfunction isThinkingEnabled(req: ClaudeRequest): boolean {\n const t = req.thinking;\n return typeof t === \"object\" && t !== null && (t as { type?: unknown }).type === \"enabled\";\n}\n\n/**\n * True iff the user turn at `req.messages[userIndex]` carries a `tool_result`\n * referencing one of `toolUseIds` — i.e. it answers the preceding assistant\n * turn's `tool_use` block(s), making that assistant turn a tool-loop\n * continuation point.\n */\nfunction userTurnAnswersToolUse(msg: ClaudeMessage | undefined, toolUseIds: Set<string>): boolean {\n if (!msg || msg.role !== \"user\" || !Array.isArray(msg.content)) return false;\n return msg.content.some(\n (b) =>\n b != null &&\n typeof b === \"object\" &&\n b.type === \"tool_result\" &&\n typeof b.tool_use_id === \"string\" &&\n toolUseIds.has(b.tool_use_id),\n );\n}\n\n/**\n * Validate the Anthropic extended-thinking request invariants on tool-loop\n * continuation turns. Pure: returns the first detected `ThinkingViolation` or\n * `null` when thinking is disabled or every in-scope turn is well-formed.\n *\n * Scope: an assistant turn is in-scope only when it (a) is array content\n * carrying at least one `tool_use` block and (b) is followed by a matching\n * `tool_result` on the next user turn. Text-only / `end_turn` turns, string or\n * empty turns, and trailing unanswered `tool_use` turns are all exempt.\n *\n * Scope detection assumes well-formed Anthropic transcripts: `tool_use` ids are\n * unique, and a tool_use turn is answered by the immediately-following user\n * turn's `tool_result` (adjacency). Malformed shapes — non-adjacent answers,\n * idless `tool_use` blocks, or a `tool_result` separated from its `tool_use` by\n * intervening turns — are intentionally treated as out-of-scope (and therefore\n * not 400'd) rather than validated, since the real Anthropic API would never\n * have produced them.\n */\nexport function validateThinkingInvariants(req: ClaudeRequest): ThinkingViolation | null {\n if (!isThinkingEnabled(req)) return null;\n\n const messages = Array.isArray(req.messages) ? req.messages : [];\n for (let i = 0; i < messages.length; i++) {\n const msg = messages[i];\n // `req.messages` is untrusted JSON; entries may be null / non-object.\n if (!msg || typeof msg !== \"object\") continue;\n if (msg.role !== \"assistant\" || !Array.isArray(msg.content) || msg.content.length === 0) {\n continue;\n }\n\n const toolUseIds = new Set<string>();\n for (const b of msg.content) {\n // Content blocks are equally untrusted; skip null / non-object entries.\n if (!b || typeof b !== \"object\") continue;\n if (b.type === \"tool_use\" && typeof b.id === \"string\") toolUseIds.add(b.id);\n }\n // Not a tool_use-bearing turn → not a reasoning-bearing continuation turn.\n if (toolUseIds.size === 0) continue;\n // Trailing/unanswered tool_use (no matching tool_result follows) → out of scope.\n if (!userTurnAnswersToolUse(messages[i + 1], toolUseIds)) continue;\n\n const first = msg.content[0];\n\n // A null / non-object leading block cannot be a thinking block → it\n // violates invariant (a) just as a wrong-typed block would.\n if (!first || typeof first !== \"object\") {\n return {\n kind: \"missing_thinking_first\",\n messageIndex: i,\n observedFirstBlockType: undefined,\n };\n }\n\n // (a) The in-scope turn must lead with a thinking / redacted_thinking block.\n if (first.type !== \"thinking\" && first.type !== \"redacted_thinking\") {\n return {\n kind: \"missing_thinking_first\",\n messageIndex: i,\n observedFirstBlockType: first.type,\n };\n }\n\n // (b) A leading `thinking` block must carry a non-empty string `signature`.\n if (first.type === \"thinking\") {\n if (typeof first.signature !== \"string\" || first.signature.length === 0) {\n return { kind: \"missing_signature\", messageIndex: i };\n }\n }\n\n // (c) A leading `redacted_thinking` block must preserve a non-empty `data`.\n if (first.type === \"redacted_thinking\") {\n if (typeof first.data !== \"string\" || first.data.length === 0) {\n return { kind: \"dropped_redacted_thinking\", messageIndex: i };\n }\n }\n }\n\n return null;\n}\n\n/** Render the Anthropic-shaped 400 error message for a thinking violation. */\nfunction thinkingViolationMessage(v: ThinkingViolation): string {\n const prefix = `messages.${v.messageIndex}.content.0`;\n switch (v.kind) {\n case \"missing_thinking_first\":\n return `${prefix}: when \\`thinking\\` is enabled, a tool-loop continuation assistant turn must begin with a \\`thinking\\` block; got \\`${v.observedFirstBlockType ?? \"unknown\"}\\`.`;\n case \"missing_signature\":\n return `${prefix}: the leading \\`thinking\\` block is missing a non-empty \\`signature\\`.`;\n case \"dropped_redacted_thinking\":\n return `${prefix}: the leading \\`redacted_thinking\\` block must preserve its \\`data\\`.`;\n }\n}\n\n// ─── Response building: fixture → Claude Messages API format ────────────────\n\nfunction claudeStopReason(finishReason: string | undefined, defaultReason: string): string {\n if (!finishReason) return defaultReason;\n if (finishReason === \"stop\") return \"end_turn\";\n if (finishReason === \"tool_calls\") return \"tool_use\";\n if (finishReason === \"length\") return \"max_tokens\";\n return finishReason;\n}\n\nfunction claudeUsage(overrides?: ResponseOverrides): {\n input_tokens: number;\n output_tokens: number;\n} {\n if (!overrides?.usage) return { input_tokens: 0, output_tokens: 0 };\n return {\n input_tokens: overrides.usage.input_tokens ?? overrides.usage.prompt_tokens ?? 0,\n output_tokens: overrides.usage.output_tokens ?? overrides.usage.completion_tokens ?? 0,\n };\n}\n\ninterface ClaudeSSEEvent {\n type: string;\n [key: string]: unknown;\n}\n\n/**\n * Emit faithful Anthropic `redacted_thinking` content-block events at the head\n * of a streamed turn — one `content_block_start` (carrying the opaque `data`)\n * and `content_block_stop` per recorded block, with NO deltas (real Anthropic\n * delivers the encrypted reasoning entirely on the start event). Returns the\n * next free block index. When `redactedThinking` is empty/absent this is a\n * no-op, so no events are added.\n *\n * Leading placement keeps the turn round-trip-safe for the extended-thinking\n * leading-block invariant (a thinking / redacted_thinking block must come\n * first). It does NOT reproduce real Anthropic's stream order: aimock joins all\n * plaintext reasoning into one block and emits all redacted blocks ahead of it,\n * whereas real Anthropic interleaves `thinking` and `redacted_thinking` in the\n * order they occurred. The interleaving between the two is not preserved.\n */\nfunction pushRedactedThinkingStreamEvents(\n events: ClaudeSSEEvent[],\n startIndex: number,\n redactedThinking: string[] | undefined,\n): number {\n let blockIndex = startIndex;\n for (const data of redactedThinking ?? []) {\n events.push({\n type: \"content_block_start\",\n index: blockIndex,\n content_block: { type: \"redacted_thinking\", data },\n });\n events.push({\n type: \"content_block_stop\",\n index: blockIndex,\n });\n blockIndex++;\n }\n return blockIndex;\n}\n\nfunction buildClaudeTextStreamEvents(\n content: string,\n model: string,\n chunkSize: number,\n reasoning?: string,\n overrides?: ResponseOverrides,\n reasoningSignature?: string,\n redactedThinking?: string[],\n): ClaudeSSEEvent[] {\n const msgId = overrides?.id ?? generateMessageId();\n const effectiveModel = overrides?.model ?? model;\n // Prefer a recorded real signature when one was captured; otherwise fall back\n // to the round-trip-safe placeholder.\n const signature = reasoningSignature ?? PLACEHOLDER_SIGNATURE;\n const events: ClaudeSSEEvent[] = [];\n\n // message_start\n events.push({\n type: \"message_start\",\n message: {\n id: msgId,\n type: \"message\",\n role: overrides?.role ?? \"assistant\",\n content: [],\n model: effectiveModel,\n stop_reason: null,\n stop_sequence: null,\n usage: claudeUsage(overrides),\n },\n });\n\n let blockIndex = 0;\n\n // Redacted-thinking blocks lead the turn (before any thinking / text) to\n // satisfy the leading-block invariant on replay; see the helper for the\n // ordering caveat.\n blockIndex = pushRedactedThinkingStreamEvents(events, blockIndex, redactedThinking);\n\n // Thinking block (emitted before text when reasoning is present)\n if (reasoning) {\n // Real Anthropic emits an empty `signature` on the thinking\n // `content_block_start`; the cryptographic signature arrives only via the\n // trailing `signature_delta`. Mirror that wire shape here.\n events.push({\n type: \"content_block_start\",\n index: blockIndex,\n content_block: { type: \"thinking\", thinking: \"\", signature: \"\" },\n });\n\n for (let i = 0; i < reasoning.length; i += chunkSize) {\n const slice = reasoning.slice(i, i + chunkSize);\n events.push({\n type: \"content_block_delta\",\n index: blockIndex,\n delta: { type: \"thinking_delta\", thinking: slice },\n });\n }\n\n events.push({\n type: \"content_block_delta\",\n index: blockIndex,\n delta: { type: \"signature_delta\", signature },\n });\n\n events.push({\n type: \"content_block_stop\",\n index: blockIndex,\n });\n\n blockIndex++;\n }\n\n // content_block_start (text)\n events.push({\n type: \"content_block_start\",\n index: blockIndex,\n content_block: { type: \"text\", text: \"\" },\n });\n\n // content_block_delta — text chunks\n for (let i = 0; i < content.length; i += chunkSize) {\n const slice = content.slice(i, i + chunkSize);\n events.push({\n type: \"content_block_delta\",\n index: blockIndex,\n delta: { type: \"text_delta\", text: slice },\n });\n }\n\n // content_block_stop\n events.push({\n type: \"content_block_stop\",\n index: blockIndex,\n });\n\n // message_delta\n events.push({\n type: \"message_delta\",\n delta: {\n stop_reason: claudeStopReason(overrides?.finishReason, \"end_turn\"),\n stop_sequence: null,\n },\n usage: { output_tokens: claudeUsage(overrides).output_tokens },\n });\n\n // message_stop\n events.push({ type: \"message_stop\" });\n\n return events;\n}\n\nfunction buildClaudeToolCallStreamEvents(\n toolCalls: ToolCall[],\n model: string,\n chunkSize: number,\n logger: Logger,\n reasoning?: string,\n overrides?: ResponseOverrides,\n reasoningSignature?: string,\n redactedThinking?: string[],\n): ClaudeSSEEvent[] {\n const msgId = overrides?.id ?? generateMessageId();\n const effectiveModel = overrides?.model ?? model;\n // Prefer a recorded real signature when one was captured; otherwise fall back\n // to the round-trip-safe placeholder.\n const signature = reasoningSignature ?? PLACEHOLDER_SIGNATURE;\n const events: ClaudeSSEEvent[] = [];\n\n // message_start\n events.push({\n type: \"message_start\",\n message: {\n id: msgId,\n type: \"message\",\n role: overrides?.role ?? \"assistant\",\n content: [],\n model: effectiveModel,\n stop_reason: null,\n stop_sequence: null,\n usage: claudeUsage(overrides),\n },\n });\n\n let blockIndex = 0;\n\n // Redacted-thinking blocks lead the turn (before thinking / tool_use blocks)\n // to satisfy the extended-thinking leading-block invariant on replay; see the\n // helper for the ordering caveat.\n blockIndex = pushRedactedThinkingStreamEvents(events, blockIndex, redactedThinking);\n\n // Optional thinking block (emitted before the tool_use blocks when reasoning\n // is present). Mirrors buildClaudeContentWithToolCallsStreamEvents exactly so\n // a pure-tool-call turn under extended thinking emits a leading thinking\n // block — without it, replaying the emitted turn under strict self-trips\n // `missing_thinking_first`.\n if (reasoning) {\n // Real Anthropic emits an empty `signature` on the thinking\n // `content_block_start`; the cryptographic signature arrives only via the\n // trailing `signature_delta`. Mirror that wire shape here.\n events.push({\n type: \"content_block_start\",\n index: blockIndex,\n content_block: { type: \"thinking\", thinking: \"\", signature: \"\" },\n });\n\n for (let i = 0; i < reasoning.length; i += chunkSize) {\n const slice = reasoning.slice(i, i + chunkSize);\n events.push({\n type: \"content_block_delta\",\n index: blockIndex,\n delta: { type: \"thinking_delta\", thinking: slice },\n });\n }\n\n events.push({\n type: \"content_block_delta\",\n index: blockIndex,\n delta: { type: \"signature_delta\", signature },\n });\n\n events.push({\n type: \"content_block_stop\",\n index: blockIndex,\n });\n\n blockIndex++;\n }\n\n for (const tc of toolCalls) {\n const toolUseId = tc.id || generateToolUseId();\n\n // Parse arguments to JSON object (Claude uses objects, not strings)\n let argsObj: unknown;\n try {\n argsObj = JSON.parse(tc.arguments || \"{}\");\n } catch {\n logger.warn(\n `Malformed JSON in fixture tool call arguments for \"${tc.name}\": ${tc.arguments}`,\n );\n argsObj = {};\n }\n const argsJson = JSON.stringify(argsObj);\n\n // content_block_start\n events.push({\n type: \"content_block_start\",\n index: blockIndex,\n content_block: {\n type: \"tool_use\",\n id: toolUseId,\n name: tc.name,\n input: {},\n },\n });\n\n // content_block_delta — input_json_delta chunks\n for (let i = 0; i < argsJson.length; i += chunkSize) {\n const slice = argsJson.slice(i, i + chunkSize);\n events.push({\n type: \"content_block_delta\",\n index: blockIndex,\n delta: { type: \"input_json_delta\", partial_json: slice },\n });\n }\n\n // content_block_stop\n events.push({\n type: \"content_block_stop\",\n index: blockIndex,\n });\n\n blockIndex++;\n }\n\n // message_delta\n events.push({\n type: \"message_delta\",\n delta: {\n stop_reason: claudeStopReason(overrides?.finishReason, \"tool_use\"),\n stop_sequence: null,\n },\n usage: { output_tokens: claudeUsage(overrides).output_tokens },\n });\n\n // message_stop\n events.push({ type: \"message_stop\" });\n\n return events;\n}\n\n// Non-streaming response builders\n\n/**\n * Push faithful Anthropic `redacted_thinking` content blocks (each a\n * `{ type: \"redacted_thinking\", data }`) onto a non-streaming content array, in\n * recorded order. No-op when `redactedThinking` is empty/absent, so no blocks\n * are added.\n *\n * Leading placement keeps the turn round-trip-safe for the extended-thinking\n * leading-block invariant. It does NOT reproduce real Anthropic's block order:\n * aimock joins all plaintext reasoning into one block and emits all redacted\n * blocks ahead of it, whereas real Anthropic interleaves `thinking` and\n * `redacted_thinking` in occurrence order. The interleaving is not preserved.\n */\nfunction pushRedactedThinkingBlocks(\n contentBlocks: object[],\n redactedThinking: string[] | undefined,\n): void {\n for (const data of redactedThinking ?? []) {\n contentBlocks.push({ type: \"redacted_thinking\", data });\n }\n}\n\nfunction buildClaudeTextResponse(\n content: string,\n model: string,\n reasoning?: string,\n overrides?: ResponseOverrides,\n reasoningSignature?: string,\n redactedThinking?: string[],\n): object {\n const contentBlocks: object[] = [];\n\n // Redacted-thinking blocks lead the content array (before thinking / text);\n // see the helper for the ordering caveat.\n pushRedactedThinkingBlocks(contentBlocks, redactedThinking);\n\n if (reasoning) {\n contentBlocks.push({\n type: \"thinking\",\n thinking: reasoning,\n // Prefer a recorded real signature; otherwise the round-trip-safe placeholder.\n signature: reasoningSignature ?? PLACEHOLDER_SIGNATURE,\n });\n }\n\n contentBlocks.push({ type: \"text\", text: content });\n\n return {\n id: overrides?.id ?? generateMessageId(),\n type: \"message\",\n role: overrides?.role ?? \"assistant\",\n content: contentBlocks,\n model: overrides?.model ?? model,\n stop_reason: claudeStopReason(overrides?.finishReason, \"end_turn\"),\n stop_sequence: null,\n usage: claudeUsage(overrides),\n };\n}\n\nfunction buildClaudeToolCallResponse(\n toolCalls: ToolCall[],\n model: string,\n logger: Logger,\n reasoning?: string,\n overrides?: ResponseOverrides,\n reasoningSignature?: string,\n redactedThinking?: string[],\n): object {\n const contentBlocks: object[] = [];\n\n // Redacted-thinking blocks lead the content array (before thinking / tool_use)\n // to satisfy the leading-block invariant on replay; see the helper for the\n // ordering caveat.\n pushRedactedThinkingBlocks(contentBlocks, redactedThinking);\n\n // Leading thinking block when reasoning is present — mirrors\n // buildClaudeContentWithToolCallsResponse so a pure-tool-call turn under\n // extended thinking carries the same leading thinking block.\n if (reasoning) {\n contentBlocks.push({\n type: \"thinking\",\n thinking: reasoning,\n // Prefer a recorded real signature; otherwise the round-trip-safe placeholder.\n signature: reasoningSignature ?? PLACEHOLDER_SIGNATURE,\n });\n }\n\n for (const tc of toolCalls) {\n let argsObj: unknown;\n try {\n argsObj = JSON.parse(tc.arguments || \"{}\");\n } catch {\n logger.warn(\n `Malformed JSON in fixture tool call arguments for \"${tc.name}\": ${tc.arguments}`,\n );\n argsObj = {};\n }\n contentBlocks.push({\n type: \"tool_use\",\n id: tc.id || generateToolUseId(),\n name: tc.name,\n input: argsObj,\n });\n }\n\n return {\n id: overrides?.id ?? generateMessageId(),\n type: \"message\",\n role: overrides?.role ?? \"assistant\",\n content: contentBlocks,\n model: overrides?.model ?? model,\n stop_reason: claudeStopReason(overrides?.finishReason, \"tool_use\"),\n stop_sequence: null,\n usage: claudeUsage(overrides),\n };\n}\n\nfunction buildClaudeContentWithToolCallsStreamEvents(\n content: string,\n toolCalls: ToolCall[],\n model: string,\n chunkSize: number,\n logger: Logger,\n reasoning?: string,\n overrides?: ResponseOverrides,\n reasoningSignature?: string,\n redactedThinking?: string[],\n blocks?: FixtureBlock[],\n): ClaudeSSEEvent[] {\n const msgId = overrides?.id ?? generateMessageId();\n const effectiveModel = overrides?.model ?? model;\n // Prefer a recorded real signature when one was captured; otherwise fall back\n // to the round-trip-safe placeholder.\n const signature = reasoningSignature ?? PLACEHOLDER_SIGNATURE;\n const events: ClaudeSSEEvent[] = [];\n\n // message_start\n events.push({\n type: \"message_start\",\n message: {\n id: msgId,\n type: \"message\",\n role: overrides?.role ?? \"assistant\",\n content: [],\n model: effectiveModel,\n stop_reason: null,\n stop_sequence: null,\n usage: claudeUsage(overrides),\n },\n });\n\n let blockIndex = 0;\n\n // Redacted-thinking blocks lead the turn (before thinking / text / tool_use);\n // see the helper for the ordering caveat. Applies to both the legacy and the\n // ordered-`blocks` paths.\n blockIndex = pushRedactedThinkingStreamEvents(events, blockIndex, redactedThinking);\n\n // Optional thinking block — also shared by both paths.\n if (reasoning) {\n // Real Anthropic emits an empty `signature` on the thinking\n // `content_block_start`; the cryptographic signature arrives only via the\n // trailing `signature_delta`. Mirror that wire shape here.\n events.push({\n type: \"content_block_start\",\n index: blockIndex,\n content_block: { type: \"thinking\", thinking: \"\", signature: \"\" },\n });\n\n for (let i = 0; i < reasoning.length; i += chunkSize) {\n const slice = reasoning.slice(i, i + chunkSize);\n events.push({\n type: \"content_block_delta\",\n index: blockIndex,\n delta: { type: \"thinking_delta\", thinking: slice },\n });\n }\n\n events.push({\n type: \"content_block_delta\",\n index: blockIndex,\n delta: { type: \"signature_delta\", signature },\n });\n\n events.push({\n type: \"content_block_stop\",\n index: blockIndex,\n });\n\n blockIndex++;\n }\n\n if (blocks && blocks.length > 0) {\n // NEW PATH: stream `text`/`tool_use` content blocks in the fixture's array\n // order. Anthropic is fully tool-first capable — a `toolCall` block can take\n // a lower `index` than a `text` block. Content-block indices are assigned in\n // encounter order, continuing from any leading thinking/redacted blocks.\n const ordered = resolveFixtureBlocks(blocks);\n\n for (const block of ordered) {\n if (block.type === \"text\") {\n events.push({\n type: \"content_block_start\",\n index: blockIndex,\n content_block: { type: \"text\", text: \"\" },\n });\n\n for (let i = 0; i < block.text.length; i += chunkSize) {\n const slice = block.text.slice(i, i + chunkSize);\n events.push({\n type: \"content_block_delta\",\n index: blockIndex,\n delta: { type: \"text_delta\", text: slice },\n });\n }\n\n events.push({\n type: \"content_block_stop\",\n index: blockIndex,\n });\n\n blockIndex++;\n } else {\n const toolUseId = block.id || generateToolUseId();\n\n let argsObj: unknown;\n try {\n argsObj = JSON.parse(block.arguments || \"{}\");\n } catch {\n logger.warn(\n `Malformed JSON in fixture tool call arguments for \"${block.name}\": ${block.arguments}`,\n );\n argsObj = {};\n }\n const argsJson = JSON.stringify(argsObj);\n\n events.push({\n type: \"content_block_start\",\n index: blockIndex,\n content_block: {\n type: \"tool_use\",\n id: toolUseId,\n name: block.name,\n input: {},\n },\n });\n\n for (let i = 0; i < argsJson.length; i += chunkSize) {\n const slice = argsJson.slice(i, i + chunkSize);\n events.push({\n type: \"content_block_delta\",\n index: blockIndex,\n delta: { type: \"input_json_delta\", partial_json: slice },\n });\n }\n\n events.push({\n type: \"content_block_stop\",\n index: blockIndex,\n });\n\n blockIndex++;\n }\n }\n\n // message_delta\n events.push({\n type: \"message_delta\",\n delta: {\n stop_reason: claudeStopReason(overrides?.finishReason, \"tool_use\"),\n stop_sequence: null,\n },\n usage: { output_tokens: claudeUsage(overrides).output_tokens },\n });\n\n // message_stop\n events.push({ type: \"message_stop\" });\n\n return events;\n }\n\n // LEGACY PATH (byte-for-byte unchanged): text content block, then tool_use\n // blocks in `toolCalls` order. Reached only when `blocks` is absent; the\n // leading redacted-thinking/thinking blocks above are shared with the new\n // path and produce identical wire output here.\n // Text content block\n events.push({\n type: \"content_block_start\",\n index: blockIndex,\n content_block: { type: \"text\", text: \"\" },\n });\n\n for (let i = 0; i < content.length; i += chunkSize) {\n const slice = content.slice(i, i + chunkSize);\n events.push({\n type: \"content_block_delta\",\n index: blockIndex,\n delta: { type: \"text_delta\", text: slice },\n });\n }\n\n events.push({\n type: \"content_block_stop\",\n index: blockIndex,\n });\n\n blockIndex++;\n\n // Tool use blocks\n for (const tc of toolCalls) {\n const toolUseId = tc.id || generateToolUseId();\n\n let argsObj: unknown;\n try {\n argsObj = JSON.parse(tc.arguments || \"{}\");\n } catch {\n logger.warn(\n `Malformed JSON in fixture tool call arguments for \"${tc.name}\": ${tc.arguments}`,\n );\n argsObj = {};\n }\n const argsJson = JSON.stringify(argsObj);\n\n events.push({\n type: \"content_block_start\",\n index: blockIndex,\n content_block: {\n type: \"tool_use\",\n id: toolUseId,\n name: tc.name,\n input: {},\n },\n });\n\n for (let i = 0; i < argsJson.length; i += chunkSize) {\n const slice = argsJson.slice(i, i + chunkSize);\n events.push({\n type: \"content_block_delta\",\n index: blockIndex,\n delta: { type: \"input_json_delta\", partial_json: slice },\n });\n }\n\n events.push({\n type: \"content_block_stop\",\n index: blockIndex,\n });\n\n blockIndex++;\n }\n\n // message_delta\n events.push({\n type: \"message_delta\",\n delta: {\n stop_reason: claudeStopReason(overrides?.finishReason, \"tool_use\"),\n stop_sequence: null,\n },\n usage: { output_tokens: claudeUsage(overrides).output_tokens },\n });\n\n // message_stop\n events.push({ type: \"message_stop\" });\n\n return events;\n}\n\nfunction buildClaudeContentWithToolCallsResponse(\n content: string,\n toolCalls: ToolCall[],\n model: string,\n logger: Logger,\n reasoning?: string,\n overrides?: ResponseOverrides,\n reasoningSignature?: string,\n redactedThinking?: string[],\n blocks?: FixtureBlock[],\n): object {\n const contentBlocks: object[] = [];\n\n // Redacted-thinking blocks lead the content array (before thinking / text /\n // tool_use); see the helper for the ordering caveat.\n pushRedactedThinkingBlocks(contentBlocks, redactedThinking);\n\n if (reasoning) {\n contentBlocks.push({\n type: \"thinking\",\n thinking: reasoning,\n // Prefer a recorded real signature; otherwise the round-trip-safe placeholder.\n signature: reasoningSignature ?? PLACEHOLDER_SIGNATURE,\n });\n }\n\n // Build a tool_use content block from a fixture tool call, parsing its\n // string `arguments` into the object `input` Anthropic emits (warning on\n // malformed JSON, same idiom as the streaming/legacy paths).\n const toolUseBlock = (tc: { name: string; arguments: string; id?: string }): object => {\n let argsObj: unknown;\n try {\n argsObj = JSON.parse(tc.arguments || \"{}\");\n } catch {\n logger.warn(\n `Malformed JSON in fixture tool call arguments for \"${tc.name}\": ${tc.arguments}`,\n );\n argsObj = {};\n }\n return {\n type: \"tool_use\",\n id: tc.id || generateToolUseId(),\n name: tc.name,\n input: argsObj,\n };\n };\n\n if (blocks && blocks.length > 0) {\n // NEW PATH: the non-streaming `content[]` array is positionally observable,\n // so emit `text`/`tool_use` content blocks in the fixture's ARRAY ORDER\n // (after any leading redacted/thinking blocks). A toolCall block before a\n // text block therefore yields a tool_use ahead of the text — matching the\n // streaming path for the same `blocks` fixture.\n const ordered = resolveFixtureBlocks(blocks);\n for (const block of ordered) {\n if (block.type === \"text\") {\n contentBlocks.push({ type: \"text\", text: block.text });\n } else {\n contentBlocks.push(\n toolUseBlock({ name: block.name, arguments: block.arguments, id: block.id }),\n );\n }\n }\n } else {\n // LEGACY PATH (unchanged): text content block, then tool_use blocks.\n contentBlocks.push({ type: \"text\", text: content });\n for (const tc of toolCalls) {\n contentBlocks.push(toolUseBlock(tc));\n }\n }\n\n return {\n id: overrides?.id ?? generateMessageId(),\n type: \"message\",\n role: overrides?.role ?? \"assistant\",\n content: contentBlocks,\n model: overrides?.model ?? model,\n stop_reason: claudeStopReason(overrides?.finishReason, \"tool_use\"),\n stop_sequence: null,\n usage: claudeUsage(overrides),\n };\n}\n\n// ─── SSE writer for Claude Messages API ─────────────────────────────────────\n\ninterface ClaudeStreamOptions {\n latency?: number;\n streamingProfile?: StreamingProfile;\n recordedTimings?: RecordedTimings;\n replaySpeed?: number;\n signal?: AbortSignal;\n onChunkSent?: () => void;\n}\n\nasync function writeClaudeSSEStream(\n res: http.ServerResponse,\n events: ClaudeSSEEvent[],\n optionsOrLatency?: number | ClaudeStreamOptions,\n): Promise<boolean> {\n const opts: ClaudeStreamOptions =\n typeof optionsOrLatency === \"number\" ? { latency: optionsOrLatency } : (optionsOrLatency ?? {});\n const latency = opts.latency ?? 0;\n const profile = opts.streamingProfile;\n const { recordedTimings, replaySpeed } = opts;\n const signal = opts.signal;\n const onChunkSent = opts.onChunkSent;\n\n if (res.writableEnded) return true;\n res.setHeader(\"Content-Type\", \"text/event-stream\");\n res.setHeader(\"Cache-Control\", \"no-cache\");\n res.setHeader(\"Connection\", \"keep-alive\");\n\n let chunkIndex = 0;\n for (const event of events) {\n const chunkDelay = calculateDelay(chunkIndex, profile, latency, recordedTimings, replaySpeed);\n if (chunkDelay > 0) await delay(chunkDelay, signal);\n if (signal?.aborted) return false;\n if (res.writableEnded) return true;\n res.write(`event: ${event.type}\\ndata: ${JSON.stringify(event)}\\n\\n`);\n onChunkSent?.();\n if (signal?.aborted) return false;\n chunkIndex++;\n }\n\n if (!res.writableEnded) {\n res.end();\n }\n return true;\n}\n\n// ─── Request handler ────────────────────────────────────────────────────────\n\nexport async function handleMessages(\n req: http.IncomingMessage,\n res: http.ServerResponse,\n raw: string,\n fixtures: Fixture[],\n journal: Journal,\n defaults: HandlerDefaults,\n setCorsHeaders: (res: http.ServerResponse) => void,\n): Promise<void> {\n const { logger } = defaults;\n setCorsHeaders(res);\n\n let claudeReq: ClaudeRequest;\n try {\n claudeReq = JSON.parse(raw) as ClaudeRequest;\n } catch (parseErr) {\n const detail = parseErr instanceof Error ? parseErr.message : \"unknown\";\n journal.add({\n method: req.method ?? \"POST\",\n path: req.url ?? \"/v1/messages\",\n headers: flattenHeaders(req.headers),\n body: null,\n response: { status: 400, fixture: null },\n });\n writeErrorResponse(\n res,\n 400,\n JSON.stringify({\n error: {\n message: `Malformed JSON: ${detail}`,\n type: \"invalid_request_error\",\n },\n }),\n );\n return;\n }\n\n // Extended-thinking invariant validation. The validator runs whenever\n // thinking is enabled (it self-short-circuits to null otherwise). On a\n // detected violation: strict ON → 400, strict OFF → warn + replay. Mirrors\n // the real Anthropic API, which 400s on these.\n const thinkingViolation = validateThinkingInvariants(claudeReq);\n if (thinkingViolation) {\n const effectiveStrict = resolveStrictMode(defaults.strict, req.headers);\n const violationMessage = thinkingViolationMessage(thinkingViolation);\n if (effectiveStrict) {\n logger.error(`THINKING: ${violationMessage}`);\n journal.add({\n method: req.method ?? \"POST\",\n path: req.url ?? \"/v1/messages\",\n headers: flattenHeaders(req.headers),\n body: null,\n response: {\n status: 400,\n fixture: null,\n ...strictOverrideField(defaults.strict, req.headers),\n },\n });\n writeErrorResponse(\n res,\n 400,\n JSON.stringify({\n type: \"error\",\n error: {\n type: \"invalid_request_error\",\n message: violationMessage,\n },\n }),\n );\n return;\n }\n logger.warn(`THINKING: ${violationMessage} (strict off — replaying anyway)`);\n // Fall through to existing match/replay behavior.\n }\n\n // Convert to ChatCompletionRequest for fixture matching\n const completionReq = claudeToCompletionRequest(claudeReq);\n completionReq._context = getContext(req);\n\n const testId = getTestId(req);\n const { fixture, skippedBySequenceOrTurn } = matchFixtureDiagnostic(\n fixtures,\n completionReq,\n journal.getFixtureMatchCountsForTest(testId),\n defaults.requestTransform,\n // Record mode proxies on a miss to capture a fresh turn (see record gate\n // below), so keep turnIndex strict to prevent an earlier-turn fixture from\n // shadowing a longer request and skipping the new turn's recording.\n recordMatchOptions(!!defaults.record, defaults.logger),\n );\n\n if (fixture) {\n journal.incrementFixtureMatchCount(fixture, fixtures, testId);\n logger.debug(`Fixture matched: ${JSON.stringify(fixture.match).slice(0, 120)}`);\n } else {\n const lastUserMsg = completionReq.messages.filter((m) => m.role === \"user\").pop();\n const snippet =\n typeof lastUserMsg?.content === \"string\" ? lastUserMsg.content.slice(0, 80) : \"\";\n logger.debug(\n `No fixture matched for request (model=${completionReq.model ?? \"?\"}, msg=\"${snippet}\")`,\n );\n }\n\n if (\n applyChaos(\n res,\n fixture,\n defaults.chaos,\n req.headers,\n journal,\n {\n method: req.method ?? \"POST\",\n path: req.url ?? \"/v1/messages\",\n headers: flattenHeaders(req.headers),\n body: completionReq,\n },\n fixture ? \"fixture\" : \"proxy\",\n defaults.registry,\n defaults.logger,\n )\n )\n return;\n\n if (!fixture) {\n const effectiveStrict = resolveStrictMode(defaults.strict, req.headers);\n if (effectiveStrict) {\n const strictStatus = 503;\n const strictMessage = strictNoMatchMessage(skippedBySequenceOrTurn);\n logger.error(\n strictNoMatchLogLine(\n req.method ?? \"POST\",\n req.url ?? \"/v1/messages\",\n skippedBySequenceOrTurn,\n ),\n );\n journal.add({\n method: req.method ?? \"POST\",\n path: req.url ?? \"/v1/messages\",\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: {\n status: strictStatus,\n fixture: null,\n ...strictOverrideField(defaults.strict, req.headers),\n },\n });\n writeErrorResponse(\n res,\n strictStatus,\n JSON.stringify({\n error: {\n message: strictMessage,\n type: \"invalid_request_error\",\n },\n }),\n );\n return;\n }\n if (defaults.record) {\n const outcome = await proxyAndRecord(\n req,\n res,\n completionReq,\n \"anthropic\",\n req.url ?? \"/v1/messages\",\n fixtures,\n defaults,\n raw,\n );\n if (outcome === \"handled_by_hook\") return;\n if (outcome !== \"not_configured\") {\n journal.add({\n method: req.method ?? \"POST\",\n path: req.url ?? \"/v1/messages\",\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: { status: res.statusCode ?? 200, fixture: null, source: \"proxy\" },\n });\n return;\n }\n }\n journal.add({\n method: req.method ?? \"POST\",\n path: req.url ?? \"/v1/messages\",\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: {\n status: 404,\n fixture: null,\n ...strictOverrideField(defaults.strict, req.headers),\n },\n });\n writeErrorResponse(\n res,\n 404,\n JSON.stringify({\n error: {\n message: \"No fixture matched\",\n type: \"invalid_request_error\",\n },\n }),\n );\n return;\n }\n\n const response = await resolveResponse(fixture, completionReq);\n const latency = fixture.latency ?? defaults.latency;\n const chunkSize = Math.max(1, fixture.chunkSize ?? defaults.chunkSize);\n\n // Error response\n if (isErrorResponse(response)) {\n const status = response.status ?? 500;\n journal.add({\n method: req.method ?? \"POST\",\n path: req.url ?? \"/v1/messages\",\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: { status, fixture },\n });\n // Anthropic-style error format: { type: \"error\", error: { type, message } }\n const anthropicError = {\n type: \"error\",\n error: {\n type: response.error.type ?? \"api_error\",\n message: response.error.message,\n },\n };\n writeErrorResponse(res, status, JSON.stringify(anthropicError), {\n retryAfter: response.retryAfter,\n });\n return;\n }\n\n // Content + tool calls response (must be checked before text/tool-only branches)\n if (isContentWithToolCallsResponse(response)) {\n if (response.webSearches?.length) {\n logger.warn(\n \"webSearches in fixture response are not supported for Claude Messages API — ignoring\",\n );\n }\n const overrides = extractOverrides(response);\n const effectiveStrict = resolveStrictMode(defaults.strict, req.headers);\n const effReasoning = resolveReasoningForModel(\n response.reasoning,\n completionReq.model,\n effectiveStrict,\n defaults.logger,\n );\n const { reasoningSignature: effReasoningSignature, redactedThinking: effRedactedThinking } =\n resolveReasoningArtifactsForModel(\n response.reasoningSignature,\n response.redactedThinking,\n completionReq.model,\n effectiveStrict,\n defaults.logger,\n );\n const journalEntry = journal.add({\n method: req.method ?? \"POST\",\n path: req.url ?? \"/v1/messages\",\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: { status: 200, fixture },\n });\n if (claudeReq.stream !== true) {\n const body = buildClaudeContentWithToolCallsResponse(\n response.content ?? \"\",\n response.toolCalls ?? [],\n completionReq.model,\n logger,\n effReasoning,\n overrides,\n effReasoningSignature,\n effRedactedThinking,\n response.blocks,\n );\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify(body));\n } else {\n const events = buildClaudeContentWithToolCallsStreamEvents(\n response.content ?? \"\",\n response.toolCalls ?? [],\n completionReq.model,\n chunkSize,\n logger,\n effReasoning,\n overrides,\n effReasoningSignature,\n effRedactedThinking,\n response.blocks,\n );\n const interruption = createInterruptionSignal(fixture);\n const completed = await writeClaudeSSEStream(res, events, {\n latency,\n streamingProfile: fixture.streamingProfile,\n recordedTimings: fixture.recordedTimings,\n replaySpeed: fixture.replaySpeed ?? defaults.replaySpeed,\n signal: interruption?.signal,\n onChunkSent: interruption?.tick,\n });\n if (!completed) {\n if (!res.writableEnded) res.destroy();\n journalEntry.response.interrupted = true;\n journalEntry.response.interruptReason = interruption?.reason();\n }\n interruption?.cleanup();\n }\n return;\n }\n\n // Text response\n if (isTextResponse(response)) {\n if (response.webSearches?.length) {\n logger.warn(\n \"webSearches in fixture response are not supported for Claude Messages API — ignoring\",\n );\n }\n const overrides = extractOverrides(response);\n const effectiveStrict = resolveStrictMode(defaults.strict, req.headers);\n const effReasoning = resolveReasoningForModel(\n response.reasoning,\n completionReq.model,\n effectiveStrict,\n defaults.logger,\n );\n const { reasoningSignature: effReasoningSignature, redactedThinking: effRedactedThinking } =\n resolveReasoningArtifactsForModel(\n response.reasoningSignature,\n response.redactedThinking,\n completionReq.model,\n effectiveStrict,\n defaults.logger,\n );\n const journalEntry = journal.add({\n method: req.method ?? \"POST\",\n path: req.url ?? \"/v1/messages\",\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: { status: 200, fixture },\n });\n if (claudeReq.stream !== true) {\n const body = buildClaudeTextResponse(\n response.content,\n completionReq.model,\n effReasoning,\n overrides,\n effReasoningSignature,\n effRedactedThinking,\n );\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify(body));\n } else {\n const events = buildClaudeTextStreamEvents(\n response.content,\n completionReq.model,\n chunkSize,\n effReasoning,\n overrides,\n effReasoningSignature,\n effRedactedThinking,\n );\n const interruption = createInterruptionSignal(fixture);\n const completed = await writeClaudeSSEStream(res, events, {\n latency,\n streamingProfile: fixture.streamingProfile,\n recordedTimings: fixture.recordedTimings,\n replaySpeed: fixture.replaySpeed ?? defaults.replaySpeed,\n signal: interruption?.signal,\n onChunkSent: interruption?.tick,\n });\n if (!completed) {\n if (!res.writableEnded) res.destroy();\n journalEntry.response.interrupted = true;\n journalEntry.response.interruptReason = interruption?.reason();\n }\n interruption?.cleanup();\n }\n return;\n }\n\n // Tool call response\n if (isToolCallResponse(response)) {\n if (response.webSearches?.length) {\n logger.warn(\n \"webSearches in fixture response are not supported for Claude Messages API — ignoring\",\n );\n }\n const overrides = extractOverrides(response);\n const effectiveStrict = resolveStrictMode(defaults.strict, req.headers);\n const effReasoning = resolveReasoningForModel(\n response.reasoning,\n completionReq.model,\n effectiveStrict,\n defaults.logger,\n );\n const { reasoningSignature: effReasoningSignature, redactedThinking: effRedactedThinking } =\n resolveReasoningArtifactsForModel(\n response.reasoningSignature,\n response.redactedThinking,\n completionReq.model,\n effectiveStrict,\n defaults.logger,\n );\n const journalEntry = journal.add({\n method: req.method ?? \"POST\",\n path: req.url ?? \"/v1/messages\",\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: { status: 200, fixture },\n });\n if (claudeReq.stream !== true) {\n const body = buildClaudeToolCallResponse(\n response.toolCalls,\n completionReq.model,\n logger,\n effReasoning,\n overrides,\n effReasoningSignature,\n effRedactedThinking,\n );\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify(body));\n } else {\n const events = buildClaudeToolCallStreamEvents(\n response.toolCalls,\n completionReq.model,\n chunkSize,\n logger,\n effReasoning,\n overrides,\n effReasoningSignature,\n effRedactedThinking,\n );\n const interruption = createInterruptionSignal(fixture);\n const completed = await writeClaudeSSEStream(res, events, {\n latency,\n streamingProfile: fixture.streamingProfile,\n recordedTimings: fixture.recordedTimings,\n replaySpeed: fixture.replaySpeed ?? defaults.replaySpeed,\n signal: interruption?.signal,\n onChunkSent: interruption?.tick,\n });\n if (!completed) {\n if (!res.writableEnded) res.destroy();\n journalEntry.response.interrupted = true;\n journalEntry.response.interruptReason = interruption?.reason();\n }\n interruption?.cleanup();\n }\n return;\n }\n\n // Unknown response type\n journal.add({\n method: req.method ?? \"POST\",\n path: req.url ?? \"/v1/messages\",\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: { status: 500, fixture },\n });\n writeErrorResponse(\n res,\n 500,\n JSON.stringify({\n error: {\n message: \"Fixture response did not match any known type\",\n type: \"server_error\",\n },\n }),\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"messages.js","names":[],"sources":["../src/messages.ts"],"sourcesContent":["/**\n * Anthropic Claude Messages API support.\n *\n * Translates incoming /v1/messages requests into the ChatCompletionRequest\n * format used by the fixture router, and converts fixture responses back into\n * the Claude Messages API streaming (or non-streaming) format.\n */\n\nimport type * as http from \"node:http\";\nimport type {\n ChatCompletionRequest,\n ChatMessage,\n Fixture,\n HandlerDefaults,\n RecordedTimings,\n FixtureBlock,\n ResponseOverrides,\n StreamingProfile,\n ToolCall,\n ToolDefinition,\n} from \"./types.js\";\nimport {\n generateMessageId,\n generateToolUseId,\n extractOverrides,\n isTextResponse,\n isToolCallResponse,\n isContentWithToolCallsResponse,\n isErrorResponse,\n resolveFixtureBlocks,\n flattenHeaders,\n isJsonObject,\n getTestId,\n resolveResponse,\n resolveStrictMode,\n resolveReasoningForModel,\n resolveReasoningArtifactsForModel,\n strictOverrideField,\n getContext,\n strictNoMatchMessage,\n strictNoMatchLogLine,\n} from \"./helpers.js\";\nimport { matchFixtureDiagnostic, recordMatchOptions } from \"./router.js\";\nimport { writeErrorResponse, delay, calculateDelay } from \"./sse-writer.js\";\nimport { createInterruptionSignal } from \"./interruption.js\";\nimport type { Journal } from \"./journal.js\";\nimport type { Logger } from \"./logger.js\";\nimport { applyChaos } from \"./chaos.js\";\nimport { proxyAndRecord } from \"./recorder.js\";\n\n/**\n * Non-empty placeholder signature written into emitted `thinking` blocks.\n *\n * The real Anthropic signature is a cryptographic value aimock cannot\n * reproduce, but extended-thinking invariant (b) requires a non-empty\n * `signature` on the leading thinking block of a tool-loop continuation turn.\n * Emitting \"\" would make a record→replay round-trip of an aimock thinking turn\n * self-trip that invariant under strict mode. A non-empty placeholder keeps\n * round-trips green; the invariant only checks for non-emptiness, not value.\n */\nconst PLACEHOLDER_SIGNATURE = \"aimock-placeholder-signature\";\n\n// ─── Claude Messages API request types ──────────────────────────────────────\n\ninterface ClaudeContentBlock {\n type:\n | \"text\"\n | \"tool_use\"\n | \"tool_result\"\n | \"image\"\n | \"document\"\n | \"thinking\"\n | \"redacted_thinking\";\n text?: string;\n id?: string;\n name?: string;\n input?: unknown;\n tool_use_id?: string;\n content?: string | ClaudeContentBlock[];\n is_error?: boolean;\n // Extended-thinking fields (Anthropic): `thinking` blocks carry the reasoning\n // text plus a cryptographic `signature`; `redacted_thinking` blocks carry an\n // opaque `data` payload instead.\n thinking?: string;\n signature?: string;\n data?: string;\n}\n\ninterface ClaudeMessage {\n role: \"user\" | \"assistant\";\n content: string | ClaudeContentBlock[];\n}\n\ninterface ClaudeToolDef {\n name: string;\n description?: string;\n input_schema?: object;\n}\n\ninterface ClaudeRequest {\n model: string;\n messages: ClaudeMessage[];\n system?: string | ClaudeContentBlock[];\n tools?: ClaudeToolDef[];\n tool_choice?: unknown;\n stream?: boolean;\n max_tokens: number;\n temperature?: number;\n // Extended-thinking config. Explicitly modeled so it is no longer swallowed\n // by the index signature below; read defensively (may be a non-object).\n thinking?: { type?: \"enabled\" | \"disabled\"; budget_tokens?: number };\n [key: string]: unknown;\n}\n\n// ─── Extended-thinking request invariants (Anthropic) ───────────────────────\n\n/**\n * A detected violation of the Anthropic extended-thinking request invariants on\n * a tool-loop continuation turn. `kind` distinguishes the three failure modes\n * so callers can template a per-kind error message and tests can assert without\n * string matching.\n */\ninterface ThinkingViolation {\n kind: \"missing_thinking_first\" | \"missing_signature\" | \"dropped_redacted_thinking\";\n messageIndex: number;\n observedFirstBlockType?: string;\n}\n\n// ─── Input conversion: Claude → ChatCompletions messages ────────────────────\n\nfunction extractClaudeTextContent(content: string | ClaudeContentBlock[]): string {\n if (typeof content === \"string\") return content;\n if (!Array.isArray(content)) return \"\";\n return content\n .filter((b) => b != null && typeof b === \"object\" && b.type === \"text\")\n .map((b) => b.text ?? \"\")\n .join(\"\");\n}\n\nexport function claudeToCompletionRequest(req: ClaudeRequest): ChatCompletionRequest {\n const messages: ChatMessage[] = [];\n\n // system field → system message\n if (req.system) {\n const systemText =\n typeof req.system === \"string\"\n ? req.system\n : Array.isArray(req.system)\n ? req.system\n .filter((b) => b != null && typeof b === \"object\" && b.type === \"text\")\n .map((b) => b.text ?? \"\")\n .join(\"\")\n : \"\";\n if (systemText) {\n messages.push({ role: \"system\", content: systemText });\n }\n }\n\n const reqMessages = Array.isArray(req.messages) ? req.messages : [];\n for (const msg of reqMessages) {\n // `req.messages` is untrusted JSON; entries may be null / non-object.\n if (!msg || typeof msg !== \"object\") continue;\n if (msg.role === \"user\") {\n // Check for tool_result blocks\n if (typeof msg.content !== \"string\" && Array.isArray(msg.content)) {\n // Content blocks are equally untrusted; skip null / non-object entries.\n const blocks = msg.content.filter((b) => b != null && typeof b === \"object\");\n const toolResults = blocks.filter((b) => b.type === \"tool_result\");\n const textBlocks = blocks.filter((b) => b.type === \"text\");\n\n if (toolResults.length > 0) {\n // Anthropic bundles a leg-2 tool_result and any accompanying user text\n // into ONE user-role message. We emit the accompanying text FIRST, then\n // the tool message(s), so the normalized shape is\n // `[..., user(text), tool]` — the tool message trails the last user\n // message and the current-turn `hasToolResult` predicate\n // ({@link currentTurnHasToolResult}) correctly classifies it `true`\n // (this IS a leg-2 turn that carries a tool result). Emitting the tool\n // FIRST would produce `[..., tool, user(text)]`, which is byte-identical\n // to a genuine turn-N leg-1 (a fresh user question after a completed\n // tool turn) and would be misclassified `false`. The source-message\n // grouping — that this text and this tool_result came from the SAME\n // Anthropic message — is only available here, so this ordering is the\n // only place the two cases can be told apart. The common\n // tool_result-only follow-up (no text blocks) is unaffected.\n if (textBlocks.length > 0) {\n messages.push({\n role: \"user\",\n content: textBlocks.map((b) => b.text ?? \"\").join(\"\"),\n });\n }\n // Each tool_result → tool message\n for (const tr of toolResults) {\n const resultContent =\n typeof tr.content === \"string\"\n ? tr.content\n : Array.isArray(tr.content)\n ? tr.content\n .filter((b) => b != null && typeof b === \"object\" && b.type === \"text\")\n .map((b) => b.text ?? \"\")\n .join(\"\")\n : \"\";\n messages.push({\n role: \"tool\",\n content: resultContent,\n tool_call_id: tr.tool_use_id,\n });\n }\n continue;\n }\n }\n // Regular user message\n messages.push({\n role: \"user\",\n content: extractClaudeTextContent(msg.content),\n });\n } else if (msg.role === \"assistant\") {\n if (typeof msg.content === \"string\") {\n messages.push({ role: \"assistant\", content: msg.content });\n } else if (Array.isArray(msg.content)) {\n const toolUseBlocks = msg.content.filter(\n (b) => b != null && typeof b === \"object\" && b.type === \"tool_use\",\n );\n // Only `text` blocks feed fixture matching; `thinking` /\n // `redacted_thinking` blocks are intentionally excluded from the\n // matchable content.\n const textContent = extractClaudeTextContent(msg.content);\n\n if (toolUseBlocks.length > 0) {\n messages.push({\n role: \"assistant\",\n content: textContent || null,\n tool_calls: toolUseBlocks.map((b) => ({\n id: b.id ?? generateToolUseId(),\n type: \"function\" as const,\n function: {\n name: b.name ?? \"\",\n arguments: typeof b.input === \"string\" ? b.input : JSON.stringify(b.input ?? {}),\n },\n })),\n });\n } else {\n messages.push({ role: \"assistant\", content: textContent || null });\n }\n } else {\n // null/undefined content — tool-only assistant turn\n messages.push({ role: \"assistant\", content: null });\n }\n }\n }\n\n // Convert tools\n let tools: ToolDefinition[] | undefined;\n if (req.tools && req.tools.length > 0) {\n tools = req.tools.map((t) => ({\n type: \"function\" as const,\n function: {\n name: t.name,\n description: t.description,\n parameters: t.input_schema,\n },\n }));\n }\n\n return {\n model: req.model,\n messages,\n stream: req.stream,\n temperature: req.temperature,\n max_tokens: req.max_tokens,\n tools,\n _endpointType: \"chat\",\n };\n}\n\n// ─── Extended-thinking invariant validation ─────────────────────────────────\n\n/** True iff `req.thinking` is an object with `type === \"enabled\"`. */\nfunction isThinkingEnabled(req: ClaudeRequest): boolean {\n const t = req.thinking;\n return typeof t === \"object\" && t !== null && (t as { type?: unknown }).type === \"enabled\";\n}\n\n/**\n * True iff the user turn at `req.messages[userIndex]` carries a `tool_result`\n * referencing one of `toolUseIds` — i.e. it answers the preceding assistant\n * turn's `tool_use` block(s), making that assistant turn a tool-loop\n * continuation point.\n */\nfunction userTurnAnswersToolUse(msg: ClaudeMessage | undefined, toolUseIds: Set<string>): boolean {\n if (!msg || msg.role !== \"user\" || !Array.isArray(msg.content)) return false;\n return msg.content.some(\n (b) =>\n b != null &&\n typeof b === \"object\" &&\n b.type === \"tool_result\" &&\n typeof b.tool_use_id === \"string\" &&\n toolUseIds.has(b.tool_use_id),\n );\n}\n\n/**\n * Validate the Anthropic extended-thinking request invariants on tool-loop\n * continuation turns. Pure: returns the first detected `ThinkingViolation` or\n * `null` when thinking is disabled or every in-scope turn is well-formed.\n *\n * Scope: an assistant turn is in-scope only when it (a) is array content\n * carrying at least one `tool_use` block and (b) is followed by a matching\n * `tool_result` on the next user turn. Text-only / `end_turn` turns, string or\n * empty turns, and trailing unanswered `tool_use` turns are all exempt.\n *\n * Scope detection assumes well-formed Anthropic transcripts: `tool_use` ids are\n * unique, and a tool_use turn is answered by the immediately-following user\n * turn's `tool_result` (adjacency). Malformed shapes — non-adjacent answers,\n * idless `tool_use` blocks, or a `tool_result` separated from its `tool_use` by\n * intervening turns — are intentionally treated as out-of-scope (and therefore\n * not 400'd) rather than validated, since the real Anthropic API would never\n * have produced them.\n */\nexport function validateThinkingInvariants(req: ClaudeRequest): ThinkingViolation | null {\n if (!isThinkingEnabled(req)) return null;\n\n const messages = Array.isArray(req.messages) ? req.messages : [];\n for (let i = 0; i < messages.length; i++) {\n const msg = messages[i];\n // `req.messages` is untrusted JSON; entries may be null / non-object.\n if (!msg || typeof msg !== \"object\") continue;\n if (msg.role !== \"assistant\" || !Array.isArray(msg.content) || msg.content.length === 0) {\n continue;\n }\n\n const toolUseIds = new Set<string>();\n for (const b of msg.content) {\n // Content blocks are equally untrusted; skip null / non-object entries.\n if (!b || typeof b !== \"object\") continue;\n if (b.type === \"tool_use\" && typeof b.id === \"string\") toolUseIds.add(b.id);\n }\n // Not a tool_use-bearing turn → not a reasoning-bearing continuation turn.\n if (toolUseIds.size === 0) continue;\n // Trailing/unanswered tool_use (no matching tool_result follows) → out of scope.\n if (!userTurnAnswersToolUse(messages[i + 1], toolUseIds)) continue;\n\n const first = msg.content[0];\n\n // A null / non-object leading block cannot be a thinking block → it\n // violates invariant (a) just as a wrong-typed block would.\n if (!first || typeof first !== \"object\") {\n return {\n kind: \"missing_thinking_first\",\n messageIndex: i,\n observedFirstBlockType: undefined,\n };\n }\n\n // (a) The in-scope turn must lead with a thinking / redacted_thinking block.\n if (first.type !== \"thinking\" && first.type !== \"redacted_thinking\") {\n return {\n kind: \"missing_thinking_first\",\n messageIndex: i,\n observedFirstBlockType: first.type,\n };\n }\n\n // (b) A leading `thinking` block must carry a non-empty string `signature`.\n if (first.type === \"thinking\") {\n if (typeof first.signature !== \"string\" || first.signature.length === 0) {\n return { kind: \"missing_signature\", messageIndex: i };\n }\n }\n\n // (c) A leading `redacted_thinking` block must preserve a non-empty `data`.\n if (first.type === \"redacted_thinking\") {\n if (typeof first.data !== \"string\" || first.data.length === 0) {\n return { kind: \"dropped_redacted_thinking\", messageIndex: i };\n }\n }\n }\n\n return null;\n}\n\n/** Render the Anthropic-shaped 400 error message for a thinking violation. */\nfunction thinkingViolationMessage(v: ThinkingViolation): string {\n const prefix = `messages.${v.messageIndex}.content.0`;\n switch (v.kind) {\n case \"missing_thinking_first\":\n return `${prefix}: when \\`thinking\\` is enabled, a tool-loop continuation assistant turn must begin with a \\`thinking\\` block; got \\`${v.observedFirstBlockType ?? \"unknown\"}\\`.`;\n case \"missing_signature\":\n return `${prefix}: the leading \\`thinking\\` block is missing a non-empty \\`signature\\`.`;\n case \"dropped_redacted_thinking\":\n return `${prefix}: the leading \\`redacted_thinking\\` block must preserve its \\`data\\`.`;\n }\n}\n\n// ─── Response building: fixture → Claude Messages API format ────────────────\n\nfunction claudeStopReason(finishReason: string | undefined, defaultReason: string): string {\n if (!finishReason) return defaultReason;\n if (finishReason === \"stop\") return \"end_turn\";\n if (finishReason === \"tool_calls\") return \"tool_use\";\n if (finishReason === \"length\") return \"max_tokens\";\n return finishReason;\n}\n\nfunction claudeUsage(overrides?: ResponseOverrides): {\n input_tokens: number;\n output_tokens: number;\n} {\n if (!overrides?.usage) return { input_tokens: 0, output_tokens: 0 };\n return {\n input_tokens: overrides.usage.input_tokens ?? overrides.usage.prompt_tokens ?? 0,\n output_tokens: overrides.usage.output_tokens ?? overrides.usage.completion_tokens ?? 0,\n };\n}\n\ninterface ClaudeSSEEvent {\n type: string;\n [key: string]: unknown;\n}\n\n/**\n * Emit faithful Anthropic `redacted_thinking` content-block events at the head\n * of a streamed turn — one `content_block_start` (carrying the opaque `data`)\n * and `content_block_stop` per recorded block, with NO deltas (real Anthropic\n * delivers the encrypted reasoning entirely on the start event). Returns the\n * next free block index. When `redactedThinking` is empty/absent this is a\n * no-op, so no events are added.\n *\n * Leading placement keeps the turn round-trip-safe for the extended-thinking\n * leading-block invariant (a thinking / redacted_thinking block must come\n * first). It does NOT reproduce real Anthropic's stream order: aimock joins all\n * plaintext reasoning into one block and emits all redacted blocks ahead of it,\n * whereas real Anthropic interleaves `thinking` and `redacted_thinking` in the\n * order they occurred. The interleaving between the two is not preserved.\n */\nfunction pushRedactedThinkingStreamEvents(\n events: ClaudeSSEEvent[],\n startIndex: number,\n redactedThinking: string[] | undefined,\n): number {\n let blockIndex = startIndex;\n for (const data of redactedThinking ?? []) {\n events.push({\n type: \"content_block_start\",\n index: blockIndex,\n content_block: { type: \"redacted_thinking\", data },\n });\n events.push({\n type: \"content_block_stop\",\n index: blockIndex,\n });\n blockIndex++;\n }\n return blockIndex;\n}\n\nfunction buildClaudeTextStreamEvents(\n content: string,\n model: string,\n chunkSize: number,\n reasoning?: string,\n overrides?: ResponseOverrides,\n reasoningSignature?: string,\n redactedThinking?: string[],\n): ClaudeSSEEvent[] {\n const msgId = overrides?.id ?? generateMessageId();\n const effectiveModel = overrides?.model ?? model;\n // Prefer a recorded real signature when one was captured; otherwise fall back\n // to the round-trip-safe placeholder.\n const signature = reasoningSignature ?? PLACEHOLDER_SIGNATURE;\n const events: ClaudeSSEEvent[] = [];\n\n // message_start\n events.push({\n type: \"message_start\",\n message: {\n id: msgId,\n type: \"message\",\n role: overrides?.role ?? \"assistant\",\n content: [],\n model: effectiveModel,\n stop_reason: null,\n stop_sequence: null,\n usage: claudeUsage(overrides),\n },\n });\n\n let blockIndex = 0;\n\n // Redacted-thinking blocks lead the turn (before any thinking / text) to\n // satisfy the leading-block invariant on replay; see the helper for the\n // ordering caveat.\n blockIndex = pushRedactedThinkingStreamEvents(events, blockIndex, redactedThinking);\n\n // Thinking block (emitted before text when reasoning is present)\n if (reasoning) {\n // Real Anthropic emits an empty `signature` on the thinking\n // `content_block_start`; the cryptographic signature arrives only via the\n // trailing `signature_delta`. Mirror that wire shape here.\n events.push({\n type: \"content_block_start\",\n index: blockIndex,\n content_block: { type: \"thinking\", thinking: \"\", signature: \"\" },\n });\n\n for (let i = 0; i < reasoning.length; i += chunkSize) {\n const slice = reasoning.slice(i, i + chunkSize);\n events.push({\n type: \"content_block_delta\",\n index: blockIndex,\n delta: { type: \"thinking_delta\", thinking: slice },\n });\n }\n\n events.push({\n type: \"content_block_delta\",\n index: blockIndex,\n delta: { type: \"signature_delta\", signature },\n });\n\n events.push({\n type: \"content_block_stop\",\n index: blockIndex,\n });\n\n blockIndex++;\n }\n\n // content_block_start (text)\n events.push({\n type: \"content_block_start\",\n index: blockIndex,\n content_block: { type: \"text\", text: \"\" },\n });\n\n // content_block_delta — text chunks\n for (let i = 0; i < content.length; i += chunkSize) {\n const slice = content.slice(i, i + chunkSize);\n events.push({\n type: \"content_block_delta\",\n index: blockIndex,\n delta: { type: \"text_delta\", text: slice },\n });\n }\n\n // content_block_stop\n events.push({\n type: \"content_block_stop\",\n index: blockIndex,\n });\n\n // message_delta\n events.push({\n type: \"message_delta\",\n delta: {\n stop_reason: claudeStopReason(overrides?.finishReason, \"end_turn\"),\n stop_sequence: null,\n },\n usage: { output_tokens: claudeUsage(overrides).output_tokens },\n });\n\n // message_stop\n events.push({ type: \"message_stop\" });\n\n return events;\n}\n\nfunction buildClaudeToolCallStreamEvents(\n toolCalls: ToolCall[],\n model: string,\n chunkSize: number,\n logger: Logger,\n reasoning?: string,\n overrides?: ResponseOverrides,\n reasoningSignature?: string,\n redactedThinking?: string[],\n): ClaudeSSEEvent[] {\n const msgId = overrides?.id ?? generateMessageId();\n const effectiveModel = overrides?.model ?? model;\n // Prefer a recorded real signature when one was captured; otherwise fall back\n // to the round-trip-safe placeholder.\n const signature = reasoningSignature ?? PLACEHOLDER_SIGNATURE;\n const events: ClaudeSSEEvent[] = [];\n\n // message_start\n events.push({\n type: \"message_start\",\n message: {\n id: msgId,\n type: \"message\",\n role: overrides?.role ?? \"assistant\",\n content: [],\n model: effectiveModel,\n stop_reason: null,\n stop_sequence: null,\n usage: claudeUsage(overrides),\n },\n });\n\n let blockIndex = 0;\n\n // Redacted-thinking blocks lead the turn (before thinking / tool_use blocks)\n // to satisfy the extended-thinking leading-block invariant on replay; see the\n // helper for the ordering caveat.\n blockIndex = pushRedactedThinkingStreamEvents(events, blockIndex, redactedThinking);\n\n // Optional thinking block (emitted before the tool_use blocks when reasoning\n // is present). Mirrors buildClaudeContentWithToolCallsStreamEvents exactly so\n // a pure-tool-call turn under extended thinking emits a leading thinking\n // block — without it, replaying the emitted turn under strict self-trips\n // `missing_thinking_first`.\n if (reasoning) {\n // Real Anthropic emits an empty `signature` on the thinking\n // `content_block_start`; the cryptographic signature arrives only via the\n // trailing `signature_delta`. Mirror that wire shape here.\n events.push({\n type: \"content_block_start\",\n index: blockIndex,\n content_block: { type: \"thinking\", thinking: \"\", signature: \"\" },\n });\n\n for (let i = 0; i < reasoning.length; i += chunkSize) {\n const slice = reasoning.slice(i, i + chunkSize);\n events.push({\n type: \"content_block_delta\",\n index: blockIndex,\n delta: { type: \"thinking_delta\", thinking: slice },\n });\n }\n\n events.push({\n type: \"content_block_delta\",\n index: blockIndex,\n delta: { type: \"signature_delta\", signature },\n });\n\n events.push({\n type: \"content_block_stop\",\n index: blockIndex,\n });\n\n blockIndex++;\n }\n\n for (const tc of toolCalls) {\n const toolUseId = tc.id || generateToolUseId();\n\n // Parse arguments to JSON object (Claude uses objects, not strings)\n let argsObj: unknown;\n try {\n argsObj = JSON.parse(tc.arguments || \"{}\");\n } catch {\n logger.warn(\n `Malformed JSON in fixture tool call arguments for \"${tc.name}\": ${tc.arguments}`,\n );\n argsObj = {};\n }\n const argsJson = JSON.stringify(argsObj);\n\n // content_block_start\n events.push({\n type: \"content_block_start\",\n index: blockIndex,\n content_block: {\n type: \"tool_use\",\n id: toolUseId,\n name: tc.name,\n input: {},\n },\n });\n\n // content_block_delta — input_json_delta chunks\n for (let i = 0; i < argsJson.length; i += chunkSize) {\n const slice = argsJson.slice(i, i + chunkSize);\n events.push({\n type: \"content_block_delta\",\n index: blockIndex,\n delta: { type: \"input_json_delta\", partial_json: slice },\n });\n }\n\n // content_block_stop\n events.push({\n type: \"content_block_stop\",\n index: blockIndex,\n });\n\n blockIndex++;\n }\n\n // message_delta\n events.push({\n type: \"message_delta\",\n delta: {\n stop_reason: claudeStopReason(overrides?.finishReason, \"tool_use\"),\n stop_sequence: null,\n },\n usage: { output_tokens: claudeUsage(overrides).output_tokens },\n });\n\n // message_stop\n events.push({ type: \"message_stop\" });\n\n return events;\n}\n\n// Non-streaming response builders\n\n/**\n * Push faithful Anthropic `redacted_thinking` content blocks (each a\n * `{ type: \"redacted_thinking\", data }`) onto a non-streaming content array, in\n * recorded order. No-op when `redactedThinking` is empty/absent, so no blocks\n * are added.\n *\n * Leading placement keeps the turn round-trip-safe for the extended-thinking\n * leading-block invariant. It does NOT reproduce real Anthropic's block order:\n * aimock joins all plaintext reasoning into one block and emits all redacted\n * blocks ahead of it, whereas real Anthropic interleaves `thinking` and\n * `redacted_thinking` in occurrence order. The interleaving is not preserved.\n */\nfunction pushRedactedThinkingBlocks(\n contentBlocks: object[],\n redactedThinking: string[] | undefined,\n): void {\n for (const data of redactedThinking ?? []) {\n contentBlocks.push({ type: \"redacted_thinking\", data });\n }\n}\n\nfunction buildClaudeTextResponse(\n content: string,\n model: string,\n reasoning?: string,\n overrides?: ResponseOverrides,\n reasoningSignature?: string,\n redactedThinking?: string[],\n): object {\n const contentBlocks: object[] = [];\n\n // Redacted-thinking blocks lead the content array (before thinking / text);\n // see the helper for the ordering caveat.\n pushRedactedThinkingBlocks(contentBlocks, redactedThinking);\n\n if (reasoning) {\n contentBlocks.push({\n type: \"thinking\",\n thinking: reasoning,\n // Prefer a recorded real signature; otherwise the round-trip-safe placeholder.\n signature: reasoningSignature ?? PLACEHOLDER_SIGNATURE,\n });\n }\n\n contentBlocks.push({ type: \"text\", text: content });\n\n return {\n id: overrides?.id ?? generateMessageId(),\n type: \"message\",\n role: overrides?.role ?? \"assistant\",\n content: contentBlocks,\n model: overrides?.model ?? model,\n stop_reason: claudeStopReason(overrides?.finishReason, \"end_turn\"),\n stop_sequence: null,\n usage: claudeUsage(overrides),\n };\n}\n\nfunction buildClaudeToolCallResponse(\n toolCalls: ToolCall[],\n model: string,\n logger: Logger,\n reasoning?: string,\n overrides?: ResponseOverrides,\n reasoningSignature?: string,\n redactedThinking?: string[],\n): object {\n const contentBlocks: object[] = [];\n\n // Redacted-thinking blocks lead the content array (before thinking / tool_use)\n // to satisfy the leading-block invariant on replay; see the helper for the\n // ordering caveat.\n pushRedactedThinkingBlocks(contentBlocks, redactedThinking);\n\n // Leading thinking block when reasoning is present — mirrors\n // buildClaudeContentWithToolCallsResponse so a pure-tool-call turn under\n // extended thinking carries the same leading thinking block.\n if (reasoning) {\n contentBlocks.push({\n type: \"thinking\",\n thinking: reasoning,\n // Prefer a recorded real signature; otherwise the round-trip-safe placeholder.\n signature: reasoningSignature ?? PLACEHOLDER_SIGNATURE,\n });\n }\n\n for (const tc of toolCalls) {\n let argsObj: unknown;\n try {\n argsObj = JSON.parse(tc.arguments || \"{}\");\n } catch {\n logger.warn(\n `Malformed JSON in fixture tool call arguments for \"${tc.name}\": ${tc.arguments}`,\n );\n argsObj = {};\n }\n contentBlocks.push({\n type: \"tool_use\",\n id: tc.id || generateToolUseId(),\n name: tc.name,\n input: argsObj,\n });\n }\n\n return {\n id: overrides?.id ?? generateMessageId(),\n type: \"message\",\n role: overrides?.role ?? \"assistant\",\n content: contentBlocks,\n model: overrides?.model ?? model,\n stop_reason: claudeStopReason(overrides?.finishReason, \"tool_use\"),\n stop_sequence: null,\n usage: claudeUsage(overrides),\n };\n}\n\nfunction buildClaudeContentWithToolCallsStreamEvents(\n content: string,\n toolCalls: ToolCall[],\n model: string,\n chunkSize: number,\n logger: Logger,\n reasoning?: string,\n overrides?: ResponseOverrides,\n reasoningSignature?: string,\n redactedThinking?: string[],\n blocks?: FixtureBlock[],\n): ClaudeSSEEvent[] {\n const msgId = overrides?.id ?? generateMessageId();\n const effectiveModel = overrides?.model ?? model;\n // Prefer a recorded real signature when one was captured; otherwise fall back\n // to the round-trip-safe placeholder.\n const signature = reasoningSignature ?? PLACEHOLDER_SIGNATURE;\n const events: ClaudeSSEEvent[] = [];\n\n // message_start\n events.push({\n type: \"message_start\",\n message: {\n id: msgId,\n type: \"message\",\n role: overrides?.role ?? \"assistant\",\n content: [],\n model: effectiveModel,\n stop_reason: null,\n stop_sequence: null,\n usage: claudeUsage(overrides),\n },\n });\n\n let blockIndex = 0;\n\n // Redacted-thinking blocks lead the turn (before thinking / text / tool_use);\n // see the helper for the ordering caveat. Applies to both the legacy and the\n // ordered-`blocks` paths.\n blockIndex = pushRedactedThinkingStreamEvents(events, blockIndex, redactedThinking);\n\n // Optional thinking block — also shared by both paths.\n if (reasoning) {\n // Real Anthropic emits an empty `signature` on the thinking\n // `content_block_start`; the cryptographic signature arrives only via the\n // trailing `signature_delta`. Mirror that wire shape here.\n events.push({\n type: \"content_block_start\",\n index: blockIndex,\n content_block: { type: \"thinking\", thinking: \"\", signature: \"\" },\n });\n\n for (let i = 0; i < reasoning.length; i += chunkSize) {\n const slice = reasoning.slice(i, i + chunkSize);\n events.push({\n type: \"content_block_delta\",\n index: blockIndex,\n delta: { type: \"thinking_delta\", thinking: slice },\n });\n }\n\n events.push({\n type: \"content_block_delta\",\n index: blockIndex,\n delta: { type: \"signature_delta\", signature },\n });\n\n events.push({\n type: \"content_block_stop\",\n index: blockIndex,\n });\n\n blockIndex++;\n }\n\n if (blocks && blocks.length > 0) {\n // NEW PATH: stream `text`/`tool_use` content blocks in the fixture's array\n // order. Anthropic is fully tool-first capable — a `toolCall` block can take\n // a lower `index` than a `text` block. Content-block indices are assigned in\n // encounter order, continuing from any leading thinking/redacted blocks.\n const ordered = resolveFixtureBlocks(blocks);\n\n for (const block of ordered) {\n if (block.type === \"text\") {\n events.push({\n type: \"content_block_start\",\n index: blockIndex,\n content_block: { type: \"text\", text: \"\" },\n });\n\n for (let i = 0; i < block.text.length; i += chunkSize) {\n const slice = block.text.slice(i, i + chunkSize);\n events.push({\n type: \"content_block_delta\",\n index: blockIndex,\n delta: { type: \"text_delta\", text: slice },\n });\n }\n\n events.push({\n type: \"content_block_stop\",\n index: blockIndex,\n });\n\n blockIndex++;\n } else {\n const toolUseId = block.id || generateToolUseId();\n\n let argsObj: unknown;\n try {\n argsObj = JSON.parse(block.arguments || \"{}\");\n } catch {\n logger.warn(\n `Malformed JSON in fixture tool call arguments for \"${block.name}\": ${block.arguments}`,\n );\n argsObj = {};\n }\n const argsJson = JSON.stringify(argsObj);\n\n events.push({\n type: \"content_block_start\",\n index: blockIndex,\n content_block: {\n type: \"tool_use\",\n id: toolUseId,\n name: block.name,\n input: {},\n },\n });\n\n for (let i = 0; i < argsJson.length; i += chunkSize) {\n const slice = argsJson.slice(i, i + chunkSize);\n events.push({\n type: \"content_block_delta\",\n index: blockIndex,\n delta: { type: \"input_json_delta\", partial_json: slice },\n });\n }\n\n events.push({\n type: \"content_block_stop\",\n index: blockIndex,\n });\n\n blockIndex++;\n }\n }\n\n // message_delta\n events.push({\n type: \"message_delta\",\n delta: {\n stop_reason: claudeStopReason(overrides?.finishReason, \"tool_use\"),\n stop_sequence: null,\n },\n usage: { output_tokens: claudeUsage(overrides).output_tokens },\n });\n\n // message_stop\n events.push({ type: \"message_stop\" });\n\n return events;\n }\n\n // LEGACY PATH (byte-for-byte unchanged): text content block, then tool_use\n // blocks in `toolCalls` order. Reached only when `blocks` is absent; the\n // leading redacted-thinking/thinking blocks above are shared with the new\n // path and produce identical wire output here.\n // Text content block\n events.push({\n type: \"content_block_start\",\n index: blockIndex,\n content_block: { type: \"text\", text: \"\" },\n });\n\n for (let i = 0; i < content.length; i += chunkSize) {\n const slice = content.slice(i, i + chunkSize);\n events.push({\n type: \"content_block_delta\",\n index: blockIndex,\n delta: { type: \"text_delta\", text: slice },\n });\n }\n\n events.push({\n type: \"content_block_stop\",\n index: blockIndex,\n });\n\n blockIndex++;\n\n // Tool use blocks\n for (const tc of toolCalls) {\n const toolUseId = tc.id || generateToolUseId();\n\n let argsObj: unknown;\n try {\n argsObj = JSON.parse(tc.arguments || \"{}\");\n } catch {\n logger.warn(\n `Malformed JSON in fixture tool call arguments for \"${tc.name}\": ${tc.arguments}`,\n );\n argsObj = {};\n }\n const argsJson = JSON.stringify(argsObj);\n\n events.push({\n type: \"content_block_start\",\n index: blockIndex,\n content_block: {\n type: \"tool_use\",\n id: toolUseId,\n name: tc.name,\n input: {},\n },\n });\n\n for (let i = 0; i < argsJson.length; i += chunkSize) {\n const slice = argsJson.slice(i, i + chunkSize);\n events.push({\n type: \"content_block_delta\",\n index: blockIndex,\n delta: { type: \"input_json_delta\", partial_json: slice },\n });\n }\n\n events.push({\n type: \"content_block_stop\",\n index: blockIndex,\n });\n\n blockIndex++;\n }\n\n // message_delta\n events.push({\n type: \"message_delta\",\n delta: {\n stop_reason: claudeStopReason(overrides?.finishReason, \"tool_use\"),\n stop_sequence: null,\n },\n usage: { output_tokens: claudeUsage(overrides).output_tokens },\n });\n\n // message_stop\n events.push({ type: \"message_stop\" });\n\n return events;\n}\n\nfunction buildClaudeContentWithToolCallsResponse(\n content: string,\n toolCalls: ToolCall[],\n model: string,\n logger: Logger,\n reasoning?: string,\n overrides?: ResponseOverrides,\n reasoningSignature?: string,\n redactedThinking?: string[],\n blocks?: FixtureBlock[],\n): object {\n const contentBlocks: object[] = [];\n\n // Redacted-thinking blocks lead the content array (before thinking / text /\n // tool_use); see the helper for the ordering caveat.\n pushRedactedThinkingBlocks(contentBlocks, redactedThinking);\n\n if (reasoning) {\n contentBlocks.push({\n type: \"thinking\",\n thinking: reasoning,\n // Prefer a recorded real signature; otherwise the round-trip-safe placeholder.\n signature: reasoningSignature ?? PLACEHOLDER_SIGNATURE,\n });\n }\n\n // Build a tool_use content block from a fixture tool call, parsing its\n // string `arguments` into the object `input` Anthropic emits (warning on\n // malformed JSON, same idiom as the streaming/legacy paths).\n const toolUseBlock = (tc: { name: string; arguments: string; id?: string }): object => {\n let argsObj: unknown;\n try {\n argsObj = JSON.parse(tc.arguments || \"{}\");\n } catch {\n logger.warn(\n `Malformed JSON in fixture tool call arguments for \"${tc.name}\": ${tc.arguments}`,\n );\n argsObj = {};\n }\n return {\n type: \"tool_use\",\n id: tc.id || generateToolUseId(),\n name: tc.name,\n input: argsObj,\n };\n };\n\n if (blocks && blocks.length > 0) {\n // NEW PATH: the non-streaming `content[]` array is positionally observable,\n // so emit `text`/`tool_use` content blocks in the fixture's ARRAY ORDER\n // (after any leading redacted/thinking blocks). A toolCall block before a\n // text block therefore yields a tool_use ahead of the text — matching the\n // streaming path for the same `blocks` fixture.\n const ordered = resolveFixtureBlocks(blocks);\n for (const block of ordered) {\n if (block.type === \"text\") {\n contentBlocks.push({ type: \"text\", text: block.text });\n } else {\n contentBlocks.push(\n toolUseBlock({ name: block.name, arguments: block.arguments, id: block.id }),\n );\n }\n }\n } else {\n // LEGACY PATH (unchanged): text content block, then tool_use blocks.\n contentBlocks.push({ type: \"text\", text: content });\n for (const tc of toolCalls) {\n contentBlocks.push(toolUseBlock(tc));\n }\n }\n\n return {\n id: overrides?.id ?? generateMessageId(),\n type: \"message\",\n role: overrides?.role ?? \"assistant\",\n content: contentBlocks,\n model: overrides?.model ?? model,\n stop_reason: claudeStopReason(overrides?.finishReason, \"tool_use\"),\n stop_sequence: null,\n usage: claudeUsage(overrides),\n };\n}\n\n// ─── SSE writer for Claude Messages API ─────────────────────────────────────\n\ninterface ClaudeStreamOptions {\n latency?: number;\n streamingProfile?: StreamingProfile;\n recordedTimings?: RecordedTimings;\n replaySpeed?: number;\n signal?: AbortSignal;\n onChunkSent?: () => void;\n}\n\nasync function writeClaudeSSEStream(\n res: http.ServerResponse,\n events: ClaudeSSEEvent[],\n optionsOrLatency?: number | ClaudeStreamOptions,\n): Promise<boolean> {\n const opts: ClaudeStreamOptions =\n typeof optionsOrLatency === \"number\" ? { latency: optionsOrLatency } : (optionsOrLatency ?? {});\n const latency = opts.latency ?? 0;\n const profile = opts.streamingProfile;\n const { recordedTimings, replaySpeed } = opts;\n const signal = opts.signal;\n const onChunkSent = opts.onChunkSent;\n\n if (res.writableEnded) return true;\n res.setHeader(\"Content-Type\", \"text/event-stream\");\n res.setHeader(\"Cache-Control\", \"no-cache\");\n res.setHeader(\"Connection\", \"keep-alive\");\n\n let chunkIndex = 0;\n for (const event of events) {\n const chunkDelay = calculateDelay(chunkIndex, profile, latency, recordedTimings, replaySpeed);\n if (chunkDelay > 0) await delay(chunkDelay, signal);\n if (signal?.aborted) return false;\n if (res.writableEnded) return true;\n res.write(`event: ${event.type}\\ndata: ${JSON.stringify(event)}\\n\\n`);\n onChunkSent?.();\n if (signal?.aborted) return false;\n chunkIndex++;\n }\n\n if (!res.writableEnded) {\n res.end();\n }\n return true;\n}\n\n// ─── Request handler ────────────────────────────────────────────────────────\n\nexport async function handleMessages(\n req: http.IncomingMessage,\n res: http.ServerResponse,\n raw: string,\n fixtures: Fixture[],\n journal: Journal,\n defaults: HandlerDefaults,\n setCorsHeaders: (res: http.ServerResponse) => void,\n): Promise<void> {\n const { logger } = defaults;\n setCorsHeaders(res);\n\n let claudeReq: ClaudeRequest;\n try {\n claudeReq = JSON.parse(raw) as ClaudeRequest;\n } catch (parseErr) {\n const detail = parseErr instanceof Error ? parseErr.message : \"unknown\";\n journal.add({\n method: req.method ?? \"POST\",\n path: req.url ?? \"/v1/messages\",\n headers: flattenHeaders(req.headers),\n body: null,\n response: { status: 400, fixture: null },\n });\n writeErrorResponse(\n res,\n 400,\n JSON.stringify({\n error: {\n message: `Malformed JSON: ${detail}`,\n type: \"invalid_request_error\",\n },\n }),\n );\n return;\n }\n\n // Reject bodies that parsed but are not a JSON object (e.g. `null`) before\n // touching fields — otherwise `claudeReq.thinking` throws a TypeError that\n // surfaces as a 500 instead of a 400.\n if (!isJsonObject(claudeReq)) {\n journal.add({\n method: req.method ?? \"POST\",\n path: req.url ?? \"/v1/messages\",\n headers: flattenHeaders(req.headers),\n body: null,\n response: { status: 400, fixture: null },\n });\n writeErrorResponse(\n res,\n 400,\n JSON.stringify({\n error: {\n message: \"Request body must be a JSON object\",\n type: \"invalid_request_error\",\n },\n }),\n );\n return;\n }\n\n // Extended-thinking invariant validation. The validator runs whenever\n // thinking is enabled (it self-short-circuits to null otherwise). On a\n // detected violation: strict ON → 400, strict OFF → warn + replay. Mirrors\n // the real Anthropic API, which 400s on these.\n const thinkingViolation = validateThinkingInvariants(claudeReq);\n if (thinkingViolation) {\n const effectiveStrict = resolveStrictMode(defaults.strict, req.headers);\n const violationMessage = thinkingViolationMessage(thinkingViolation);\n if (effectiveStrict) {\n logger.error(`THINKING: ${violationMessage}`);\n journal.add({\n method: req.method ?? \"POST\",\n path: req.url ?? \"/v1/messages\",\n headers: flattenHeaders(req.headers),\n body: null,\n response: {\n status: 400,\n fixture: null,\n ...strictOverrideField(defaults.strict, req.headers),\n },\n });\n writeErrorResponse(\n res,\n 400,\n JSON.stringify({\n type: \"error\",\n error: {\n type: \"invalid_request_error\",\n message: violationMessage,\n },\n }),\n );\n return;\n }\n logger.warn(`THINKING: ${violationMessage} (strict off — replaying anyway)`);\n // Fall through to existing match/replay behavior.\n }\n\n // Convert to ChatCompletionRequest for fixture matching\n const completionReq = claudeToCompletionRequest(claudeReq);\n completionReq._context = getContext(req);\n\n const testId = getTestId(req);\n const { fixture, skippedBySequenceOrTurn } = matchFixtureDiagnostic(\n fixtures,\n completionReq,\n journal.getFixtureMatchCountsForTest(testId),\n defaults.requestTransform,\n // Record mode proxies on a miss to capture a fresh turn (see record gate\n // below), so keep turnIndex strict to prevent an earlier-turn fixture from\n // shadowing a longer request and skipping the new turn's recording.\n recordMatchOptions(!!defaults.record, defaults.logger),\n );\n\n if (fixture) {\n journal.incrementFixtureMatchCount(fixture, fixtures, testId);\n logger.debug(`Fixture matched: ${JSON.stringify(fixture.match).slice(0, 120)}`);\n } else {\n const lastUserMsg = completionReq.messages.filter((m) => m.role === \"user\").pop();\n const snippet =\n typeof lastUserMsg?.content === \"string\" ? lastUserMsg.content.slice(0, 80) : \"\";\n logger.debug(\n `No fixture matched for request (model=${completionReq.model ?? \"?\"}, msg=\"${snippet}\")`,\n );\n }\n\n if (\n applyChaos(\n res,\n fixture,\n defaults.chaos,\n req.headers,\n req.url,\n journal,\n {\n method: req.method ?? \"POST\",\n path: req.url ?? \"/v1/messages\",\n headers: flattenHeaders(req.headers),\n body: completionReq,\n },\n fixture ? \"fixture\" : \"proxy\",\n defaults.registry,\n defaults.logger,\n )\n )\n return;\n\n if (!fixture) {\n const effectiveStrict = resolveStrictMode(defaults.strict, req.headers);\n if (effectiveStrict) {\n const strictStatus = 503;\n const strictMessage = strictNoMatchMessage(skippedBySequenceOrTurn);\n logger.error(\n strictNoMatchLogLine(\n req.method ?? \"POST\",\n req.url ?? \"/v1/messages\",\n skippedBySequenceOrTurn,\n ),\n );\n journal.add({\n method: req.method ?? \"POST\",\n path: req.url ?? \"/v1/messages\",\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: {\n status: strictStatus,\n fixture: null,\n ...strictOverrideField(defaults.strict, req.headers),\n },\n });\n writeErrorResponse(\n res,\n strictStatus,\n JSON.stringify({\n error: {\n message: strictMessage,\n type: \"invalid_request_error\",\n },\n }),\n );\n return;\n }\n if (defaults.record) {\n const outcome = await proxyAndRecord(\n req,\n res,\n completionReq,\n \"anthropic\",\n req.url ?? \"/v1/messages\",\n fixtures,\n defaults,\n raw,\n );\n if (outcome === \"handled_by_hook\") return;\n if (outcome !== \"not_configured\") {\n journal.add({\n method: req.method ?? \"POST\",\n path: req.url ?? \"/v1/messages\",\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: { status: res.statusCode ?? 200, fixture: null, source: \"proxy\" },\n });\n return;\n }\n }\n journal.add({\n method: req.method ?? \"POST\",\n path: req.url ?? \"/v1/messages\",\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: {\n status: 404,\n fixture: null,\n ...strictOverrideField(defaults.strict, req.headers),\n },\n });\n writeErrorResponse(\n res,\n 404,\n JSON.stringify({\n error: {\n message: \"No fixture matched\",\n type: \"invalid_request_error\",\n },\n }),\n );\n return;\n }\n\n const response = await resolveResponse(fixture, completionReq);\n const latency = fixture.latency ?? defaults.latency;\n const chunkSize = Math.max(1, fixture.chunkSize ?? defaults.chunkSize);\n\n // Error response\n if (isErrorResponse(response)) {\n const status = response.status ?? 500;\n journal.add({\n method: req.method ?? \"POST\",\n path: req.url ?? \"/v1/messages\",\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: { status, fixture },\n });\n // Anthropic-style error format: { type: \"error\", error: { type, message } }\n const anthropicError = {\n type: \"error\",\n error: {\n type: response.error.type ?? \"api_error\",\n message: response.error.message,\n },\n };\n writeErrorResponse(res, status, JSON.stringify(anthropicError), {\n retryAfter: response.retryAfter,\n });\n return;\n }\n\n // Content + tool calls response (must be checked before text/tool-only branches)\n if (isContentWithToolCallsResponse(response)) {\n if (response.webSearches?.length) {\n logger.warn(\n \"webSearches in fixture response are not supported for Claude Messages API — ignoring\",\n );\n }\n const overrides = extractOverrides(response);\n const effectiveStrict = resolveStrictMode(defaults.strict, req.headers);\n const effReasoning = resolveReasoningForModel(\n response.reasoning,\n completionReq.model,\n effectiveStrict,\n defaults.logger,\n );\n const { reasoningSignature: effReasoningSignature, redactedThinking: effRedactedThinking } =\n resolveReasoningArtifactsForModel(\n response.reasoningSignature,\n response.redactedThinking,\n completionReq.model,\n effectiveStrict,\n defaults.logger,\n );\n const journalEntry = journal.add({\n method: req.method ?? \"POST\",\n path: req.url ?? \"/v1/messages\",\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: { status: 200, fixture },\n });\n if (claudeReq.stream !== true) {\n const body = buildClaudeContentWithToolCallsResponse(\n response.content ?? \"\",\n response.toolCalls ?? [],\n completionReq.model,\n logger,\n effReasoning,\n overrides,\n effReasoningSignature,\n effRedactedThinking,\n response.blocks,\n );\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify(body));\n } else {\n const events = buildClaudeContentWithToolCallsStreamEvents(\n response.content ?? \"\",\n response.toolCalls ?? [],\n completionReq.model,\n chunkSize,\n logger,\n effReasoning,\n overrides,\n effReasoningSignature,\n effRedactedThinking,\n response.blocks,\n );\n const interruption = createInterruptionSignal(fixture);\n const completed = await writeClaudeSSEStream(res, events, {\n latency,\n streamingProfile: fixture.streamingProfile,\n recordedTimings: fixture.recordedTimings,\n replaySpeed: fixture.replaySpeed ?? defaults.replaySpeed,\n signal: interruption?.signal,\n onChunkSent: interruption?.tick,\n });\n if (!completed) {\n if (!res.writableEnded) res.destroy();\n journalEntry.response.interrupted = true;\n journalEntry.response.interruptReason = interruption?.reason();\n }\n interruption?.cleanup();\n }\n return;\n }\n\n // Text response\n if (isTextResponse(response)) {\n if (response.webSearches?.length) {\n logger.warn(\n \"webSearches in fixture response are not supported for Claude Messages API — ignoring\",\n );\n }\n const overrides = extractOverrides(response);\n const effectiveStrict = resolveStrictMode(defaults.strict, req.headers);\n const effReasoning = resolveReasoningForModel(\n response.reasoning,\n completionReq.model,\n effectiveStrict,\n defaults.logger,\n );\n const { reasoningSignature: effReasoningSignature, redactedThinking: effRedactedThinking } =\n resolveReasoningArtifactsForModel(\n response.reasoningSignature,\n response.redactedThinking,\n completionReq.model,\n effectiveStrict,\n defaults.logger,\n );\n const journalEntry = journal.add({\n method: req.method ?? \"POST\",\n path: req.url ?? \"/v1/messages\",\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: { status: 200, fixture },\n });\n if (claudeReq.stream !== true) {\n const body = buildClaudeTextResponse(\n response.content,\n completionReq.model,\n effReasoning,\n overrides,\n effReasoningSignature,\n effRedactedThinking,\n );\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify(body));\n } else {\n const events = buildClaudeTextStreamEvents(\n response.content,\n completionReq.model,\n chunkSize,\n effReasoning,\n overrides,\n effReasoningSignature,\n effRedactedThinking,\n );\n const interruption = createInterruptionSignal(fixture);\n const completed = await writeClaudeSSEStream(res, events, {\n latency,\n streamingProfile: fixture.streamingProfile,\n recordedTimings: fixture.recordedTimings,\n replaySpeed: fixture.replaySpeed ?? defaults.replaySpeed,\n signal: interruption?.signal,\n onChunkSent: interruption?.tick,\n });\n if (!completed) {\n if (!res.writableEnded) res.destroy();\n journalEntry.response.interrupted = true;\n journalEntry.response.interruptReason = interruption?.reason();\n }\n interruption?.cleanup();\n }\n return;\n }\n\n // Tool call response\n if (isToolCallResponse(response)) {\n if (response.webSearches?.length) {\n logger.warn(\n \"webSearches in fixture response are not supported for Claude Messages API — ignoring\",\n );\n }\n const overrides = extractOverrides(response);\n const effectiveStrict = resolveStrictMode(defaults.strict, req.headers);\n const effReasoning = resolveReasoningForModel(\n response.reasoning,\n completionReq.model,\n effectiveStrict,\n defaults.logger,\n );\n const { reasoningSignature: effReasoningSignature, redactedThinking: effRedactedThinking } =\n resolveReasoningArtifactsForModel(\n response.reasoningSignature,\n response.redactedThinking,\n completionReq.model,\n effectiveStrict,\n defaults.logger,\n );\n const journalEntry = journal.add({\n method: req.method ?? \"POST\",\n path: req.url ?? \"/v1/messages\",\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: { status: 200, fixture },\n });\n if (claudeReq.stream !== true) {\n const body = buildClaudeToolCallResponse(\n response.toolCalls,\n completionReq.model,\n logger,\n effReasoning,\n overrides,\n effReasoningSignature,\n effRedactedThinking,\n );\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify(body));\n } else {\n const events = buildClaudeToolCallStreamEvents(\n response.toolCalls,\n completionReq.model,\n chunkSize,\n logger,\n effReasoning,\n overrides,\n effReasoningSignature,\n effRedactedThinking,\n );\n const interruption = createInterruptionSignal(fixture);\n const completed = await writeClaudeSSEStream(res, events, {\n latency,\n streamingProfile: fixture.streamingProfile,\n recordedTimings: fixture.recordedTimings,\n replaySpeed: fixture.replaySpeed ?? defaults.replaySpeed,\n signal: interruption?.signal,\n onChunkSent: interruption?.tick,\n });\n if (!completed) {\n if (!res.writableEnded) res.destroy();\n journalEntry.response.interrupted = true;\n journalEntry.response.interruptReason = interruption?.reason();\n }\n interruption?.cleanup();\n }\n return;\n }\n\n // Unknown response type\n journal.add({\n method: req.method ?? \"POST\",\n path: req.url ?? \"/v1/messages\",\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: { status: 500, fixture },\n });\n writeErrorResponse(\n res,\n 500,\n JSON.stringify({\n error: {\n message: \"Fixture response did not match any known type\",\n type: \"server_error\",\n },\n }),\n );\n}\n"],"mappings":"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"}
package/dist/metrics.cjs CHANGED
@@ -147,6 +147,22 @@ const VEO_OPERATION_RE = /^\/v1beta\/(operations\/.+)$/;
147
147
  const GROK_VIDEO_SUBMIT_PATH = "/v1/videos/generations";
148
148
  const GROK_VIDEO_STATUS_RE = /^\/v1\/videos\/([^/]+)$/;
149
149
  /**
150
+ * BytePlus Ark (Seedance) async video task routes. The `/api/v3` prefix is
151
+ * ENUMERATED rather than wildcarded: Ark's data-plane base carries it, but a
152
+ * client may point `baseURL` at a bare aimock root instead, so both forms must
153
+ * route. A tolerant `(?:\/[^?]*)?` prefix would additionally claim
154
+ * `/fal/contents/generations/tasks` — and these routes dispatch in the
155
+ * pre-rewrite band, ~1,100 lines ahead of every fal branch, so it would take
156
+ * that path away from the fal proxy. Enumerating the one real prefix also
157
+ * blocks a doubled suffix from matching the status RE.
158
+ *
159
+ * Declared here (not in server.ts) because normalizePathLabel below consumes
160
+ * them, matching the existing OpenRouter/Veo/Grok route-regex edge where
161
+ * server.ts imports its route regexes from this module.
162
+ */
163
+ const BYTEPLUS_VIDEO_SUBMIT_RE = /^(?:\/api\/v3)?\/contents\/generations\/tasks$/;
164
+ const BYTEPLUS_VIDEO_STATUS_RE = /^(?:\/api\/v3)?\/contents\/generations\/tasks\/([^/]+)$/;
165
+ /**
150
166
  * Normalize parametric API paths to route patterns for use as metric labels.
151
167
  * Replaces dynamic segments (model IDs, deployment names, etc.) with placeholders.
152
168
  */
@@ -166,10 +182,14 @@ function normalizePathLabel(pathname) {
166
182
  if (VEO_OPERATION_RE.test(pathname)) return "/v1beta/operations/{name}";
167
183
  if (pathname === GROK_VIDEO_SUBMIT_PATH) return GROK_VIDEO_SUBMIT_PATH;
168
184
  if (OPENAI_VIDEO_STATUS_RE.test(pathname)) return "/v1/videos/{id}";
185
+ if (BYTEPLUS_VIDEO_STATUS_RE.test(pathname)) return "/contents/generations/tasks/{id}";
186
+ if (BYTEPLUS_VIDEO_SUBMIT_RE.test(pathname)) return "/contents/generations/tasks";
169
187
  return pathname;
170
188
  }
171
189
 
172
190
  //#endregion
191
+ exports.BYTEPLUS_VIDEO_STATUS_RE = BYTEPLUS_VIDEO_STATUS_RE;
192
+ exports.BYTEPLUS_VIDEO_SUBMIT_RE = BYTEPLUS_VIDEO_SUBMIT_RE;
173
193
  exports.GROK_VIDEO_STATUS_RE = GROK_VIDEO_STATUS_RE;
174
194
  exports.GROK_VIDEO_SUBMIT_PATH = GROK_VIDEO_SUBMIT_PATH;
175
195
  exports.OPENROUTER_VIDEO_CONTENT_RE = OPENROUTER_VIDEO_CONTENT_RE;
@@ -1 +1 @@
1
- {"version":3,"file":"metrics.cjs","names":[],"sources":["../src/metrics.ts"],"sourcesContent":["/**\n * Lightweight Prometheus metrics registry for LLMock.\n *\n * Zero external dependencies — implements counters, histograms, and gauges\n * with Prometheus text exposition format serialization.\n */\n\n// ---------------------------------------------------------------------------\n// Public interface\n// ---------------------------------------------------------------------------\n\nexport interface MetricsRegistry {\n incrementCounter(name: string, labels: Record<string, string>): void;\n observeHistogram(name: string, labels: Record<string, string>, value: number): void;\n setGauge(name: string, labels: Record<string, string>, value: number): void;\n serialize(): string;\n reset(): void;\n}\n\n// ---------------------------------------------------------------------------\n// Histogram bucket boundaries (Prometheus default-ish)\n// ---------------------------------------------------------------------------\n\nconst HISTOGRAM_BUCKETS = [0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1, 2.5, 5, 10];\n\n// ---------------------------------------------------------------------------\n// Internal helpers\n// ---------------------------------------------------------------------------\n\n/** Build a stable label key string for map lookups: `label1=\"v1\",label2=\"v2\"` */\nfunction labelKey(labels: Record<string, string>): string {\n const entries = Object.entries(labels).sort(([a], [b]) => a.localeCompare(b));\n if (entries.length === 0) return \"\";\n return entries.map(([k, v]) => `${k}=\"${escapeLabelValue(v)}\"`).join(\",\");\n}\n\n/** Escape a label value per Prometheus text exposition format. */\nfunction escapeLabelValue(v: string): string {\n return v.replace(/\\\\/g, \"\\\\\\\\\").replace(/\"/g, '\\\\\"').replace(/\\n/g, \"\\\\n\");\n}\n\n/** Format labels for Prometheus output: `{label1=\"v1\",label2=\"v2\"}` */\nfunction formatLabels(labels: Record<string, string>): string {\n return `{${labelKey(labels)}}`;\n}\n\n// ---------------------------------------------------------------------------\n// Internal metric storage types\n// ---------------------------------------------------------------------------\n\ninterface CounterData {\n type: \"counter\";\n /** Map from labelKey → value */\n series: Map<string, { labels: Record<string, string>; value: number }>;\n}\n\ninterface HistogramData {\n type: \"histogram\";\n /** Map from labelKey → bucket counts, sum, count */\n series: Map<\n string,\n {\n labels: Record<string, string>;\n bucketCounts: number[]; // one per HISTOGRAM_BUCKETS entry\n sum: number;\n count: number;\n }\n >;\n}\n\ninterface GaugeData {\n type: \"gauge\";\n /** Map from labelKey → value */\n series: Map<string, { labels: Record<string, string>; value: number }>;\n}\n\ntype MetricData = CounterData | HistogramData | GaugeData;\n\n// ---------------------------------------------------------------------------\n// Registry implementation\n// ---------------------------------------------------------------------------\n\nexport function createMetricsRegistry(): MetricsRegistry {\n /** Ordered map: metric name → data. Insertion order preserved for stable output. */\n const metrics = new Map<string, MetricData>();\n\n function getOrCreateCounter(name: string): CounterData {\n let data = metrics.get(name);\n if (!data) {\n data = { type: \"counter\", series: new Map() };\n metrics.set(name, data);\n }\n if (data.type !== \"counter\") throw new Error(`Metric ${name} is not a counter`);\n return data as CounterData;\n }\n\n function getOrCreateHistogram(name: string): HistogramData {\n let data = metrics.get(name);\n if (!data) {\n data = { type: \"histogram\", series: new Map() };\n metrics.set(name, data);\n }\n if (data.type !== \"histogram\") throw new Error(`Metric ${name} is not a histogram`);\n return data as HistogramData;\n }\n\n function getOrCreateGauge(name: string): GaugeData {\n let data = metrics.get(name);\n if (!data) {\n data = { type: \"gauge\", series: new Map() };\n metrics.set(name, data);\n }\n if (data.type !== \"gauge\") throw new Error(`Metric ${name} is not a gauge`);\n return data as GaugeData;\n }\n\n return {\n incrementCounter(name: string, labels: Record<string, string>): void {\n const counter = getOrCreateCounter(name);\n const key = labelKey(labels);\n const existing = counter.series.get(key);\n if (existing) {\n existing.value += 1;\n } else {\n counter.series.set(key, { labels, value: 1 });\n }\n },\n\n observeHistogram(name: string, labels: Record<string, string>, value: number): void {\n const histogram = getOrCreateHistogram(name);\n const key = labelKey(labels);\n let existing = histogram.series.get(key);\n if (!existing) {\n existing = {\n labels,\n bucketCounts: new Array(HISTOGRAM_BUCKETS.length).fill(0) as number[],\n sum: 0,\n count: 0,\n };\n histogram.series.set(key, existing);\n }\n // Update cumulative bucket counts\n for (let i = 0; i < HISTOGRAM_BUCKETS.length; i++) {\n if (value <= HISTOGRAM_BUCKETS[i]) {\n existing.bucketCounts[i] += 1;\n }\n }\n existing.sum += value;\n existing.count += 1;\n },\n\n setGauge(name: string, labels: Record<string, string>, value: number): void {\n const gauge = getOrCreateGauge(name);\n const key = labelKey(labels);\n const existing = gauge.series.get(key);\n if (existing) {\n existing.value = value;\n } else {\n gauge.series.set(key, { labels, value });\n }\n },\n\n serialize(): string {\n const lines: string[] = [];\n\n for (const [name, data] of metrics) {\n switch (data.type) {\n case \"counter\": {\n lines.push(`# TYPE ${name} counter`);\n for (const series of data.series.values()) {\n lines.push(`${name}${formatLabels(series.labels)} ${series.value}`);\n }\n break;\n }\n case \"histogram\": {\n lines.push(`# TYPE ${name} histogram`);\n for (const series of data.series.values()) {\n const lblStr = labelKey(series.labels);\n const lblPrefix = lblStr ? `${lblStr},` : \"\";\n // Bucket lines\n for (let i = 0; i < HISTOGRAM_BUCKETS.length; i++) {\n lines.push(\n `${name}_bucket{${lblPrefix}le=\"${HISTOGRAM_BUCKETS[i]}\"} ${series.bucketCounts[i]}`,\n );\n }\n // +Inf bucket\n lines.push(`${name}_bucket{${lblPrefix}le=\"+Inf\"} ${series.count}`);\n // Sum and count\n lines.push(`${name}_sum${formatLabels(series.labels)} ${series.sum}`);\n lines.push(`${name}_count${formatLabels(series.labels)} ${series.count}`);\n }\n break;\n }\n case \"gauge\": {\n lines.push(`# TYPE ${name} gauge`);\n for (const series of data.series.values()) {\n lines.push(`${name}${formatLabels(series.labels)} ${series.value}`);\n }\n break;\n }\n }\n }\n\n return lines.length > 0 ? lines.join(\"\\n\") + \"\\n\" : \"\";\n },\n\n reset(): void {\n metrics.clear();\n },\n };\n}\n\n// ---------------------------------------------------------------------------\n// Path normalization for metric labels\n// ---------------------------------------------------------------------------\n\n// Regex patterns for parametric API routes\nconst BEDROCK_RE =\n /^\\/model\\/([^/]+)\\/(invoke|invoke-with-response-stream|converse|converse-stream)$/;\nconst GEMINI_RE = /^\\/v1beta\\/models\\/([^:]+):(generateContent|streamGenerateContent)$/;\nconst AZURE_RE = /^\\/openai\\/deployments\\/([^/]+)\\/(chat\\/completions|embeddings)$/;\nconst ELEVENLABS_TTS_RE = /^\\/v1\\/text-to-speech\\/([^/]+)$/;\nconst VERTEX_RE =\n /^\\/v1\\/projects\\/([^/]+)\\/locations\\/([^/]+)\\/publishers\\/google\\/models\\/([^:]+):(.+)$/;\n// Exported: server.ts route dispatch matches the same OpenRouter and OpenAI\n// video paths.\nexport const OPENROUTER_VIDEO_CONTENT_RE = /^\\/api\\/v1\\/videos\\/([^/]+)\\/content$/;\nexport const OPENROUTER_VIDEO_STATUS_RE = /^\\/api\\/v1\\/videos\\/([^/]+)$/;\nexport const OPENAI_VIDEO_STATUS_RE = /^\\/v1\\/videos\\/([^/]+)$/;\n// Exported: server.ts route dispatch matches the same Google Veo and xAI Grok\n// video paths. Veo submit (`:predictLongRunning`) is anchored so it never\n// collides with the bare Gemini `:predict` route; Veo operations live in a\n// fresh `/v1beta/operations/...` namespace. Grok submit is an exact literal\n// path; Grok status reuses the OpenAI `/v1/videos/{id}` shape (the dispatch\n// guards `id !== \"generations\"` and the job-map lookup disambiguates Sora).\nexport const VEO_PREDICT_LRO_RE = /^\\/v1beta\\/models\\/([^:]+):predictLongRunning$/;\nexport const VEO_OPERATION_RE = /^\\/v1beta\\/(operations\\/.+)$/;\nexport const GROK_VIDEO_SUBMIT_PATH = \"/v1/videos/generations\";\nexport const GROK_VIDEO_STATUS_RE = /^\\/v1\\/videos\\/([^/]+)$/;\n\n/**\n * Normalize parametric API paths to route patterns for use as metric labels.\n * Replaces dynamic segments (model IDs, deployment names, etc.) with placeholders.\n */\nexport function normalizePathLabel(pathname: string): string {\n // Bedrock: /model/{modelId}/{operation}\n const bedrockMatch = pathname.match(BEDROCK_RE);\n if (bedrockMatch) {\n return `/model/{modelId}/${bedrockMatch[2]}`;\n }\n\n // Gemini: /v1beta/models/{model}:{action}\n const geminiMatch = pathname.match(GEMINI_RE);\n if (geminiMatch) {\n return `/v1beta/models/{model}:${geminiMatch[2]}`;\n }\n\n // Azure: /openai/deployments/{id}/{operation}\n const azureMatch = pathname.match(AZURE_RE);\n if (azureMatch) {\n return `/openai/deployments/{id}/${azureMatch[2]}`;\n }\n\n // Vertex AI: /v1/projects/{p}/locations/{l}/publishers/google/models/{m}:{action}\n const vertexMatch = pathname.match(VERTEX_RE);\n if (vertexMatch) {\n return `/v1/projects/{p}/locations/{l}/publishers/google/models/{m}:${vertexMatch[4]}`;\n }\n\n // ElevenLabs TTS: /v1/text-to-speech/{voice_id}\n if (ELEVENLABS_TTS_RE.test(pathname)) {\n return \"/v1/text-to-speech/{voice_id}\";\n }\n\n // OpenRouter video: /api/v1/videos/{jobId}[/content] — jobIds are random\n // UUIDs, so raw paths would mint unbounded label cardinality. The static\n // /api/v1/videos/models listing route must not collapse into {jobId}.\n if (OPENROUTER_VIDEO_CONTENT_RE.test(pathname)) {\n return \"/api/v1/videos/{jobId}/content\";\n }\n if (pathname !== \"/api/v1/videos/models\" && OPENROUTER_VIDEO_STATUS_RE.test(pathname)) {\n return \"/api/v1/videos/{jobId}\";\n }\n\n // Google Veo video: submit `:predictLongRunning` + poll `/v1beta/operations/{name}`.\n // Operation names are random UUIDs, so the raw path would mint unbounded\n // label cardinality.\n if (VEO_PREDICT_LRO_RE.test(pathname)) {\n return \"/v1beta/models/{model}:predictLongRunning\";\n }\n if (VEO_OPERATION_RE.test(pathname)) {\n return \"/v1beta/operations/{name}\";\n }\n\n // xAI Grok Imagine submit is a static literal path — keep it distinct from\n // the `/v1/videos/{id}` status label below (which it would otherwise collapse\n // into), exactly as `/api/v1/videos/models` is kept out of the jobId bucket.\n if (pathname === GROK_VIDEO_SUBMIT_PATH) {\n return GROK_VIDEO_SUBMIT_PATH;\n }\n\n // OpenAI/Grok video status: /v1/videos/{id}\n if (OPENAI_VIDEO_STATUS_RE.test(pathname)) {\n return \"/v1/videos/{id}\";\n }\n\n // Static path — return as-is\n return pathname;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"metrics.cjs","names":[],"sources":["../src/metrics.ts"],"sourcesContent":["/**\n * Lightweight Prometheus metrics registry for LLMock.\n *\n * Zero external dependencies — implements counters, histograms, and gauges\n * with Prometheus text exposition format serialization.\n */\n\n// ---------------------------------------------------------------------------\n// Public interface\n// ---------------------------------------------------------------------------\n\nexport interface MetricsRegistry {\n incrementCounter(name: string, labels: Record<string, string>): void;\n observeHistogram(name: string, labels: Record<string, string>, value: number): void;\n setGauge(name: string, labels: Record<string, string>, value: number): void;\n serialize(): string;\n reset(): void;\n}\n\n// ---------------------------------------------------------------------------\n// Histogram bucket boundaries (Prometheus default-ish)\n// ---------------------------------------------------------------------------\n\nconst HISTOGRAM_BUCKETS = [0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1, 2.5, 5, 10];\n\n// ---------------------------------------------------------------------------\n// Internal helpers\n// ---------------------------------------------------------------------------\n\n/** Build a stable label key string for map lookups: `label1=\"v1\",label2=\"v2\"` */\nfunction labelKey(labels: Record<string, string>): string {\n const entries = Object.entries(labels).sort(([a], [b]) => a.localeCompare(b));\n if (entries.length === 0) return \"\";\n return entries.map(([k, v]) => `${k}=\"${escapeLabelValue(v)}\"`).join(\",\");\n}\n\n/** Escape a label value per Prometheus text exposition format. */\nfunction escapeLabelValue(v: string): string {\n return v.replace(/\\\\/g, \"\\\\\\\\\").replace(/\"/g, '\\\\\"').replace(/\\n/g, \"\\\\n\");\n}\n\n/** Format labels for Prometheus output: `{label1=\"v1\",label2=\"v2\"}` */\nfunction formatLabels(labels: Record<string, string>): string {\n return `{${labelKey(labels)}}`;\n}\n\n// ---------------------------------------------------------------------------\n// Internal metric storage types\n// ---------------------------------------------------------------------------\n\ninterface CounterData {\n type: \"counter\";\n /** Map from labelKey → value */\n series: Map<string, { labels: Record<string, string>; value: number }>;\n}\n\ninterface HistogramData {\n type: \"histogram\";\n /** Map from labelKey → bucket counts, sum, count */\n series: Map<\n string,\n {\n labels: Record<string, string>;\n bucketCounts: number[]; // one per HISTOGRAM_BUCKETS entry\n sum: number;\n count: number;\n }\n >;\n}\n\ninterface GaugeData {\n type: \"gauge\";\n /** Map from labelKey → value */\n series: Map<string, { labels: Record<string, string>; value: number }>;\n}\n\ntype MetricData = CounterData | HistogramData | GaugeData;\n\n// ---------------------------------------------------------------------------\n// Registry implementation\n// ---------------------------------------------------------------------------\n\nexport function createMetricsRegistry(): MetricsRegistry {\n /** Ordered map: metric name → data. Insertion order preserved for stable output. */\n const metrics = new Map<string, MetricData>();\n\n function getOrCreateCounter(name: string): CounterData {\n let data = metrics.get(name);\n if (!data) {\n data = { type: \"counter\", series: new Map() };\n metrics.set(name, data);\n }\n if (data.type !== \"counter\") throw new Error(`Metric ${name} is not a counter`);\n return data as CounterData;\n }\n\n function getOrCreateHistogram(name: string): HistogramData {\n let data = metrics.get(name);\n if (!data) {\n data = { type: \"histogram\", series: new Map() };\n metrics.set(name, data);\n }\n if (data.type !== \"histogram\") throw new Error(`Metric ${name} is not a histogram`);\n return data as HistogramData;\n }\n\n function getOrCreateGauge(name: string): GaugeData {\n let data = metrics.get(name);\n if (!data) {\n data = { type: \"gauge\", series: new Map() };\n metrics.set(name, data);\n }\n if (data.type !== \"gauge\") throw new Error(`Metric ${name} is not a gauge`);\n return data as GaugeData;\n }\n\n return {\n incrementCounter(name: string, labels: Record<string, string>): void {\n const counter = getOrCreateCounter(name);\n const key = labelKey(labels);\n const existing = counter.series.get(key);\n if (existing) {\n existing.value += 1;\n } else {\n counter.series.set(key, { labels, value: 1 });\n }\n },\n\n observeHistogram(name: string, labels: Record<string, string>, value: number): void {\n const histogram = getOrCreateHistogram(name);\n const key = labelKey(labels);\n let existing = histogram.series.get(key);\n if (!existing) {\n existing = {\n labels,\n bucketCounts: new Array(HISTOGRAM_BUCKETS.length).fill(0) as number[],\n sum: 0,\n count: 0,\n };\n histogram.series.set(key, existing);\n }\n // Update cumulative bucket counts\n for (let i = 0; i < HISTOGRAM_BUCKETS.length; i++) {\n if (value <= HISTOGRAM_BUCKETS[i]) {\n existing.bucketCounts[i] += 1;\n }\n }\n existing.sum += value;\n existing.count += 1;\n },\n\n setGauge(name: string, labels: Record<string, string>, value: number): void {\n const gauge = getOrCreateGauge(name);\n const key = labelKey(labels);\n const existing = gauge.series.get(key);\n if (existing) {\n existing.value = value;\n } else {\n gauge.series.set(key, { labels, value });\n }\n },\n\n serialize(): string {\n const lines: string[] = [];\n\n for (const [name, data] of metrics) {\n switch (data.type) {\n case \"counter\": {\n lines.push(`# TYPE ${name} counter`);\n for (const series of data.series.values()) {\n lines.push(`${name}${formatLabels(series.labels)} ${series.value}`);\n }\n break;\n }\n case \"histogram\": {\n lines.push(`# TYPE ${name} histogram`);\n for (const series of data.series.values()) {\n const lblStr = labelKey(series.labels);\n const lblPrefix = lblStr ? `${lblStr},` : \"\";\n // Bucket lines\n for (let i = 0; i < HISTOGRAM_BUCKETS.length; i++) {\n lines.push(\n `${name}_bucket{${lblPrefix}le=\"${HISTOGRAM_BUCKETS[i]}\"} ${series.bucketCounts[i]}`,\n );\n }\n // +Inf bucket\n lines.push(`${name}_bucket{${lblPrefix}le=\"+Inf\"} ${series.count}`);\n // Sum and count\n lines.push(`${name}_sum${formatLabels(series.labels)} ${series.sum}`);\n lines.push(`${name}_count${formatLabels(series.labels)} ${series.count}`);\n }\n break;\n }\n case \"gauge\": {\n lines.push(`# TYPE ${name} gauge`);\n for (const series of data.series.values()) {\n lines.push(`${name}${formatLabels(series.labels)} ${series.value}`);\n }\n break;\n }\n }\n }\n\n return lines.length > 0 ? lines.join(\"\\n\") + \"\\n\" : \"\";\n },\n\n reset(): void {\n metrics.clear();\n },\n };\n}\n\n// ---------------------------------------------------------------------------\n// Path normalization for metric labels\n// ---------------------------------------------------------------------------\n\n// Regex patterns for parametric API routes\nconst BEDROCK_RE =\n /^\\/model\\/([^/]+)\\/(invoke|invoke-with-response-stream|converse|converse-stream)$/;\nconst GEMINI_RE = /^\\/v1beta\\/models\\/([^:]+):(generateContent|streamGenerateContent)$/;\nconst AZURE_RE = /^\\/openai\\/deployments\\/([^/]+)\\/(chat\\/completions|embeddings)$/;\nconst ELEVENLABS_TTS_RE = /^\\/v1\\/text-to-speech\\/([^/]+)$/;\nconst VERTEX_RE =\n /^\\/v1\\/projects\\/([^/]+)\\/locations\\/([^/]+)\\/publishers\\/google\\/models\\/([^:]+):(.+)$/;\n// Exported: server.ts route dispatch matches the same OpenRouter and OpenAI\n// video paths.\nexport const OPENROUTER_VIDEO_CONTENT_RE = /^\\/api\\/v1\\/videos\\/([^/]+)\\/content$/;\nexport const OPENROUTER_VIDEO_STATUS_RE = /^\\/api\\/v1\\/videos\\/([^/]+)$/;\nexport const OPENAI_VIDEO_STATUS_RE = /^\\/v1\\/videos\\/([^/]+)$/;\n// Exported: server.ts route dispatch matches the same Google Veo and xAI Grok\n// video paths. Veo submit (`:predictLongRunning`) is anchored so it never\n// collides with the bare Gemini `:predict` route; Veo operations live in a\n// fresh `/v1beta/operations/...` namespace. Grok submit is an exact literal\n// path; Grok status reuses the OpenAI `/v1/videos/{id}` shape (the dispatch\n// guards `id !== \"generations\"` and the job-map lookup disambiguates Sora).\nexport const VEO_PREDICT_LRO_RE = /^\\/v1beta\\/models\\/([^:]+):predictLongRunning$/;\nexport const VEO_OPERATION_RE = /^\\/v1beta\\/(operations\\/.+)$/;\nexport const GROK_VIDEO_SUBMIT_PATH = \"/v1/videos/generations\";\nexport const GROK_VIDEO_STATUS_RE = /^\\/v1\\/videos\\/([^/]+)$/;\n\n/**\n * BytePlus Ark (Seedance) async video task routes. The `/api/v3` prefix is\n * ENUMERATED rather than wildcarded: Ark's data-plane base carries it, but a\n * client may point `baseURL` at a bare aimock root instead, so both forms must\n * route. A tolerant `(?:\\/[^?]*)?` prefix would additionally claim\n * `/fal/contents/generations/tasks` — and these routes dispatch in the\n * pre-rewrite band, ~1,100 lines ahead of every fal branch, so it would take\n * that path away from the fal proxy. Enumerating the one real prefix also\n * blocks a doubled suffix from matching the status RE.\n *\n * Declared here (not in server.ts) because normalizePathLabel below consumes\n * them, matching the existing OpenRouter/Veo/Grok route-regex edge where\n * server.ts imports its route regexes from this module.\n */\nexport const BYTEPLUS_VIDEO_SUBMIT_RE = /^(?:\\/api\\/v3)?\\/contents\\/generations\\/tasks$/;\nexport const BYTEPLUS_VIDEO_STATUS_RE = /^(?:\\/api\\/v3)?\\/contents\\/generations\\/tasks\\/([^/]+)$/;\n\n/**\n * Normalize parametric API paths to route patterns for use as metric labels.\n * Replaces dynamic segments (model IDs, deployment names, etc.) with placeholders.\n */\nexport function normalizePathLabel(pathname: string): string {\n // Bedrock: /model/{modelId}/{operation}\n const bedrockMatch = pathname.match(BEDROCK_RE);\n if (bedrockMatch) {\n return `/model/{modelId}/${bedrockMatch[2]}`;\n }\n\n // Gemini: /v1beta/models/{model}:{action}\n const geminiMatch = pathname.match(GEMINI_RE);\n if (geminiMatch) {\n return `/v1beta/models/{model}:${geminiMatch[2]}`;\n }\n\n // Azure: /openai/deployments/{id}/{operation}\n const azureMatch = pathname.match(AZURE_RE);\n if (azureMatch) {\n return `/openai/deployments/{id}/${azureMatch[2]}`;\n }\n\n // Vertex AI: /v1/projects/{p}/locations/{l}/publishers/google/models/{m}:{action}\n const vertexMatch = pathname.match(VERTEX_RE);\n if (vertexMatch) {\n return `/v1/projects/{p}/locations/{l}/publishers/google/models/{m}:${vertexMatch[4]}`;\n }\n\n // ElevenLabs TTS: /v1/text-to-speech/{voice_id}\n if (ELEVENLABS_TTS_RE.test(pathname)) {\n return \"/v1/text-to-speech/{voice_id}\";\n }\n\n // OpenRouter video: /api/v1/videos/{jobId}[/content] — jobIds are random\n // UUIDs, so raw paths would mint unbounded label cardinality. The static\n // /api/v1/videos/models listing route must not collapse into {jobId}.\n if (OPENROUTER_VIDEO_CONTENT_RE.test(pathname)) {\n return \"/api/v1/videos/{jobId}/content\";\n }\n if (pathname !== \"/api/v1/videos/models\" && OPENROUTER_VIDEO_STATUS_RE.test(pathname)) {\n return \"/api/v1/videos/{jobId}\";\n }\n\n // Google Veo video: submit `:predictLongRunning` + poll `/v1beta/operations/{name}`.\n // Operation names are random UUIDs, so the raw path would mint unbounded\n // label cardinality.\n if (VEO_PREDICT_LRO_RE.test(pathname)) {\n return \"/v1beta/models/{model}:predictLongRunning\";\n }\n if (VEO_OPERATION_RE.test(pathname)) {\n return \"/v1beta/operations/{name}\";\n }\n\n // xAI Grok Imagine submit is a static literal path — keep it distinct from\n // the `/v1/videos/{id}` status label below (which it would otherwise collapse\n // into), exactly as `/api/v1/videos/models` is kept out of the jobId bucket.\n if (pathname === GROK_VIDEO_SUBMIT_PATH) {\n return GROK_VIDEO_SUBMIT_PATH;\n }\n\n // OpenAI/Grok video status: /v1/videos/{id}\n if (OPENAI_VIDEO_STATUS_RE.test(pathname)) {\n return \"/v1/videos/{id}\";\n }\n\n // BytePlus Ark video: /[api/v3/]contents/generations/tasks[/{id}] — task ids\n // (`cgt-…`) are unbounded cardinality. Status before submit: the status RE is\n // the more specific of the two and the submit RE cannot match a trailing id\n // segment, so the order is belt-and-braces. Appended at the end of the\n // cascade because the anchored REs above cannot match any path an earlier\n // entry claims.\n if (BYTEPLUS_VIDEO_STATUS_RE.test(pathname)) {\n return \"/contents/generations/tasks/{id}\";\n }\n if (BYTEPLUS_VIDEO_SUBMIT_RE.test(pathname)) {\n return \"/contents/generations/tasks\";\n }\n\n // Static path — return as-is\n return pathname;\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"metrics.d.cts","names":[],"sources":["../src/metrics.ts"],"sourcesContent":[],"mappings":";;AAWA;;;;;AAGuC,UAHtB,eAAA,CAGsB;EAoEvB,gBAAA,CAAA,IAAA,EAAA,MAAqB,EAAA,MAAI,EAtEA,MAsEA,CAAA,MAAe,EAAA,MAAA,CAAA,CAAA,EAAA,IAAA;EAkKxC,gBAAA,CAAA,IAAkB,EAAA,MAAA,EAAA,MAAA,EAvOO,MAuOP,CAAA,MAAA,EAAA,MAAA,CAAA,EAAA,KAAA,EAAA,MAAA,CAAA,EAAA,IAAA;iCAtOD;;;;iBAoEjB,qBAAA,CAAA,GAAyB;;;;;iBAkKzB,kBAAA"}
1
+ {"version":3,"file":"metrics.d.cts","names":[],"sources":["../src/metrics.ts"],"sourcesContent":[],"mappings":";;AAWA;;;;;AAGuC,UAHtB,eAAA,CAGsB;EAoEvB,gBAAA,CAAA,IAAA,EAAA,MAAqB,EAAA,MAAI,EAtEA,MAsEA,CAAA,MAAe,EAAA,MAAA,CAAA,CAAA,EAAA,IAAA;EAmLxC,gBAAA,CAAA,IAAkB,EAAA,MAAA,EAAA,MAAA,EAxPO,MAwPP,CAAA,MAAA,EAAA,MAAA,CAAA,EAAA,KAAA,EAAA,MAAA,CAAA,EAAA,IAAA;iCAvPD;;;;iBAoEjB,qBAAA,CAAA,GAAyB;;;;;iBAmLzB,kBAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"metrics.d.ts","names":[],"sources":["../src/metrics.ts"],"sourcesContent":[],"mappings":";;AAWA;;;;;AAGuC,UAHtB,eAAA,CAGsB;EAoEvB,gBAAA,CAAA,IAAA,EAAA,MAAqB,EAAA,MAAI,EAtEA,MAsEA,CAAA,MAAe,EAAA,MAAA,CAAA,CAAA,EAAA,IAAA;EAkKxC,gBAAA,CAAA,IAAkB,EAAA,MAAA,EAAA,MAAA,EAvOO,MAuOP,CAAA,MAAA,EAAA,MAAA,CAAA,EAAA,KAAA,EAAA,MAAA,CAAA,EAAA,IAAA;iCAtOD;;;;iBAoEjB,qBAAA,CAAA,GAAyB;;;;;iBAkKzB,kBAAA"}
1
+ {"version":3,"file":"metrics.d.ts","names":[],"sources":["../src/metrics.ts"],"sourcesContent":[],"mappings":";;AAWA;;;;;AAGuC,UAHtB,eAAA,CAGsB;EAoEvB,gBAAA,CAAA,IAAA,EAAA,MAAqB,EAAA,MAAI,EAtEA,MAsEA,CAAA,MAAe,EAAA,MAAA,CAAA,CAAA,EAAA,IAAA;EAmLxC,gBAAA,CAAA,IAAkB,EAAA,MAAA,EAAA,MAAA,EAxPO,MAwPP,CAAA,MAAA,EAAA,MAAA,CAAA,EAAA,KAAA,EAAA,MAAA,CAAA,EAAA,IAAA;iCAvPD;;;;iBAoEjB,qBAAA,CAAA,GAAyB;;;;;iBAmLzB,kBAAA"}
package/dist/metrics.js CHANGED
@@ -146,6 +146,22 @@ const VEO_OPERATION_RE = /^\/v1beta\/(operations\/.+)$/;
146
146
  const GROK_VIDEO_SUBMIT_PATH = "/v1/videos/generations";
147
147
  const GROK_VIDEO_STATUS_RE = /^\/v1\/videos\/([^/]+)$/;
148
148
  /**
149
+ * BytePlus Ark (Seedance) async video task routes. The `/api/v3` prefix is
150
+ * ENUMERATED rather than wildcarded: Ark's data-plane base carries it, but a
151
+ * client may point `baseURL` at a bare aimock root instead, so both forms must
152
+ * route. A tolerant `(?:\/[^?]*)?` prefix would additionally claim
153
+ * `/fal/contents/generations/tasks` — and these routes dispatch in the
154
+ * pre-rewrite band, ~1,100 lines ahead of every fal branch, so it would take
155
+ * that path away from the fal proxy. Enumerating the one real prefix also
156
+ * blocks a doubled suffix from matching the status RE.
157
+ *
158
+ * Declared here (not in server.ts) because normalizePathLabel below consumes
159
+ * them, matching the existing OpenRouter/Veo/Grok route-regex edge where
160
+ * server.ts imports its route regexes from this module.
161
+ */
162
+ const BYTEPLUS_VIDEO_SUBMIT_RE = /^(?:\/api\/v3)?\/contents\/generations\/tasks$/;
163
+ const BYTEPLUS_VIDEO_STATUS_RE = /^(?:\/api\/v3)?\/contents\/generations\/tasks\/([^/]+)$/;
164
+ /**
149
165
  * Normalize parametric API paths to route patterns for use as metric labels.
150
166
  * Replaces dynamic segments (model IDs, deployment names, etc.) with placeholders.
151
167
  */
@@ -165,9 +181,11 @@ function normalizePathLabel(pathname) {
165
181
  if (VEO_OPERATION_RE.test(pathname)) return "/v1beta/operations/{name}";
166
182
  if (pathname === GROK_VIDEO_SUBMIT_PATH) return GROK_VIDEO_SUBMIT_PATH;
167
183
  if (OPENAI_VIDEO_STATUS_RE.test(pathname)) return "/v1/videos/{id}";
184
+ if (BYTEPLUS_VIDEO_STATUS_RE.test(pathname)) return "/contents/generations/tasks/{id}";
185
+ if (BYTEPLUS_VIDEO_SUBMIT_RE.test(pathname)) return "/contents/generations/tasks";
168
186
  return pathname;
169
187
  }
170
188
 
171
189
  //#endregion
172
- export { GROK_VIDEO_STATUS_RE, GROK_VIDEO_SUBMIT_PATH, OPENROUTER_VIDEO_CONTENT_RE, OPENROUTER_VIDEO_STATUS_RE, VEO_OPERATION_RE, VEO_PREDICT_LRO_RE, createMetricsRegistry, normalizePathLabel };
190
+ export { BYTEPLUS_VIDEO_STATUS_RE, BYTEPLUS_VIDEO_SUBMIT_RE, GROK_VIDEO_STATUS_RE, GROK_VIDEO_SUBMIT_PATH, OPENROUTER_VIDEO_CONTENT_RE, OPENROUTER_VIDEO_STATUS_RE, VEO_OPERATION_RE, VEO_PREDICT_LRO_RE, createMetricsRegistry, normalizePathLabel };
173
191
  //# sourceMappingURL=metrics.js.map