@copilotkit/aimock 1.45.1 → 1.46.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.
- package/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/CHANGELOG.md +13 -0
- package/README.md +7 -1
- package/dist/agui-mock.cjs +25 -25
- package/dist/agui-mock.cjs.map +1 -1
- package/dist/agui-mock.d.cts.map +1 -1
- package/dist/agui-mock.d.ts.map +1 -1
- package/dist/agui-mock.js +25 -25
- package/dist/agui-mock.js.map +1 -1
- package/dist/agui-types.d.cts.map +1 -1
- package/dist/aimock-cli.cjs +15 -1
- package/dist/aimock-cli.cjs.map +1 -1
- package/dist/aimock-cli.d.cts.map +1 -1
- package/dist/aimock-cli.d.ts.map +1 -1
- package/dist/aimock-cli.js +16 -2
- package/dist/aimock-cli.js.map +1 -1
- package/dist/api-key-auth.cjs +10 -9
- package/dist/api-key-auth.cjs.map +1 -1
- package/dist/api-key-auth.d.cts.map +1 -1
- package/dist/api-key-auth.d.ts.map +1 -1
- package/dist/api-key-auth.js +11 -10
- package/dist/api-key-auth.js.map +1 -1
- package/dist/batches.cjs +2 -4
- package/dist/batches.cjs.map +1 -1
- package/dist/batches.d.cts.map +1 -1
- package/dist/batches.d.ts.map +1 -1
- package/dist/batches.js +2 -4
- package/dist/batches.js.map +1 -1
- package/dist/bedrock-converse.cjs +61 -80
- package/dist/bedrock-converse.cjs.map +1 -1
- package/dist/bedrock-converse.d.cts.map +1 -1
- package/dist/bedrock-converse.d.ts.map +1 -1
- package/dist/bedrock-converse.js +58 -77
- package/dist/bedrock-converse.js.map +1 -1
- package/dist/bedrock.cjs +65 -97
- package/dist/bedrock.cjs.map +1 -1
- package/dist/bedrock.d.cts.map +1 -1
- package/dist/bedrock.d.ts.map +1 -1
- package/dist/bedrock.js +62 -94
- package/dist/bedrock.js.map +1 -1
- package/dist/byteplus-video.cjs +11 -10
- package/dist/byteplus-video.cjs.map +1 -1
- package/dist/byteplus-video.d.cts.map +1 -1
- package/dist/byteplus-video.d.ts.map +1 -1
- package/dist/byteplus-video.js +5 -4
- package/dist/byteplus-video.js.map +1 -1
- package/dist/chaos.cjs +5 -4
- package/dist/chaos.cjs.map +1 -1
- package/dist/chaos.d.cts.map +1 -1
- package/dist/chaos.d.ts.map +1 -1
- package/dist/chaos.js +5 -4
- package/dist/chaos.js.map +1 -1
- package/dist/cli.cjs +15 -0
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +15 -0
- package/dist/cli.js.map +1 -1
- package/dist/cohere.cjs +49 -74
- package/dist/cohere.cjs.map +1 -1
- package/dist/cohere.d.cts.map +1 -1
- package/dist/cohere.d.ts.map +1 -1
- package/dist/cohere.js +47 -72
- package/dist/cohere.js.map +1 -1
- package/dist/config-loader.cjs +29 -1
- package/dist/config-loader.cjs.map +1 -1
- package/dist/config-loader.d.cts +7 -1
- package/dist/config-loader.d.cts.map +1 -1
- package/dist/config-loader.d.ts +7 -1
- package/dist/config-loader.d.ts.map +1 -1
- package/dist/config-loader.js +29 -2
- package/dist/config-loader.js.map +1 -1
- package/dist/count-tokens.cjs +146 -0
- package/dist/count-tokens.cjs.map +1 -0
- package/dist/count-tokens.js +146 -0
- package/dist/count-tokens.js.map +1 -0
- package/dist/elevenlabs-audio.cjs +36 -34
- package/dist/elevenlabs-audio.cjs.map +1 -1
- package/dist/elevenlabs-audio.d.cts.map +1 -1
- package/dist/elevenlabs-audio.d.ts.map +1 -1
- package/dist/elevenlabs-audio.js +33 -31
- package/dist/elevenlabs-audio.js.map +1 -1
- package/dist/elevenlabs-voice.cjs +38 -37
- package/dist/elevenlabs-voice.cjs.map +1 -1
- package/dist/elevenlabs-voice.d.cts.map +1 -1
- package/dist/elevenlabs-voice.d.ts.map +1 -1
- package/dist/elevenlabs-voice.js +33 -32
- package/dist/elevenlabs-voice.js.map +1 -1
- package/dist/embedding-request.cjs +16 -0
- package/dist/embedding-request.cjs.map +1 -0
- package/dist/embedding-request.js +14 -0
- package/dist/embedding-request.js.map +1 -0
- package/dist/embeddings.cjs +30 -20
- package/dist/embeddings.cjs.map +1 -1
- package/dist/embeddings.d.cts.map +1 -1
- package/dist/embeddings.d.ts.map +1 -1
- package/dist/embeddings.js +30 -20
- package/dist/embeddings.js.map +1 -1
- package/dist/fal-audio.cjs +45 -43
- package/dist/fal-audio.cjs.map +1 -1
- package/dist/fal-audio.d.cts.map +1 -1
- package/dist/fal-audio.d.ts.map +1 -1
- package/dist/fal-audio.js +42 -40
- package/dist/fal-audio.js.map +1 -1
- package/dist/fal.cjs +17 -15
- package/dist/fal.cjs.map +1 -1
- package/dist/fal.js +16 -14
- package/dist/fal.js.map +1 -1
- package/dist/files.cjs +2 -4
- package/dist/files.cjs.map +1 -1
- package/dist/files.d.cts.map +1 -1
- package/dist/files.d.ts.map +1 -1
- package/dist/files.js +2 -4
- package/dist/files.js.map +1 -1
- package/dist/fine-tuning.cjs +2 -4
- package/dist/fine-tuning.cjs.map +1 -1
- package/dist/fine-tuning.d.cts.map +1 -1
- package/dist/fine-tuning.d.ts.map +1 -1
- package/dist/fine-tuning.js +2 -4
- package/dist/fine-tuning.js.map +1 -1
- package/dist/fixture-loader.cjs +37 -42
- package/dist/fixture-loader.cjs.map +1 -1
- package/dist/fixture-loader.d.cts.map +1 -1
- package/dist/fixture-loader.d.ts.map +1 -1
- package/dist/fixture-loader.js +37 -42
- package/dist/fixture-loader.js.map +1 -1
- package/dist/gemini-embeddings.cjs +26 -24
- package/dist/gemini-embeddings.cjs.map +1 -1
- package/dist/gemini-embeddings.js +26 -24
- package/dist/gemini-embeddings.js.map +1 -1
- package/dist/gemini-interactions.cjs +50 -35
- package/dist/gemini-interactions.cjs.map +1 -1
- package/dist/gemini-interactions.d.cts +0 -1
- package/dist/gemini-interactions.d.cts.map +1 -1
- package/dist/gemini-interactions.d.ts +0 -1
- package/dist/gemini-interactions.d.ts.map +1 -1
- package/dist/gemini-interactions.js +49 -34
- package/dist/gemini-interactions.js.map +1 -1
- package/dist/gemini.cjs +57 -67
- package/dist/gemini.cjs.map +1 -1
- package/dist/gemini.d.cts.map +1 -1
- package/dist/gemini.d.ts.map +1 -1
- package/dist/gemini.js +56 -67
- package/dist/gemini.js.map +1 -1
- package/dist/grok-video.cjs +14 -12
- package/dist/grok-video.cjs.map +1 -1
- package/dist/grok-video.d.cts.map +1 -1
- package/dist/grok-video.d.ts.map +1 -1
- package/dist/grok-video.js +8 -6
- package/dist/grok-video.js.map +1 -1
- package/dist/helpers.cjs +11 -68
- package/dist/helpers.cjs.map +1 -1
- package/dist/helpers.d.cts.map +1 -1
- package/dist/helpers.d.ts.map +1 -1
- package/dist/helpers.js +12 -67
- package/dist/helpers.js.map +1 -1
- package/dist/images.cjs +30 -28
- package/dist/images.cjs.map +1 -1
- package/dist/images.d.cts.map +1 -1
- package/dist/images.d.ts.map +1 -1
- package/dist/images.js +27 -25
- package/dist/images.js.map +1 -1
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/journal.cjs +3 -1
- package/dist/journal.cjs.map +1 -1
- package/dist/journal.d.cts.map +1 -1
- package/dist/journal.d.ts.map +1 -1
- package/dist/journal.js +2 -1
- package/dist/journal.js.map +1 -1
- package/dist/llmock.cjs +3 -0
- package/dist/llmock.cjs.map +1 -1
- package/dist/llmock.d.cts.map +1 -1
- package/dist/llmock.d.ts.map +1 -1
- package/dist/llmock.js +3 -0
- package/dist/llmock.js.map +1 -1
- package/dist/mcp-handler.cjs +7 -3
- package/dist/mcp-handler.cjs.map +1 -1
- package/dist/mcp-handler.js +7 -3
- package/dist/mcp-handler.js.map +1 -1
- package/dist/mcp-recorder.cjs +2 -1
- package/dist/mcp-recorder.cjs.map +1 -1
- package/dist/mcp-recorder.js +3 -2
- package/dist/mcp-recorder.js.map +1 -1
- package/dist/messages.cjs +111 -96
- package/dist/messages.cjs.map +1 -1
- package/dist/messages.d.cts.map +1 -1
- package/dist/messages.d.ts.map +1 -1
- package/dist/messages.js +109 -95
- package/dist/messages.js.map +1 -1
- package/dist/metrics.cjs +5 -2
- package/dist/metrics.cjs.map +1 -1
- package/dist/metrics.d.cts.map +1 -1
- package/dist/metrics.d.ts.map +1 -1
- package/dist/metrics.js +6 -3
- package/dist/metrics.js.map +1 -1
- package/dist/model-utils.cjs +1 -0
- package/dist/model-utils.cjs.map +1 -1
- package/dist/model-utils.js +1 -1
- package/dist/model-utils.js.map +1 -1
- package/dist/moderation.cjs +15 -12
- package/dist/moderation.cjs.map +1 -1
- package/dist/moderation.js +14 -11
- package/dist/moderation.js.map +1 -1
- package/dist/ollama.cjs +128 -68
- package/dist/ollama.cjs.map +1 -1
- package/dist/ollama.d.cts.map +1 -1
- package/dist/ollama.d.ts.map +1 -1
- package/dist/ollama.js +124 -64
- package/dist/ollama.js.map +1 -1
- package/dist/openapi.cjs +323 -3
- package/dist/openapi.cjs.map +1 -1
- package/dist/openapi.d.cts.map +1 -1
- package/dist/openapi.d.ts.map +1 -1
- package/dist/openapi.js +324 -4
- package/dist/openapi.js.map +1 -1
- package/dist/openrouter-chat.cjs +11 -155
- package/dist/openrouter-chat.cjs.map +1 -1
- package/dist/openrouter-chat.js +11 -155
- package/dist/openrouter-chat.js.map +1 -1
- package/dist/openrouter-video.cjs +51 -74
- package/dist/openrouter-video.cjs.map +1 -1
- package/dist/openrouter-video.d.cts.map +1 -1
- package/dist/openrouter-video.d.ts.map +1 -1
- package/dist/openrouter-video.js +37 -60
- package/dist/openrouter-video.js.map +1 -1
- package/dist/recorder.cjs +176 -95
- package/dist/recorder.cjs.map +1 -1
- package/dist/recorder.d.cts.map +1 -1
- package/dist/recorder.d.ts.map +1 -1
- package/dist/recorder.js +176 -95
- package/dist/recorder.js.map +1 -1
- package/dist/rerank.cjs +13 -11
- package/dist/rerank.cjs.map +1 -1
- package/dist/rerank.js +12 -10
- package/dist/rerank.js.map +1 -1
- package/dist/response/catalog/anthropic-page.cjs +39 -0
- package/dist/response/catalog/anthropic-page.cjs.map +1 -0
- package/dist/response/catalog/anthropic-page.js +38 -0
- package/dist/response/catalog/anthropic-page.js.map +1 -0
- package/dist/response/catalog/collect.cjs +61 -0
- package/dist/response/catalog/collect.cjs.map +1 -0
- package/dist/response/catalog/collect.js +59 -0
- package/dist/response/catalog/collect.js.map +1 -0
- package/dist/response/catalog/gemini-page.cjs +25 -0
- package/dist/response/catalog/gemini-page.cjs.map +1 -0
- package/dist/response/catalog/gemini-page.js +24 -0
- package/dist/response/catalog/gemini-page.js.map +1 -0
- package/dist/response/catalog/render.cjs +182 -0
- package/dist/response/catalog/render.cjs.map +1 -0
- package/dist/response/catalog/render.js +171 -0
- package/dist/response/catalog/render.js.map +1 -0
- package/dist/response/errors.cjs +225 -0
- package/dist/response/errors.cjs.map +1 -0
- package/dist/response/errors.js +224 -0
- package/dist/response/errors.js.map +1 -0
- package/dist/response/finish.cjs +39 -0
- package/dist/response/finish.cjs.map +1 -0
- package/dist/response/finish.js +35 -0
- package/dist/response/finish.js.map +1 -0
- package/dist/response/policy.cjs +126 -0
- package/dist/response/policy.cjs.map +1 -0
- package/dist/response/policy.js +120 -0
- package/dist/response/policy.js.map +1 -0
- package/dist/response/profiles.cjs +69 -0
- package/dist/response/profiles.cjs.map +1 -0
- package/dist/response/profiles.js +65 -0
- package/dist/response/profiles.js.map +1 -0
- package/dist/response/usage/codecs/anthropic.cjs +52 -0
- package/dist/response/usage/codecs/anthropic.cjs.map +1 -0
- package/dist/response/usage/codecs/anthropic.js +50 -0
- package/dist/response/usage/codecs/anthropic.js.map +1 -0
- package/dist/response/usage/codecs/cohere.cjs +36 -0
- package/dist/response/usage/codecs/cohere.cjs.map +1 -0
- package/dist/response/usage/codecs/cohere.js +35 -0
- package/dist/response/usage/codecs/cohere.js.map +1 -0
- package/dist/response/usage/codecs/converse.cjs +16 -0
- package/dist/response/usage/codecs/converse.cjs.map +1 -0
- package/dist/response/usage/codecs/converse.js +15 -0
- package/dist/response/usage/codecs/converse.js.map +1 -0
- package/dist/response/usage/codecs/gemini.cjs +31 -0
- package/dist/response/usage/codecs/gemini.cjs.map +1 -0
- package/dist/response/usage/codecs/gemini.js +31 -0
- package/dist/response/usage/codecs/gemini.js.map +1 -0
- package/dist/response/usage/codecs/interactions.cjs +22 -0
- package/dist/response/usage/codecs/interactions.cjs.map +1 -0
- package/dist/response/usage/codecs/interactions.js +21 -0
- package/dist/response/usage/codecs/interactions.js.map +1 -0
- package/dist/response/usage/codecs/ollama.cjs +17 -0
- package/dist/response/usage/codecs/ollama.cjs.map +1 -0
- package/dist/response/usage/codecs/ollama.js +17 -0
- package/dist/response/usage/codecs/ollama.js.map +1 -0
- package/dist/response/usage/codecs/openai.cjs +52 -0
- package/dist/response/usage/codecs/openai.cjs.map +1 -0
- package/dist/response/usage/codecs/openai.js +50 -0
- package/dist/response/usage/codecs/openai.js.map +1 -0
- package/dist/response/usage/codecs/openrouter.cjs +68 -0
- package/dist/response/usage/codecs/openrouter.cjs.map +1 -0
- package/dist/response/usage/codecs/openrouter.js +67 -0
- package/dist/response/usage/codecs/openrouter.js.map +1 -0
- package/dist/response/usage/codecs/realtime.cjs +19 -0
- package/dist/response/usage/codecs/realtime.cjs.map +1 -0
- package/dist/response/usage/codecs/realtime.js +18 -0
- package/dist/response/usage/codecs/realtime.js.map +1 -0
- package/dist/response/usage/codecs/responses.cjs +21 -0
- package/dist/response/usage/codecs/responses.cjs.map +1 -0
- package/dist/response/usage/codecs/responses.js +20 -0
- package/dist/response/usage/codecs/responses.js.map +1 -0
- package/dist/response/usage/estimate.cjs +23 -0
- package/dist/response/usage/estimate.cjs.map +1 -0
- package/dist/response/usage/estimate.js +21 -0
- package/dist/response/usage/estimate.js.map +1 -0
- package/dist/response/usage/fields.cjs +72 -0
- package/dist/response/usage/fields.cjs.map +1 -0
- package/dist/response/usage/fields.js +70 -0
- package/dist/response/usage/fields.js.map +1 -0
- package/dist/response/usage/recording.cjs +52 -0
- package/dist/response/usage/recording.cjs.map +1 -0
- package/dist/response/usage/recording.js +52 -0
- package/dist/response/usage/recording.js.map +1 -0
- package/dist/responses.cjs +45 -58
- package/dist/responses.cjs.map +1 -1
- package/dist/responses.d.cts.map +1 -1
- package/dist/responses.d.ts.map +1 -1
- package/dist/responses.js +44 -57
- package/dist/responses.js.map +1 -1
- package/dist/route-registry.cjs +45 -0
- package/dist/route-registry.cjs.map +1 -1
- package/dist/route-registry.d.cts.map +1 -1
- package/dist/route-registry.d.ts.map +1 -1
- package/dist/route-registry.js +41 -1
- package/dist/route-registry.js.map +1 -1
- package/dist/router.cjs +4 -0
- package/dist/router.cjs.map +1 -1
- package/dist/router.d.cts.map +1 -1
- package/dist/router.d.ts.map +1 -1
- package/dist/router.js +3 -0
- package/dist/router.js.map +1 -1
- package/dist/search.cjs +13 -11
- package/dist/search.cjs.map +1 -1
- package/dist/search.js +12 -10
- package/dist/search.js.map +1 -1
- package/dist/server.cjs +222 -146
- package/dist/server.cjs.map +1 -1
- package/dist/server.d.cts.map +1 -1
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +223 -147
- package/dist/server.js.map +1 -1
- package/dist/speech.cjs +20 -18
- package/dist/speech.cjs.map +1 -1
- package/dist/speech.d.cts.map +1 -1
- package/dist/speech.d.ts.map +1 -1
- package/dist/speech.js +19 -17
- package/dist/speech.js.map +1 -1
- package/dist/sse-writer.cjs +7 -0
- package/dist/sse-writer.cjs.map +1 -1
- package/dist/sse-writer.d.cts.map +1 -1
- package/dist/sse-writer.d.ts.map +1 -1
- package/dist/sse-writer.js +7 -1
- package/dist/sse-writer.js.map +1 -1
- package/dist/stream-collapse.cjs +118 -11
- package/dist/stream-collapse.cjs.map +1 -1
- package/dist/stream-collapse.d.cts +10 -9
- package/dist/stream-collapse.d.cts.map +1 -1
- package/dist/stream-collapse.d.ts +10 -9
- package/dist/stream-collapse.d.ts.map +1 -1
- package/dist/stream-collapse.js +118 -11
- package/dist/stream-collapse.js.map +1 -1
- package/dist/transcription.cjs +12 -10
- package/dist/transcription.cjs.map +1 -1
- package/dist/transcription.d.cts.map +1 -1
- package/dist/transcription.d.ts.map +1 -1
- package/dist/transcription.js +11 -9
- package/dist/transcription.js.map +1 -1
- package/dist/types.cjs.map +1 -1
- package/dist/types.d.cts +62 -3
- package/dist/types.d.cts.map +1 -1
- package/dist/types.d.ts +62 -3
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/dist/vector-mock.cjs +3 -8
- package/dist/vector-mock.cjs.map +1 -1
- package/dist/vector-mock.d.cts.map +1 -1
- package/dist/vector-mock.d.ts.map +1 -1
- package/dist/vector-mock.js +3 -8
- package/dist/vector-mock.js.map +1 -1
- package/dist/vector-stores.cjs +2 -4
- package/dist/vector-stores.cjs.map +1 -1
- package/dist/vector-stores.d.cts.map +1 -1
- package/dist/vector-stores.d.ts.map +1 -1
- package/dist/vector-stores.js +2 -4
- package/dist/vector-stores.js.map +1 -1
- package/dist/veo-video.cjs +16 -14
- package/dist/veo-video.cjs.map +1 -1
- package/dist/veo-video.js +10 -8
- package/dist/veo-video.js.map +1 -1
- package/dist/video.cjs +21 -19
- package/dist/video.cjs.map +1 -1
- package/dist/video.d.cts.map +1 -1
- package/dist/video.d.ts.map +1 -1
- package/dist/video.js +20 -18
- package/dist/video.js.map +1 -1
- package/dist/ws-gemini-live.cjs +11 -3
- package/dist/ws-gemini-live.cjs.map +1 -1
- package/dist/ws-gemini-live.d.cts +0 -1
- package/dist/ws-gemini-live.d.cts.map +1 -1
- package/dist/ws-gemini-live.d.ts +0 -1
- package/dist/ws-gemini-live.d.ts.map +1 -1
- package/dist/ws-gemini-live.js +10 -2
- package/dist/ws-gemini-live.js.map +1 -1
- package/dist/ws-live.cjs +6 -5
- package/dist/ws-live.cjs.map +1 -1
- package/dist/ws-live.js +5 -4
- package/dist/ws-live.js.map +1 -1
- package/dist/ws-realtime.cjs +28 -54
- package/dist/ws-realtime.cjs.map +1 -1
- package/dist/ws-realtime.d.cts.map +1 -1
- package/dist/ws-realtime.d.ts.map +1 -1
- package/dist/ws-realtime.js +25 -51
- package/dist/ws-realtime.js.map +1 -1
- package/dist/ws-responses.cjs +8 -6
- package/dist/ws-responses.cjs.map +1 -1
- package/dist/ws-responses.js +7 -5
- package/dist/ws-responses.js.map +1 -1
- package/package.json +4 -1
- package/skills/write-fixtures/SKILL.md +25 -16
package/dist/helpers.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"helpers.js","names":[],"sources":["../src/helpers.ts"],"sourcesContent":["import type { MisbehaviorPlan, ServedMisbehaviorToolCall } from \"./misbehavior.js\";\nimport type { LiveFixtureResponse } from \"./live-types.js\";\nimport { AsyncLocalStorage } from \"node:async_hooks\";\nimport { createHash, randomBytes } from \"node:crypto\";\nimport type * as http from \"node:http\";\nimport type { IncomingHttpHeaders } from \"node:http\";\nimport { DEFAULT_TEST_ID, MCP_FAKES_ECHO_LIMIT } from \"./constants.js\";\nimport { cutText } from \"./echo-text.js\";\nimport { build, fixed, msg, quote, type MessageValue } from \"./message-text.js\";\nimport type { Logger } from \"./logger.js\";\nimport { isReasoningModel } from \"./model-utils.js\";\nimport { isRecognizedApiKeyHeader } from \"./api-key-auth.js\";\nimport type {\n ChatCompletionRequest,\n Fixture,\n FixtureMatch,\n FixtureResponse,\n ResponseFactory,\n TextResponse,\n ToolCallResponse,\n ContentWithToolCallsResponse,\n ErrorResponse,\n EmbeddingResponse,\n ImageResponse,\n AudioResponse,\n TranscriptionResponse,\n VideoResponse,\n RawJSONResponse,\n SSEChunk,\n ToolCall,\n FixtureToolCall,\n CustomToolCall,\n JournalEntry,\n FixtureBlock,\n FixtureFileBlock,\n FixtureFileResponsesBlock,\n ResponsesFixtureBlock,\n ChatCompletion,\n ResponseOverrides,\n RecordConfig,\n RecordProviderKey,\n McpFakeIdentity,\n McpFakeUndeclaredPolicy,\n ResponsesToolsMode,\n} from \"./types.js\";\nimport type { MCPSession } from \"./mcp-types.js\";\n\n/** Preserve authored text for string wires without silently repairing invalid JSON. */\nexport function toolArgsForWire(tc: Pick<ToolCall, \"name\" | \"arguments\">) {\n const raw = tc.arguments || \"{}\";\n try {\n const value: unknown = JSON.parse(raw);\n return { kind: \"parsed\" as const, value, text: JSON.stringify(value), raw };\n } catch {\n return { kind: \"verbatim\" as const, raw };\n }\n}\n\n/** Object wires reject the verbatim branch; string wires never need this error. */\nexport class InvalidToolArgumentsError extends Error {\n readonly toolName: string;\n readonly parseDiagnostic: string;\n\n constructor(tc: Pick<ToolCall, \"name\" | \"arguments\">) {\n let parseDiagnostic = \"\";\n try {\n JSON.parse(tc.arguments || \"{}\");\n } catch (error) {\n parseDiagnostic = error instanceof Error ? error.message : String(error);\n }\n super(\n `aimock: fixture tool call \"${tc.name}\" has invalid JSON arguments; this wire carries arguments as an object. ` +\n `Use a wire that carries tool arguments as a string to test malformed JSON. (${parseDiagnostic})`,\n );\n this.name = \"InvalidToolArgumentsError\";\n this.toolName = tc.name;\n this.parseDiagnostic = parseDiagnostic;\n }\n}\n\n/**\n * The `strictToolArguments` server option and logger in effect for the request\n * being served. The server enters it once per HTTP request and once per Gemini\n * Live message, so response builders read it without extra parameters.\n */\ninterface ToolArgumentsScope {\n strict: boolean;\n logger?: Logger;\n}\n\nconst toolArgumentsScope = new AsyncLocalStorage<ToolArgumentsScope>();\n\n/** Run `fn` with the `strictToolArguments` setting and logger of one request. */\nexport function runWithToolArgumentsScope<T>(scope: ToolArgumentsScope, fn: () => T): T {\n return toolArgumentsScope.run(scope, fn);\n}\n\n/** Whether `strictToolArguments` is on for the request being served (default off). */\nexport function strictToolArgumentsEnabled(): boolean {\n return toolArgumentsScope.getStore()?.strict === true;\n}\n\n/**\n * Serve the rest of this request's tool arguments as authored (the\n * `strictToolArguments` behavior). An applied misbehavior fault calls this so\n * that the malformed arguments it injects are not replaced with `{}`; the\n * planner only applies a fault to a fixture whose own arguments are valid\n * wherever the default would replace them.\n */\nexport function keepAuthoredToolArguments(): void {\n const scope = toolArgumentsScope.getStore();\n if (scope) scope.strict = true;\n}\n\n/**\n * The fixture tool-call arguments a wire serves on its normal (no misbehavior)\n * path. Valid JSON gives the parsed value, its re-serialized text, and the\n * authored text. Invalid JSON logs one warning and, by default, is served as\n * `{}` / `\"{}\"`, as in 1.44.0. With `strictToolArguments`, an object wire\n * (`\"object\"`) throws {@link InvalidToolArgumentsError} and a string wire\n * (`\"string\"`) serves the authored text unchanged.\n */\nexport function servedToolArgs(\n tc: Pick<ToolCall, \"name\" | \"arguments\">,\n wire: \"object\" | \"string\",\n logger?: Logger,\n): { value: unknown; text: string; raw: string } {\n const args = toolArgsForWire(tc);\n if (args.kind === \"parsed\") return args;\n const scope = toolArgumentsScope.getStore();\n (logger ?? scope?.logger)?.warn(\n `Malformed JSON in fixture tool call arguments for \"${tc.name}\": ${tc.arguments}`,\n );\n if (scope?.strict !== true) return { value: {}, text: \"{}\", raw: \"{}\" };\n if (wire === \"object\") throw new InvalidToolArgumentsError(tc);\n return { value: undefined, text: args.raw, raw: args.raw };\n}\n\n/**\n * A custom (freeform) tool call reached a wire that has no faithful projection\n * for it. Only the OpenAI Responses API (HTTP and WebSocket) emits custom tool\n * calls, from a fixture's `customToolCalls` or a `customToolCall` block in its\n * `responsesBlocks`. The handlers of the other wires raise this through\n * {@link rejectResponsesOnlyToolCalls} (usually via\n * {@link requireEmittedFunctionToolCalls}) before they write any content.\n * Ollama `/api/generate` is the exception: it rejects every tool-call fixture\n * with a 400 before this check.\n */\nexport class UnsupportedToolCallError extends Error {\n readonly toolName: string;\n readonly wire: string;\n\n constructor(toolName: string, wire: string) {\n super(\n `aimock: fixture tool call \"${toolName}\" is a custom tool call, which only the OpenAI Responses API supports; ` +\n `this request arrived on ${wire}`,\n );\n this.name = \"UnsupportedToolCallError\";\n this.toolName = toolName;\n this.wire = wire;\n }\n}\n\n/**\n * A fixture tool call is malformed: a `toolCalls` entry that is not an object,\n * a malformed `customToolCalls` entry, or a malformed tool block in\n * `responsesBlocks`. Programmatic and factory fixtures skip load validation,\n * so this request-time guard is their validation for the rules\n * {@link assertResponsesToolCalls} and {@link resolveResponsesBlocks} check.\n * An HTTP request then fails with a 500 that carries this message and `code`;\n * a WebSocket turn gets the transport's error message with the code. A\n * malformed legacy `blocks` entry keeps the plain Error of\n * {@link resolveFixtureBlocks}.\n */\nexport class InvalidFixtureToolCallError extends Error {\n readonly code = \"aimock_invalid_fixture_tool_call\";\n\n constructor(\n index: number,\n problem: string,\n carrier: \"toolCalls\" | \"customToolCalls\" | \"blocks\" = \"toolCalls\",\n ) {\n super(\n carrier === \"blocks\"\n ? `Invalid fixture block at index ${index}: ${problem}`\n : `Invalid fixture tool call: ${problem} (${carrier}[${index}])`,\n );\n this.name = \"InvalidFixtureToolCallError\";\n }\n}\n\n/**\n * Request-time guard for one custom tool call (a `customToolCalls` entry).\n * Programmatic and factory fixtures skip load validation, so this is their\n * validation: an object with a non-empty string `name`, a string `input`, no\n * `arguments`, a string `id` when present and a non-empty string `namespace`\n * when present.\n */\nfunction assertCustomToolCall(call: unknown, index: number): void {\n const fail = (problem: string): never => {\n throw new InvalidFixtureToolCallError(index, problem, \"customToolCalls\");\n };\n if (!isPlainObject(call)) fail(\"expected an object\");\n const tc = call as Record<string, unknown>;\n if (tc.namespace !== undefined && (typeof tc.namespace !== \"string\" || tc.namespace === \"\")) {\n fail('\"namespace\" must be a non-empty string when present');\n }\n if (typeof tc.name !== \"string\" || tc.name === \"\") {\n fail('custom tool call requires a non-empty string \"name\" field');\n }\n if (typeof tc.input !== \"string\") fail('\"input\" must be a string for a custom tool call');\n if (tc.arguments !== undefined) fail('custom tool call takes \"input\", not \"arguments\"');\n if (tc.id !== undefined && typeof tc.id !== \"string\") {\n fail('custom tool call \"id\" must be a string when present');\n }\n}\n\n/**\n * Validate a response's `customToolCalls` (OpenAI Responses only) before it is\n * served. Throws {@link InvalidFixtureToolCallError} on the first malformed\n * entry. The key is new, so no fixture of an earlier release reaches this.\n */\nexport function assertCustomToolCalls(response: object): void {\n const list = (response as { customToolCalls?: unknown }).customToolCalls;\n if (list === undefined) return;\n if (!Array.isArray(list)) {\n throw new InvalidFixtureToolCallError(0, \"customToolCalls must be an array\", \"customToolCalls\");\n }\n list.forEach((call, i) => assertCustomToolCall(call, i));\n}\n\n/**\n * Validate the custom calls among the tool calls the OpenAI Responses API is\n * about to serve (see {@link servedToolCalls}). Function calls are not\n * checked here: they keep the handling of earlier releases (a non-string\n * `arguments` fails with the uncoded error of the Responses builders).\n */\nexport function assertResponsesToolCalls(calls: readonly FixtureToolCall[]): void {\n let customIndex = 0;\n for (const tc of calls) {\n if (isPlainObject(tc) && tc.type === \"custom\") assertCustomToolCall(tc, customIndex++);\n }\n}\n\n/**\n * A fixture's `toolCalls` for a wire that cannot carry custom tool calls.\n * Every entry is a function call and passes through unchanged, exactly as in\n * earlier releases (the builders read its `name`, `arguments` and `id`; its\n * Responses-only `namespace` is not emitted). Custom calls live in\n * `customToolCalls`, which {@link rejectResponsesOnlyToolCalls} rejects.\n */\nexport function requireFunctionToolCalls(calls: ToolCall[]): ToolCall[] {\n return calls;\n}\n\n/**\n * Reject the Responses-only custom tool calls on a wire that cannot carry\n * them: a non-empty `customToolCalls`, or a `customToolCall` block in\n * `responsesBlocks`. Throws {@link UnsupportedToolCallError} naming `wire`.\n * Both keys are new, so no fixture of an earlier release reaches this error.\n */\nexport function rejectResponsesOnlyToolCalls(response: object, wire: string): void {\n const r = response as { customToolCalls?: unknown; responsesBlocks?: unknown };\n if (Array.isArray(r.customToolCalls) && r.customToolCalls.length > 0) {\n const first: unknown = r.customToolCalls[0];\n const name = isPlainObject(first) && typeof first.name === \"string\" ? first.name : \"\";\n throw new UnsupportedToolCallError(name, wire);\n }\n if (Array.isArray(r.responsesBlocks)) {\n for (const block of r.responsesBlocks as unknown[]) {\n if (isPlainObject(block) && block.type === \"customToolCall\") {\n throw new UnsupportedToolCallError(typeof block.name === \"string\" ? block.name : \"\", wire);\n }\n }\n }\n}\n\n/**\n * {@link rejectResponsesOnlyToolCalls}, then {@link requireFunctionToolCalls}\n * on the response's `toolCalls` (ignoring any `blocks`).\n */\nexport function requireServedFunctionToolCalls(\n response: { toolCalls?: ToolCall[] },\n wire: string,\n): ToolCall[] {\n rejectResponsesOnlyToolCalls(response, wire);\n return requireFunctionToolCalls(response.toolCalls ?? []);\n}\n\n/**\n * The function calls a non-Responses wire emits for this response, after\n * {@link rejectResponsesOnlyToolCalls}. Non-empty ordered `blocks` are\n * authoritative: the builders stream them (and validate them through\n * {@link resolveFixtureBlocks}) and never emit the legacy `toolCalls`, so the\n * result is empty. Without blocks, `toolCalls` is checked by\n * {@link requireFunctionToolCalls}.\n */\nexport function requireEmittedFunctionToolCalls(\n response: { toolCalls?: ToolCall[]; blocks?: FixtureFileBlock[] },\n wire: string,\n): ToolCall[] {\n rejectResponsesOnlyToolCalls(response, wire);\n if (Array.isArray(response.blocks) && response.blocks.length > 0) return [];\n return requireFunctionToolCalls(response.toolCalls ?? []);\n}\n\n/** A fixture tool-call error that a handler surfaces as a coded 500. */\nexport type FixtureToolCallError =\n | InvalidToolArgumentsError\n | UnsupportedToolCallError\n | InvalidFixtureToolCallError;\n\n/** True for the fixture tool-call errors a handler surfaces as a coded 500. */\nexport function isFixtureToolCallError(error: unknown): error is FixtureToolCallError {\n return (\n error instanceof InvalidToolArgumentsError ||\n error instanceof UnsupportedToolCallError ||\n error instanceof InvalidFixtureToolCallError\n );\n}\n\n/** The aimock error code carried in every provider envelope for a fixture tool-call error. */\nexport function fixtureToolCallErrorCode(\n error: FixtureToolCallError,\n):\n | \"aimock_unsupported_tool_call\"\n | \"aimock_invalid_tool_arguments\"\n | \"aimock_invalid_fixture_tool_call\" {\n if (error instanceof UnsupportedToolCallError) return \"aimock_unsupported_tool_call\";\n if (error instanceof InvalidFixtureToolCallError) return error.code;\n return \"aimock_invalid_tool_arguments\";\n}\n\n/**\n * Google RPC error `details` carrying an aimock fixture tool-call code, for the\n * Gemini, Vertex AI and Gemini Live envelopes (`google.rpc.ErrorInfo`: an\n * UPPER_SNAKE `reason`, a `domain`, and string `metadata`).\n */\nexport function googleFixtureToolCallErrorDetails(code: string) {\n return [\n {\n \"@type\": \"type.googleapis.com/google.rpc.ErrorInfo\",\n reason: code.toUpperCase(),\n domain: \"aimock\",\n metadata: { code },\n },\n ];\n}\n\n/**\n * Run a WebSocket preflight `check` that may raise a fixture tool-call error\n * ({@link InvalidToolArgumentsError}, {@link UnsupportedToolCallError} or\n * {@link InvalidFixtureToolCallError}).\n * Such an error marks the already-journaled entry as a 500 carrying the\n * message, then propagates to the transport's error handler.\n */\nexport function journalFixtureToolCallError<T>(journalEntry: JournalEntry, check: () => T): T {\n try {\n return check();\n } catch (error) {\n if (isFixtureToolCallError(error)) {\n journalEntry.response.status = 500;\n journalEntry.response.error = error.message;\n }\n throw error;\n }\n}\n\n/**\n * Resolve effective strict mode from per-request header and server default.\n * Header values override the server default — same precedence pattern as chaos\n * config headers (see resolveChaosConfig in chaos.ts).\n *\n * Header: `X-AIMock-Strict` — \"true\"/\"1\" → strict on, \"false\"/\"0\" → strict off\n * (case-insensitive, surrounding whitespace trimmed).\n * When absent or unrecognised, falls back to the server-level default.\n */\nexport function resolveStrictMode(\n serverDefault: boolean | undefined,\n rawHeaders?: IncomingHttpHeaders,\n): boolean {\n if (rawHeaders) {\n const header = rawHeaders[\"x-aimock-strict\"];\n const rawVal =\n typeof header === \"string\" ? header : Array.isArray(header) ? header[0] : undefined;\n if (typeof rawVal === \"string\") {\n const val = rawVal.trim().toLowerCase();\n if (val === \"true\" || val === \"1\") return true;\n if (val === \"false\" || val === \"0\") return false;\n }\n }\n return serverDefault ?? false;\n}\n\nconst STRICT_INTEGER_RE = /^\\d+$/;\n\n/**\n * Strict decimal-integer grammar shared by the query/list parsers that grew\n * their own local copy on separate branches (files `limit`, fine-tuning\n * `limit`, chaos integer fields): an unsigned digit run and nothing else — no\n * sign, no exponent, no radix prefix, no surrounding whitespace (callers that\n * accept padded input trim before calling).\n *\n * Deliberately narrower than `Number()`, which reads `0x10` as 16, `1e3` as\n * 1000 and strips whitespace and a leading `+`. Returns the value, or `null`\n * when the text is not a safe integer literal. Range checks and error\n * messages stay with the callers — this is the grammar gate only, so sharing\n * it cannot change any surface's accepted range or its 400 text.\n */\nexport function parseStrictIntegerText(text: string): number | null {\n if (!STRICT_INTEGER_RE.test(text)) return null;\n const n = Number(text);\n return Number.isSafeInteger(n) ? n : null;\n}\n\n/**\n * Would a fixture miss on this request have been forwarded upstream? This is\n * the ONE rule behind the no-fixture chaos journal `source` label: \"proxy\"\n * only when record mode has an upstream for THIS provider and strict mode is\n * not refusing the miss first. `effectiveStrict` is the header-resolved value\n * from {@link resolveStrictMode}. `record.providers` is read guarded — a JS\n * caller can hand over `record: {}`.\n */\nexport function wouldProxyMiss(\n effectiveStrict: boolean,\n record: RecordConfig | undefined,\n providerKey: RecordProviderKey | undefined,\n): boolean {\n if (effectiveStrict || providerKey === undefined) return false;\n return Boolean(record?.providers?.[providerKey]);\n}\n\n/**\n * Returns `true` or `false` when the X-AIMock-Strict header overrides the\n * server default, or `undefined` when it doesn't. Designed to be spread\n * directly into a journal entry's `response` object:\n *\n * response: { status, fixture, ...strictOverrideField(defaults.strict, req.headers) }\n */\nexport function strictOverrideField(\n serverDefault: boolean | undefined,\n rawHeaders?: IncomingHttpHeaders,\n): { strictOverride?: boolean } {\n const effective = resolveStrictMode(serverDefault, rawHeaders);\n if (effective !== (serverDefault ?? false)) {\n return { strictOverride: effective };\n }\n return {};\n}\n\n/**\n * Build the strict-mode 503 error message, distinguishing a true no-match from\n * a sequence/turn-exhausted miss.\n *\n * `skippedBySequenceOrTurn` is the count reported by `matchFixtureDiagnostic`\n * (router.ts): the number of fixtures that matched the request SHAPE but were\n * rejected ONLY by their `sequenceIndex`/`turnIndex` count state.\n *\n * - `0` → `\"Strict mode: no fixture matched\"` (no candidate had a matching shape)\n * - `>0` → `\"Strict mode: N candidate fixture(s) skipped by sequence/turn state\"`\n *\n * The HTTP status (503) and error envelope shape are unchanged at every call\n * site — only this message string differs. Endpoints with no sequence/turn\n * gates always pass `0` and therefore see the generic message.\n */\nexport function strictNoMatchMessage(skippedBySequenceOrTurn: number): string {\n if (skippedBySequenceOrTurn > 0) {\n return `Strict mode: ${skippedBySequenceOrTurn} candidate fixture(s) skipped by sequence/turn state`;\n }\n return \"Strict mode: no fixture matched\";\n}\n\n/**\n * Build the strict-mode error LOG line, mirroring {@link strictNoMatchMessage}'s\n * disambiguation so the error log distinguishes the two miss kinds too.\n */\nexport function strictNoMatchLogLine(\n method: string,\n url: string,\n skippedBySequenceOrTurn: number,\n): string {\n if (skippedBySequenceOrTurn > 0) {\n return `STRICT: ${skippedBySequenceOrTurn} candidate fixture(s) skipped by sequence/turn state for ${method} ${url}`;\n }\n return `STRICT: No fixture matched for ${method} ${url}`;\n}\n\n/**\n * Resolve the reasoning string to actually emit for a given model.\n *\n * aimock synthesizes a reasoning channel whenever a fixture carries a\n * `reasoning` string, regardless of the requested model. But a non-reasoning\n * model (e.g. `gpt-4.1`) would emit no reasoning against the real provider, so\n * replaying it is a false green (see aimock#254). This gates the emission on\n * the requested model's capability:\n *\n * - no fixture reasoning → undefined (no-op, short-circuit)\n * - reasoning-capable model → emit unchanged, no log\n * - non-reasoning model, strict OFF → `logger.warn`, still emit (preserves\n * current behavior)\n * - non-reasoning model, strict ON → `logger.error`, suppress (return undefined)\n *\n * Capability is decided from the REQUESTED model id (what the backend was wired\n * to), not any `overrides.model` echoed in the payload.\n */\nexport function resolveReasoningForModel(\n reasoning: string | undefined,\n model: string | undefined,\n strict: boolean,\n logger: Logger,\n): string | undefined {\n if (!reasoning) return undefined;\n if (isReasoningModel(model)) return reasoning;\n if (strict) {\n logger.error(\n `Strict mode: fixture has a reasoning channel but model \"${model}\" is not reasoning-capable — suppressing reasoning emission`,\n );\n return undefined;\n }\n logger.warn(\n `Fixture has a reasoning channel but model \"${model}\" is not reasoning-capable — the real provider would emit no reasoning. Emitting anyway (set X-AIMock-Strict: true to suppress).`,\n );\n return reasoning;\n}\n\n/**\n * Resolve the encrypted reasoning artifacts (`reasoningSignature` and\n * `redactedThinking`) to actually emit for a given model.\n *\n * `redacted_thinking` blocks and a thinking `signature` ARE part of the\n * reasoning channel — they are just the encrypted form of it — so they must be\n * gated on the same model-capability resolution as the plaintext `reasoning`\n * string (see resolveReasoningForModel). Gating only the plaintext channel\n * leaves a half-gated reasoning path: replaying a fixture recorded from a\n * reasoning model against a non-reasoning model would strip the `thinking`\n * block but still emit `redacted_thinking` blocks, which the real provider for\n * that model would never produce.\n *\n * Capability is decided from the REQUESTED model id, independently of whether a\n * plaintext `reasoning` string is present — a fixture may carry only\n * `redactedThinking` with no plaintext reasoning.\n *\n * - reasoning-capable model → emit both unchanged, no log\n * - non-reasoning model, no artifacts → no-op, nothing to suppress\n * - non-reasoning model, strict OFF → `logger.warn`, still emit (preserves\n * current behavior)\n * - non-reasoning model, strict ON → `logger.error`, suppress both\n *\n * Must be invoked alongside resolveReasoningForModel with identical model/strict\n * inputs so the plaintext and encrypted channels stay suppressed together.\n */\nexport function resolveReasoningArtifactsForModel(\n reasoningSignature: string | undefined,\n redactedThinking: string[] | undefined,\n model: string | undefined,\n strict: boolean,\n logger: Logger,\n): { reasoningSignature?: string; redactedThinking?: string[] } {\n const hasArtifacts = reasoningSignature !== undefined || (redactedThinking?.length ?? 0) > 0;\n if (!hasArtifacts || isReasoningModel(model)) {\n return { reasoningSignature, redactedThinking };\n }\n if (strict) {\n logger.error(\n `Strict mode: fixture has encrypted reasoning artifacts (redacted_thinking/signature) but model \"${model}\" is not reasoning-capable — suppressing reasoning emission`,\n );\n return {};\n }\n logger.warn(\n `Fixture has encrypted reasoning artifacts (redacted_thinking/signature) but model \"${model}\" is not reasoning-capable — the real provider would emit no reasoning. Emitting anyway (set X-AIMock-Strict: true to suppress).`,\n );\n return { reasoningSignature, redactedThinking };\n}\n\nexport function flattenHeaders(headers: http.IncomingHttpHeaders): Record<string, string> {\n const flat: Record<string, string> = {};\n for (const [key, value] of Object.entries(headers)) {\n if (value === undefined) continue;\n if (isRecognizedApiKeyHeader(key)) {\n flat[key] = \"[REDACTED]\";\n } else {\n flat[key] = Array.isArray(value) ? value.join(\", \") : value;\n }\n }\n return flat;\n}\n\n/** Structural discriminator; registration performs full transcript validation. */\nexport function isLiveResponse(value: unknown): value is LiveFixtureResponse {\n return value !== null && typeof value === \"object\" && !Array.isArray(value) && \"live\" in value;\n}\n\nexport function isResponseFactory(r: FixtureResponse | ResponseFactory): r is ResponseFactory {\n return typeof r === \"function\";\n}\n\n/** A server's own view of the caller's fixture array: see {@link createServerFixtureList}. */\ninterface ServerFixtureList {\n /** The caller's array, which every read and write reaches. */\n target: Fixture[];\n /** The server's `responsesTools` mode, read live. */\n mode: () => ResponsesToolsMode | undefined;\n}\n\nconst serverFixtureLists = new WeakMap<object, ServerFixtureList>();\n\n/**\n * Array methods that read elements and write them back. On a server list\n * they run on the caller's array, so the extended view of an element is\n * never written into it.\n */\nconst ELEMENT_MOVING_METHODS = new Set<PropertyKey>([\n \"copyWithin\",\n \"fill\",\n \"pop\",\n \"push\",\n \"reverse\",\n \"shift\",\n \"sort\",\n \"splice\",\n \"unshift\",\n]);\n\n/**\n * The fixture list one server reads and writes: a live view of the caller's\n * `fixtures` array, with its own identity. The matcher reads the server's\n * `responsesTools` mode from the list it is given, so every handler on every\n * wire sees that server's mode without extra parameters, and two servers that\n * share one array each keep their own mode. With `\"extended\"`, reading an\n * element gives `extendedView(element)` (the fixture with its held keys\n * applied); the caller's array and its fixtures are never changed for it.\n * Writes (push, splice, `length = 0`, ...) reach the caller's array.\n */\nexport function createServerFixtureList(\n fixtures: Fixture[],\n mode: () => ResponsesToolsMode | undefined,\n extendedView: (fixture: Fixture) => Fixture,\n): Fixture[] {\n const boundMethods = new Map<PropertyKey, unknown>();\n const list = new Proxy(fixtures, {\n get(target, key) {\n const value: unknown = Reflect.get(target, key);\n if (typeof value === \"function\") {\n if (!ELEMENT_MOVING_METHODS.has(key)) return value;\n let bound = boundMethods.get(key);\n if (bound === undefined) {\n bound = (value as (...args: unknown[]) => unknown).bind(target);\n boundMethods.set(key, bound);\n }\n return bound;\n }\n if (typeof key !== \"string\" || mode() !== \"extended\") return value;\n const first = key.charCodeAt(0);\n if (first < 48 || first > 57 || value === null || typeof value !== \"object\") return value;\n return extendedView(value as Fixture);\n },\n });\n serverFixtureLists.set(list, { target: fixtures, mode });\n return list;\n}\n\n/** Fixture arrays that only their own server reads: see {@link markServerOwnedFixtures}. */\nconst serverOwnedFixtureArrays = new WeakSet<Fixture[]>();\n\n/**\n * @internal Mark `fixtures` as an array no other server reads (LLMock's own\n * array). A server may change the fixtures it makes itself (the control API)\n * in place only in such an array; in a caller's array, which another server\n * may share, they stay as made and an extended server reads them through its\n * view.\n */\nexport function markServerOwnedFixtures(fixtures: Fixture[]): void {\n serverOwnedFixtureArrays.add(fixtures);\n}\n\n/** Whether the array behind `fixtures` (a server list or an array) is server-owned. */\nexport function isServerOwnedFixtures(fixtures: Fixture[]): boolean {\n return serverOwnedFixtureArrays.has(fixtureListTarget(fixtures));\n}\n\n/** Whether `fixtures` is the list of a server running with `responsesTools: \"extended\"`. */\nexport function isExtendedResponsesToolsList(fixtures: readonly Fixture[]): boolean {\n return serverFixtureLists.get(fixtures)?.mode() === \"extended\";\n}\n\n/** The caller's array behind a server list; any other array is itself. */\nexport function fixtureListTarget(fixtures: Fixture[]): Fixture[] {\n return serverFixtureLists.get(fixtures)?.target ?? fixtures;\n}\n\n/**\n * Requests matched against an extended fixture list. {@link resolveResponse}\n * reads it, so the response a handler serves follows the same mode as the\n * match that chose its fixture.\n */\nconst extendedResponsesToolsRequests = new WeakSet<object>();\n\n/** Mark `request` as matched with `responsesTools: \"extended\"`. */\nexport function markExtendedResponsesToolsRequest(request: ChatCompletionRequest): void {\n extendedResponsesToolsRequests.add(request);\n}\n\n/**\n * Drop `customToolCalls` and `responsesBlocks`, for responses served without\n * `responsesTools: \"extended\"`. Earlier releases ignored both keys, so a\n * fixture that carries them serves exactly as before. Returns the same object\n * when neither key is set.\n */\nexport function withoutResponsesToolKeys<T extends FixtureResponse>(response: T): T {\n if (!isPlainObject(response)) return response;\n if (!(\"customToolCalls\" in response) && !(\"responsesBlocks\" in response)) return response;\n const copy: Record<string, unknown> = { ...(response as Record<string, unknown>) };\n delete copy.customToolCalls;\n delete copy.responsesBlocks;\n return copy as unknown as T;\n}\n\nexport async function resolveResponse(\n fixture: Fixture,\n request: ChatCompletionRequest,\n): Promise<FixtureResponse> {\n const served = (response: FixtureResponse): FixtureResponse =>\n sanitizeFixtureResponse(\n extendedResponsesToolsRequests.has(request) ? response : withoutResponsesToolKeys(response),\n );\n if (typeof fixture.response === \"function\") {\n let normalized: FixtureResponse;\n try {\n const raw = await fixture.response(request);\n normalized = normalizeFactoryResponse(raw);\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n throw new Error(`Response factory threw: ${msg}`, { cause: err });\n }\n return served(normalized);\n }\n return served(fixture.response);\n}\n\nexport function isPlainObject(value: unknown): value is Record<string, unknown> {\n return value !== null && typeof value === \"object\" && !Array.isArray(value);\n}\n\n/** A non-empty string, the only valid value of a tool call's `namespace`. */\nfunction isValidNamespace(value: unknown): value is string {\n return typeof value === \"string\" && value !== \"\";\n}\n\n/**\n * Drop the keys a served `toolCalls` entry or `toolCall` block ignores, so\n * no handler reads them: on a `toolCalls` entry, `type` and `input` (a\n * `toolCalls` entry is always a function call; custom calls live in\n * `customToolCalls`) and an invalid `namespace` (not a non-empty string); on a\n * `toolCall` block, `input` and an invalid `namespace`. Earlier releases\n * ignored all of these, so fixtures that carry them serve exactly as before.\n * Returns the same object when nothing needs dropping. `customToolCalls` and\n * `responsesBlocks` are new keys and are validated, not sanitized.\n */\nexport function sanitizeFixtureResponse(response: FixtureResponse): FixtureResponse {\n if (!isPlainObject(response)) return response;\n const r = response as Record<string, unknown>;\n const dropKeys = (entry: unknown, keys: readonly string[]): unknown => {\n if (!isPlainObject(entry)) return entry;\n const drop = keys.filter(\n (key) => key in entry && (key !== \"namespace\" || !isValidNamespace(entry.namespace)),\n );\n if (drop.length === 0) return entry;\n const copy: Record<string, unknown> = { ...entry };\n for (const key of drop) delete copy[key];\n return copy;\n };\n const sanitizeList = (list: unknown, pick: (entry: unknown) => unknown): unknown => {\n if (!Array.isArray(list)) return list;\n const next = list.map(pick);\n return next.some((entry, k) => entry !== list[k]) ? next : list;\n };\n const toolCalls = sanitizeList(r.toolCalls, (entry) =>\n dropKeys(entry, [\"type\", \"input\", \"namespace\"]),\n );\n let blocks = sanitizeList(r.blocks, (entry) =>\n isPlainObject(entry) && entry.type === \"toolCall\"\n ? dropKeys(entry, [\"input\", \"namespace\"])\n : entry,\n );\n // A custom-free `responsesBlocks` with no `blocks` beside it is served as\n // `blocks` by the wires that do not read `responsesBlocks`. (The OpenAI\n // Responses API still prefers `responsesBlocks`; a fixture with a custom\n // call in it is rejected off that API.)\n if (\n !(Array.isArray(r.blocks) && r.blocks.length > 0) &&\n Array.isArray(r.responsesBlocks) &&\n r.responsesBlocks.length > 0 &&\n !r.responsesBlocks.some((entry) => isPlainObject(entry) && entry.type === \"customToolCall\")\n ) {\n blocks = r.responsesBlocks;\n }\n if (toolCalls === r.toolCalls && blocks === r.blocks) return response;\n const copy: Record<string, unknown> = { ...r };\n if (toolCalls !== r.toolCalls) copy.toolCalls = toolCalls;\n if (blocks !== r.blocks) copy.blocks = blocks;\n return copy as unknown as FixtureResponse;\n}\n\n/**\n * Drop `namespace` from every `toolCalls` entry and `toolCall` block, for\n * OpenAI Responses requests served without `responsesTools: \"extended\"`:\n * earlier releases never emitted it. (Without that mode, resolveResponse has\n * already dropped `customToolCalls` and `responsesBlocks`.) Returns the same\n * object when no namespace is set.\n */\nexport function withoutLegacyNamespaces<T extends FixtureResponse>(response: T): T {\n if (!isPlainObject(response)) return response;\n const r = response as Record<string, unknown>;\n const strip = (list: unknown, isTarget: (entry: Record<string, unknown>) => boolean) => {\n if (!Array.isArray(list)) return list;\n if (!list.some((entry) => isPlainObject(entry) && isTarget(entry) && \"namespace\" in entry)) {\n return list;\n }\n return list.map((entry) => {\n if (!isPlainObject(entry) || !isTarget(entry) || !(\"namespace\" in entry)) return entry;\n const copy: Record<string, unknown> = { ...entry };\n delete copy.namespace;\n return copy;\n });\n };\n const toolCalls = strip(r.toolCalls, () => true);\n const blocks = strip(r.blocks, (entry) => entry.type === \"toolCall\");\n if (toolCalls === r.toolCalls && blocks === r.blocks) return response;\n const copy: Record<string, unknown> = { ...r };\n if (toolCalls !== r.toolCalls) copy.toolCalls = toolCalls;\n if (blocks !== r.blocks) copy.blocks = blocks;\n return copy as unknown as T;\n}\n\nfunction normalizeFactoryResponse(raw: FixtureResponse): FixtureResponse {\n const r = { ...raw } as Record<string, unknown>;\n if (typeof r.content === \"object\" && r.content !== null) {\n r.content = JSON.stringify(r.content);\n }\n if (Array.isArray(r.toolCalls)) {\n r.toolCalls = (r.toolCalls as Array<Record<string, unknown>>).map((tc) => {\n if (typeof tc.arguments === \"object\" && tc.arguments !== null) {\n return { ...tc, arguments: JSON.stringify(tc.arguments) };\n }\n return { ...tc };\n });\n }\n // Mirror the toolCalls[].arguments idiom for the optional ordered `blocks`\n // array: auto-stringify object `arguments` on each `toolCall` block so a\n // programmatic ResponseFactory may return objects (resolveFixtureBlocks\n // requires string `arguments`). Text blocks and string arguments pass\n // through unchanged. Matches the loader's block handling.\n if (Array.isArray(r.blocks)) {\n r.blocks = (r.blocks as Array<Record<string, unknown>>).map((block) => {\n if (\n block != null &&\n block.type === \"toolCall\" &&\n typeof block.arguments === \"object\" &&\n block.arguments !== null\n ) {\n return { ...block, arguments: JSON.stringify(block.arguments) };\n }\n return { ...block };\n });\n }\n // `responsesBlocks` (new): the same idiom, but a non-object entry stays as it\n // is for resolveResponsesBlocks to reject. A custom call's `input` (in\n // `customToolCalls` or a `customToolCall` block) is free text and is never\n // stringified.\n if (Array.isArray(r.responsesBlocks)) {\n r.responsesBlocks = (r.responsesBlocks as unknown[]).map((block) =>\n isPlainObject(block) &&\n block.type === \"toolCall\" &&\n typeof block.arguments === \"object\" &&\n block.arguments !== null\n ? { ...block, arguments: JSON.stringify(block.arguments) }\n : block,\n );\n }\n return r as unknown as FixtureResponse;\n}\n\nexport function generateId(prefix = \"chatcmpl\"): string {\n return `${prefix}-${randomBytes(12).toString(\"base64url\")}`;\n}\n\n/**\n * Resolve the request id for this HTTP request.\n *\n * - When the caller sends a well-formed `X-Request-Id` (1-128 chars of\n * `A-Za-z0-9-_.:`), it is echoed verbatim so distributed traces correlate.\n * - Otherwise (absent, empty, too long, or illegal characters) a fresh\n * `req-…` id is generated — never trust an attacker-controlled correlation\n * value to be a valid log key.\n *\n * Returns `{ id, generated }`. The server normalizes\n * `req.headers[\"x-request-id\"]` to the resolved value, so every downstream\n * `flattenHeaders` journal snapshot carries it with zero per-handler edits —\n * which also means a MINTED id would otherwise ride the egress header set to\n * a real provider. The server therefore passes `generated` to\n * `markMintedRequestId` so `buildForwardHeaders` can drop it; a caller's own\n * id still forwards verbatim.\n */\nexport function resolveRequestId(rawHeaders: http.IncomingHttpHeaders): {\n id: string;\n generated: boolean;\n} {\n const raw = rawHeaders[\"x-request-id\"];\n const first = Array.isArray(raw) ? raw[0] : raw;\n if (typeof first === \"string\") {\n const trimmed = first.trim();\n if (trimmed.length >= 1 && trimmed.length <= 128 && /^[A-Za-z0-9\\-_.:]+$/.test(trimmed)) {\n return { id: trimmed, generated: false };\n }\n }\n return { id: generateId(\"req\"), generated: true };\n}\n\n/**\n * Requests whose `x-request-id` aimock MINTED (the caller sent none, or sent\n * an unusable one). The normalized header is indistinguishable from a\n * caller-supplied one by inspection, so the provenance is tracked out-of-band\n * and keyed on the request object, exactly like the egress auth marker.\n */\nconst mintedRequestIds = new WeakSet<http.IncomingMessage>();\n\n/** @internal Record that this request's `x-request-id` is aimock's own. */\nexport function markMintedRequestId(req: http.IncomingMessage, generated: boolean): void {\n if (generated) mintedRequestIds.add(req);\n}\n\n/**\n * @internal True when `x-request-id` on this request was minted by aimock.\n * Egress paths use it to avoid transmitting an invented correlation id to a\n * real provider.\n */\nexport function hasMintedRequestId(req: http.IncomingMessage): boolean {\n return mintedRequestIds.has(req);\n}\n\nexport function generateToolCallId(): string {\n return `call_${randomBytes(12).toString(\"base64url\")}`;\n}\n\nexport function generateMessageId(): string {\n return `msg_${randomBytes(12).toString(\"base64url\")}`;\n}\n\nexport function generateToolUseId(): string {\n return `toolu_${randomBytes(12).toString(\"base64url\")}`;\n}\n\nexport function isTextResponse(r: FixtureResponse): r is TextResponse {\n return \"content\" in r && typeof (r as TextResponse).content === \"string\" && !(\"toolCalls\" in r);\n}\n\nexport function isToolCallResponse(r: FixtureResponse): r is ToolCallResponse {\n return (\n \"toolCalls\" in r &&\n Array.isArray((r as ToolCallResponse).toolCalls) &&\n !(\"content\" in r && typeof (r as unknown as Record<string, unknown>).content === \"string\")\n );\n}\n\n/**\n * Whether `r` is a combined content + tool calls response: a string `content`\n * with a `toolCalls` array, or a non-empty `blocks` array. Public, with the\n * 1.44.0 result for every input; `responsesBlocks` does not count. aimock's\n * own handlers use {@link isCombinedFixtureResponse}, which also counts it.\n */\nexport function isContentWithToolCallsResponse(\n r: FixtureResponse,\n): r is ContentWithToolCallsResponse {\n const o = r as ContentWithToolCallsResponse;\n const hasContentAndToolCalls =\n \"content\" in r &&\n typeof o.content === \"string\" &&\n \"toolCalls\" in r &&\n Array.isArray(o.toolCalls);\n const hasNonEmptyBlocks = Array.isArray(o.blocks) && o.blocks.length > 0;\n return hasContentAndToolCalls || hasNonEmptyBlocks;\n}\n\n/**\n * @internal The combined-response guard aimock's handlers, validation and\n * misbehavior use: {@link isContentWithToolCallsResponse} plus a non-empty\n * `responsesBlocks`. In legacy mode those keys are dropped before serving, so\n * only `responsesTools: \"extended\"` sees the extra clause.\n */\nexport function isCombinedFixtureResponse(r: FixtureResponse): r is ContentWithToolCallsResponse {\n const o = r as ContentWithToolCallsResponse;\n // LEGACY / COMBINED shape — BOTH content (string) + toolCalls (array). This\n // clause is byte-identical to the original guard, so every fixture that\n // matched before still matches here and is classified exactly as before.\n const hasContentAndToolCalls =\n \"content\" in r &&\n typeof o.content === \"string\" &&\n \"toolCalls\" in r &&\n Array.isArray(o.toolCalls);\n // BLOCKS-ONLY shape (additive, #274 F0) — a non-empty `blocks` array with no\n // content/toolCalls. This is a pure RELAXATION: it recognizes MORE, never\n // reclassifies an existing fixture. A blocks-only fixture cannot be claimed by\n // any earlier/looser guard in the dispatch order — `isTextResponse` requires a\n // string `content` AND `!(\"toolCalls\" in r)`, and `isToolCallResponse`\n // requires a `toolCalls` array — so it would otherwise fall through to 500.\n // `isAudioResponse` (checked first everywhere) requires an `audio` field, which\n // blocks-only lacks, so there is no overlap there either.\n const hasNonEmptyBlocks = Array.isArray(o.blocks) && o.blocks.length > 0;\n // RESPONSES-BLOCKS shape (new key): a non-empty `responsesBlocks` array. The\n // OpenAI Responses API serves it; other wires serve a custom-free one as\n // `blocks` (see sanitizeFixtureResponse) and reject a custom call in it.\n const hasNonEmptyResponsesBlocks =\n Array.isArray(o.responsesBlocks) && o.responsesBlocks.length > 0;\n return hasContentAndToolCalls || hasNonEmptyBlocks || hasNonEmptyResponsesBlocks;\n}\n\n/**\n * Validate and pass through the ordered `blocks` field of a combined\n * content+toolCalls fixture. Used ONLY on the new block-iteration path (when a\n * fixture explicitly sets `blocks`); it is NOT a legacy-order reconstructor —\n * fixtures without `blocks` never reach this function and keep their unchanged\n * text-first path.\n *\n * An EMPTY `blocks` array is treated as \"no blocks\" by every builder's\n * streaming gate (`blocks && blocks.length > 0`), so it falls back to the\n * legacy `{content, toolCalls}` path and never reaches this function — the gate\n * is the single source of truth for \"has blocks\". This validator therefore only\n * ever runs on a non-empty array.\n *\n * Accepts the relaxed on-disk {@link FixtureFileBlock} input shape — a\n * `toolCall` block's `arguments` may be a string OR a JSON object/array — which\n * makes the object-tolerance below type-visible and mirrors how\n * normalizeResponse types its file-form input (see {@link FixtureFileBlock} /\n * {@link FixtureFileContentWithToolCallsResponse}). The in-memory\n * {@link FixtureBlock} form (string `arguments`) is a structural subtype, so\n * existing callers that pass `FixtureBlock[]` continue to type-check.\n *\n * Returns the blocks in array order, NORMALIZED to {@link FixtureBlock}: a\n * `text` block with a string `text`, or a `toolCall` block with string `name` +\n * string `arguments` (object/array `arguments` is JSON.stringified) and an\n * optional string `id`. The return type guarantees `arguments: string` — every\n * caller relies on that. Throws on a malformed array or entry — same fail-fast\n * idiom as the other fixture validators in this module (see e.g. the factory\n * guard at {@link resolveResponse}). A `customToolCall` block is not a\n * `blocks` entry (it belongs in `responsesBlocks`, see\n * {@link resolveResponsesBlocks}) and fails as an unknown type.\n */\nexport function resolveFixtureBlocks(blocks: FixtureFileBlock[]): FixtureBlock[] {\n if (!Array.isArray(blocks)) {\n throw new Error(`Invalid fixture blocks: expected an array, got ${typeof blocks}`);\n }\n // Validate each block and return a normalized COPY. Builders iterate the\n // result and must not observe later mutations of — nor be able to mutate —\n // the caller's stored fixture array, and block objects are consumed read-only\n // downstream, so we never mutate the input in place: any normalization (e.g.\n // stringifying object `arguments`) is applied to a fresh per-block copy.\n return blocks.map((block, i) => {\n if (block === null || typeof block !== \"object\") {\n throw new Error(`Invalid fixture block at index ${i}: expected an object`);\n }\n const b = block as Record<string, unknown>;\n if (b.type === \"text\") {\n if (typeof b.text !== \"string\") {\n throw new Error(\n `Invalid fixture block at index ${i}: \"text\" block requires a string \"text\" field`,\n );\n }\n return { type: \"text\", text: b.text };\n } else if (b.type === \"toolCall\") {\n if (typeof b.name !== \"string\") {\n throw new Error(\n `Invalid fixture block at index ${i}: \"toolCall\" block requires a string \"name\" field`,\n );\n }\n if (b.id !== undefined && typeof b.id !== \"string\") {\n throw new Error(\n `Invalid fixture block at index ${i}: \"toolCall\" block \"id\" must be a string when present`,\n );\n }\n // `arguments` is a JSON string in normalized (file-load) form. The\n // programmatic path (addFixture/addFixtures/prependFixture) stores RAW\n // fixtures with no normalizeResponse pass, so an OBJECT `arguments` can\n // reach here. Be tolerant: stringify an object/array (mirroring\n // normalizeResponse's `JSON.stringify`) into a fresh block copy so the\n // programmatic path is safe and the caller's stored fixture is untouched.\n // A string stays byte-identical (file-load path unchanged); any other\n // type is still rejected.\n if (typeof b.arguments === \"object\" && b.arguments !== null) {\n return { ...b, arguments: JSON.stringify(b.arguments) } as unknown as FixtureBlock;\n }\n if (typeof b.arguments !== \"string\") {\n throw new Error(\n `Invalid fixture block at index ${i}: \"toolCall\" block requires a string or object \"arguments\" field`,\n );\n }\n return { ...b, type: \"toolCall\", name: b.name, arguments: b.arguments } as FixtureBlock;\n } else {\n throw new Error(\n `Invalid fixture block at index ${i}: unknown type ${JSON.stringify(b.type)} (expected \"text\" or \"toolCall\")`,\n );\n }\n });\n}\n\n/** A normalized block on a wire that cannot carry custom tool calls. */\nexport type FunctionFixtureBlock = FixtureBlock;\n\n/**\n * Validate and normalize a fixture's `responsesBlocks` (OpenAI Responses\n * only). Same rules and normalized copy as {@link resolveFixtureBlocks}, plus:\n * a `customToolCall` block with a non-empty string `name`, a string `input`\n * (never parsed or stringified), no `arguments` and an optional string `id`;\n * on either tool block, `namespace` must be a non-empty string when present,\n * and a `toolCall` block carries no `input`. The key is new, so a malformed\n * tool block throws the coded {@link InvalidFixtureToolCallError}; a malformed\n * text block or an unknown type throws a plain Error.\n */\nexport function resolveResponsesBlocks(\n blocks: readonly (FixtureFileResponsesBlock | ResponsesFixtureBlock)[],\n): ResponsesFixtureBlock[] {\n if (!Array.isArray(blocks)) {\n throw new Error(`Invalid fixture responsesBlocks: expected an array, got ${typeof blocks}`);\n }\n return blocks.map((block, i): ResponsesFixtureBlock => {\n if (!isPlainObject(block)) {\n throw new Error(`Invalid fixture block at index ${i}: expected an object`);\n }\n const b = block as Record<string, unknown>;\n const failToolCall = (problem: string): never => {\n throw new InvalidFixtureToolCallError(i, problem, \"blocks\");\n };\n if (\n (b.type === \"toolCall\" || b.type === \"customToolCall\") &&\n b.namespace !== undefined &&\n (typeof b.namespace !== \"string\" || b.namespace === \"\")\n ) {\n failToolCall('\"namespace\" must be a non-empty string when present');\n }\n const namespace = typeof b.namespace === \"string\" ? { namespace: b.namespace } : {};\n const id = typeof b.id === \"string\" ? { id: b.id } : {};\n if (b.type === \"text\") {\n if (typeof b.text !== \"string\") {\n throw new Error(\n `Invalid fixture block at index ${i}: \"text\" block requires a string \"text\" field`,\n );\n }\n return { type: \"text\", text: b.text };\n }\n if (b.type === \"toolCall\") {\n if (b.input !== undefined) failToolCall('\"input\" is only valid on a \"customToolCall\" block');\n if (typeof b.name !== \"string\")\n failToolCall('\"toolCall\" block requires a string \"name\" field');\n if (b.id !== undefined && typeof b.id !== \"string\") {\n failToolCall('\"toolCall\" block \"id\" must be a string when present');\n }\n const args =\n typeof b.arguments === \"object\" && b.arguments !== null\n ? JSON.stringify(b.arguments)\n : b.arguments;\n if (typeof args !== \"string\") {\n failToolCall('\"toolCall\" block requires a string or object \"arguments\" field');\n }\n return {\n type: \"toolCall\",\n name: b.name as string,\n arguments: args as string,\n ...id,\n ...namespace,\n };\n }\n if (b.type === \"customToolCall\") {\n if (typeof b.name !== \"string\" || b.name === \"\") {\n failToolCall('\"customToolCall\" block requires a non-empty string \"name\" field');\n }\n if (typeof b.input !== \"string\") {\n failToolCall('\"customToolCall\" block requires a string \"input\" field');\n }\n if (b.arguments !== undefined) {\n failToolCall('\"customToolCall\" block takes \"input\", not \"arguments\"');\n }\n if (b.id !== undefined && typeof b.id !== \"string\") {\n failToolCall('\"customToolCall\" block \"id\" must be a string when present');\n }\n return {\n type: \"customToolCall\",\n name: b.name as string,\n input: b.input as string,\n ...id,\n ...namespace,\n };\n }\n throw new Error(\n `Invalid fixture block at index ${i}: unknown type ${JSON.stringify(b.type)} (expected \"text\", \"toolCall\" or \"customToolCall\")`,\n );\n });\n}\n\n/** Allocate the exact OpenAI call identities once for both wire output and observations. */\nexport function prepareOpenAIChatMisbehavior(plan: MisbehaviorPlan): MisbehaviorPlan {\n const response = plan.response;\n const combined = isCombinedFixtureResponse(response);\n // Custom tool calls reach here only on the OpenAI Responses API; the planner\n // skips every other wire's custom-call fixture so its guard rejects it.\n const outcome = combined ? resolveServedBlockOutcome(response) : undefined;\n const calls =\n outcome?.toolCalls ??\n (combined || isToolCallResponse(response) ? servedToolCalls(response) : []);\n const duplicate = plan.duplicateId;\n const toolCalls = calls.map((call, index) => ({\n ...call,\n ...(index === duplicate?.destinationIndex ? {} : { id: call.id || generateToolCallId() }),\n }));\n if (duplicate) {\n toolCalls[duplicate.destinationIndex].id = toolCalls[duplicate.sourceIndex].id;\n }\n let preparedResponse = { ...response };\n if (\"usage\" in preparedResponse) delete preparedResponse.usage;\n if (plan.stop === \"length\") preparedResponse = { ...preparedResponse, finishReason: \"length\" };\n if (outcome) {\n preparedResponse = withServedToolCalls(\n { ...preparedResponse, content: outcome.content },\n toolCalls,\n rebuildOrderedBlocks(outcome.ordered, toolCalls),\n );\n } else if (combined || isToolCallResponse(response)) {\n preparedResponse = withServedToolCalls(preparedResponse, toolCalls);\n }\n return {\n ...plan,\n response: preparedResponse,\n summary: {\n ...plan.summary,\n servedToolCalls: toolCalls.map(servedMisbehaviorToolCall),\n },\n };\n}\n\n/**\n * The journaled form of a served misbehavior tool call: `{ name, arguments,\n * id }`. A Responses custom tool call adds `type: \"custom\"` and carries its\n * free-text `input` in `arguments`; `namespace` is recorded only when set.\n */\nexport function servedMisbehaviorToolCall(call: FixtureToolCall): ServedMisbehaviorToolCall {\n const namespace = call.namespace !== undefined ? { namespace: call.namespace } : {};\n return call.type === \"custom\"\n ? { type: \"custom\", name: call.name, arguments: call.input, id: call.id, ...namespace }\n : { name: call.name, arguments: call.arguments, id: call.id, ...namespace };\n}\n\n/** The text a tool call contributes to an estimated completion: name plus arguments or input. */\nexport function toolCallUsageText(call: FixtureToolCall): string {\n return call.name + (call.type === \"custom\" ? call.input : call.arguments);\n}\n\n/** Estimate once from the full applied output, before any delivery interruption. */\nexport function resolveOpenAIChatMisbehaviorUsage(\n plan: MisbehaviorPlan,\n request: ChatCompletionRequest,\n exposeReasoning = true,\n): ReturnType<typeof resolveUsage> {\n const response = plan.response;\n const combined = isCombinedFixtureResponse(response);\n const outcome = combined ? resolveServedBlockOutcome(response) : undefined;\n const calls =\n outcome?.toolCalls ??\n (combined || isToolCallResponse(response) ? servedToolCalls(response) : []);\n const content =\n outcome?.content ??\n (\"content\" in response && typeof response.content === \"string\" ? response.content : \"\");\n const reasoning =\n plan.reasoning ??\n (\"reasoning\" in response && typeof response.reasoning === \"string\" ? response.reasoning : \"\");\n const promptText = request.messages\n .map((message) =>\n typeof message.content === \"string\"\n ? message.content\n : Array.isArray(message.content)\n ? message.content.map((part) => part.text ?? \"\").join(\"\")\n : \"\",\n )\n .join(\"\");\n const completionText =\n content +\n calls.map(toolCallUsageText).join(\"\") +\n (plan.refusal ?? \"\") +\n (exposeReasoning ? reasoning : \"\");\n return resolveUsage(undefined, promptText, completionText);\n}\n\n/** Project normalized ordered blocks without changing the fixture or generating ids. */\nexport function resolveFixtureBlockOutcome(blocks: FixtureFileBlock[]) {\n const ordered = resolveFixtureBlocks(blocks);\n let content = \"\";\n const toolCalls: ToolCall[] = [];\n for (const block of ordered) {\n if (block.type === \"text\") content += block.text;\n else\n toolCalls.push({\n name: block.name,\n arguments: block.arguments,\n ...(block.id !== undefined ? { id: block.id } : {}),\n });\n }\n return { ordered, content, toolCalls, hasToolCalls: toolCalls.length > 0 };\n}\n\n/**\n * Every tool call a response serves, in order: the function calls of\n * `toolCalls`, then the custom calls of `customToolCalls` (each tagged\n * `type: \"custom\"`). Only the OpenAI Responses API may serve the custom ones;\n * the other wires reject a non-empty `customToolCalls` first\n * ({@link rejectResponsesOnlyToolCalls}).\n */\nexport function servedToolCalls(response: {\n toolCalls?: ToolCall[];\n customToolCalls?: CustomToolCall[];\n}): FixtureToolCall[] {\n const calls: FixtureToolCall[] = [...(response.toolCalls ?? [])];\n for (const call of Array.isArray(response.customToolCalls) ? response.customToolCalls : []) {\n calls.push(isPlainObject(call) ? { ...call, type: \"custom\" } : call);\n }\n return calls;\n}\n\n/**\n * Resolve the ordered blocks a response serves, with each tool block projected\n * to its {@link FixtureToolCall}: a non-empty `responsesBlocks` (OpenAI\n * Responses only, through {@link resolveResponsesBlocks}), else a non-empty\n * legacy `blocks` (through {@link resolveFixtureBlocks}, which rejects a\n * `customToolCall` block with the error of earlier releases). Undefined when\n * the response has neither.\n */\nexport function resolveServedBlockOutcome(response: object):\n | {\n ordered: ResponsesFixtureBlock[];\n content: string;\n toolCalls: FixtureToolCall[];\n hasToolCalls: boolean;\n source: \"responsesBlocks\" | \"blocks\";\n }\n | undefined {\n const r = response as {\n blocks?: FixtureFileBlock[];\n responsesBlocks?: FixtureFileResponsesBlock[];\n };\n let ordered: ResponsesFixtureBlock[];\n let source: \"responsesBlocks\" | \"blocks\";\n if (Array.isArray(r.responsesBlocks) && r.responsesBlocks.length > 0) {\n ordered = resolveResponsesBlocks(r.responsesBlocks);\n source = \"responsesBlocks\";\n } else if (Array.isArray(r.blocks) && r.blocks.length > 0) {\n ordered = resolveFixtureBlocks(r.blocks);\n source = \"blocks\";\n } else return undefined;\n let content = \"\";\n const toolCalls: FixtureToolCall[] = [];\n for (const block of ordered) {\n if (block.type === \"text\") content += block.text;\n else toolCalls.push(fixtureBlockToolCall(block));\n }\n return { ordered, content, toolCalls, hasToolCalls: toolCalls.length > 0, source };\n}\n\n/**\n * Write rewritten served tool calls (and, with `ordered`, rebuilt ordered\n * blocks) back into a copy of `response`, split by carrier: function calls go\n * to `toolCalls`, custom calls to `customToolCalls`, and the blocks to\n * `responsesBlocks` when the response served those, else to `blocks`.\n */\nexport function withServedToolCalls<T extends object>(\n response: T,\n toolCalls: readonly FixtureToolCall[],\n ordered?: ResponsesFixtureBlock[],\n): T {\n const functionCalls: ToolCall[] = [];\n const customCalls: CustomToolCall[] = [];\n for (const call of toolCalls) {\n if (call.type === \"custom\") customCalls.push(call);\n else functionCalls.push(call);\n }\n const out: Record<string, unknown> = { ...response, toolCalls: functionCalls };\n if (customCalls.length > 0 || \"customToolCalls\" in response) out.customToolCalls = customCalls;\n if (ordered) {\n const r = response as { responsesBlocks?: unknown[] };\n if (Array.isArray(r.responsesBlocks) && r.responsesBlocks.length > 0) {\n out.responsesBlocks = ordered;\n } else {\n out.blocks = ordered;\n }\n }\n return out as T;\n}\n\n/** Project one normalized tool block to the matching served tool call. */\nexport function fixtureBlockToolCall(\n block: Exclude<ResponsesFixtureBlock, { type: \"text\" }>,\n): FixtureToolCall {\n const id = block.id !== undefined ? { id: block.id } : {};\n const namespace = block.namespace !== undefined ? { namespace: block.namespace } : {};\n return block.type === \"customToolCall\"\n ? { type: \"custom\", name: block.name, input: block.input, ...id, ...namespace }\n : { name: block.name, arguments: block.arguments, ...id, ...namespace };\n}\n\n/** Project one served tool call back to its ordered tool block. */\nexport function toolCallFixtureBlock(\n call: FixtureToolCall,\n): Exclude<ResponsesFixtureBlock, { type: \"text\" }> {\n const id = call.id !== undefined ? { id: call.id } : {};\n const namespace = call.namespace !== undefined ? { namespace: call.namespace } : {};\n return call.type === \"custom\"\n ? { type: \"customToolCall\", name: call.name, input: call.input, ...id, ...namespace }\n : { type: \"toolCall\", name: call.name, arguments: call.arguments, ...id, ...namespace };\n}\n\n/**\n * Rebuild ordered blocks after their tool calls were rewritten one-for-one:\n * text blocks are copied and the Nth tool block becomes `toolCalls[N]`.\n */\nexport function rebuildOrderedBlocks(\n ordered: readonly ResponsesFixtureBlock[],\n toolCalls: readonly FixtureToolCall[],\n): ResponsesFixtureBlock[] {\n let callIndex = 0;\n return ordered.map((block) =>\n block.type === \"text\" ? { ...block } : toolCallFixtureBlock(toolCalls[callIndex++]),\n );\n}\n\nexport function isErrorResponse(r: FixtureResponse): r is ErrorResponse {\n return (\n \"error\" in r &&\n (r as ErrorResponse).error !== null &&\n typeof (r as ErrorResponse).error === \"object\" &&\n \"message\" in ((r as ErrorResponse).error as Record<string, unknown>) &&\n typeof ((r as ErrorResponse).error as Record<string, unknown>).message === \"string\"\n );\n}\n\n/**\n * Serialize an ErrorResponse to JSON, stripping the internal-only `status`\n * field that controls the HTTP status code but should never appear in the\n * response body. Real LLM APIs don't include it.\n */\nexport function serializeErrorResponse(response: ErrorResponse): string {\n return JSON.stringify({\n error: {\n message: response.error.message,\n type: response.error.type ?? \"server_error\",\n param: response.error.param ?? null,\n code: response.error.code ?? null,\n },\n });\n}\n\nexport function isEmbeddingResponse(r: FixtureResponse): r is EmbeddingResponse {\n return \"embedding\" in r && Array.isArray((r as EmbeddingResponse).embedding);\n}\n\nexport function isImageResponse(r: FixtureResponse): r is ImageResponse {\n return (\n (\"image\" in r && typeof r.image === \"object\" && r.image != null) ||\n (\"images\" in r && Array.isArray((r as ImageResponse).images))\n );\n}\n\nexport function isAudioResponse(r: FixtureResponse): r is AudioResponse {\n if (!(\"audio\" in r)) return false;\n const a = (r as AudioResponse).audio;\n return typeof a === \"string\" || (typeof a === \"object\" && a !== null && \"b64Json\" in a);\n}\n\n/**\n * Map audio format shorthand to MIME content types.\n * Shared between speech, ElevenLabs, and fal audio handlers.\n */\nexport const FORMAT_TO_CONTENT_TYPE: Record<string, string> = {\n mp3: \"audio/mpeg\",\n opus: \"audio/opus\",\n aac: \"audio/aac\",\n flac: \"audio/flac\",\n wav: \"audio/wav\",\n pcm: \"audio/pcm\",\n};\n\n/**\n * Resolve a format string (e.g. \"mp3\", \"opus\") to its MIME content type.\n * Falls back to \"application/octet-stream\" for unknown formats.\n */\nexport function formatToMime(format: string): string {\n return FORMAT_TO_CONTENT_TYPE[format] ?? \"application/octet-stream\";\n}\n\nexport function isTranscriptionResponse(r: FixtureResponse): r is TranscriptionResponse {\n return (\n \"transcription\" in r &&\n (r as TranscriptionResponse).transcription != null &&\n typeof (r as TranscriptionResponse).transcription === \"object\"\n );\n}\n\nexport function isVideoResponse(r: FixtureResponse): r is VideoResponse {\n return (\n \"video\" in r &&\n (r as VideoResponse).video != null &&\n typeof (r as VideoResponse).video === \"object\"\n );\n}\n\nexport function isJSONResponse(r: FixtureResponse): r is RawJSONResponse {\n return \"json\" in r && (r as RawJSONResponse).json !== undefined;\n}\n\nexport function extractOverrides(\n response: TextResponse | ToolCallResponse | ContentWithToolCallsResponse,\n): ResponseOverrides {\n const r = response;\n return {\n ...(r.id !== undefined && { id: r.id }),\n ...(r.created !== undefined && { created: r.created }),\n ...(r.model !== undefined && { model: r.model }),\n ...(r.usage !== undefined && { usage: r.usage }),\n ...(r.systemFingerprint !== undefined && { systemFingerprint: r.systemFingerprint }),\n ...(r.finishReason !== undefined && { finishReason: r.finishReason }),\n ...(r.role !== undefined && { role: r.role }),\n ...(r.provider !== undefined && { provider: r.provider }),\n ...(r.nativeFinishReason !== undefined && { nativeFinishReason: r.nativeFinishReason }),\n };\n}\n\n// ─── Token estimation ────────────────────────────────────────────────────\n\n/**\n * Rough token count estimation based on character length.\n * Uses the ~4 characters per token heuristic common for English text.\n */\nexport function estimateTokens(text: string): number {\n return Math.max(1, Math.ceil(text.length / 4));\n}\n\n/**\n * Estimate prompt tokens from a request's messages array.\n */\nexport function estimatePromptTokens(messages: ChatCompletionRequest[\"messages\"]): number {\n let totalChars = 0;\n for (const msg of messages) {\n if (typeof msg.content === \"string\") {\n totalChars += msg.content.length;\n } else if (Array.isArray(msg.content)) {\n for (const part of msg.content) {\n if (part.text) totalChars += part.text.length;\n }\n }\n }\n return Math.max(1, Math.ceil(totalChars / 4));\n}\n\n/**\n * Build usage object: use explicit overrides if provided, otherwise estimate.\n *\n * Shared by BOTH the non-streaming completion builders (below) and the\n * streaming usage-chunk sites in server.ts — the single source of truth for\n * \"explicit token override wins, cost-only override still estimates\". Exported\n * so the streaming path does not re-implement the estimation logic.\n */\nexport function resolveUsage(\n overrides: ResponseOverrides | undefined,\n promptText: string,\n completionText: string,\n): { prompt_tokens: number; completion_tokens: number; total_tokens: number } {\n if (overrides?.usage) {\n const u = overrides.usage;\n // A usage override that scripts only cost fields (e.g. `{ cost: 0.5 }`)\n // carries NO token counts. Real providers always report real token usage,\n // so estimate the counts rather than forcing them to 0 — otherwise a\n // cost-scripting fixture is unfaithful (cost present, tokens 0). When the\n // override DOES set any token count we preserve the explicit-or-zero merge\n // behavior, and any explicit token value always wins.\n const hasExplicitTokens =\n u.prompt_tokens !== undefined ||\n u.completion_tokens !== undefined ||\n u.total_tokens !== undefined ||\n u.input_tokens !== undefined ||\n u.output_tokens !== undefined ||\n u.promptTokenCount !== undefined ||\n u.candidatesTokenCount !== undefined ||\n u.totalTokenCount !== undefined;\n const fallbackPrompt = hasExplicitTokens ? 0 : estimateTokens(promptText || \"x\");\n const fallbackCompletion = hasExplicitTokens ? 0 : estimateTokens(completionText || \"x\");\n const prompt = u.prompt_tokens ?? u.input_tokens ?? u.promptTokenCount ?? fallbackPrompt;\n const completion =\n u.completion_tokens ?? u.output_tokens ?? u.candidatesTokenCount ?? fallbackCompletion;\n return {\n prompt_tokens: prompt,\n completion_tokens: completion,\n total_tokens: u.total_tokens ?? u.totalTokenCount ?? prompt + completion,\n };\n }\n const prompt = estimateTokens(promptText || \"x\");\n const completion = estimateTokens(completionText || \"x\");\n return {\n prompt_tokens: prompt,\n completion_tokens: completion,\n total_tokens: prompt + completion,\n };\n}\n\n/**\n * Build an SSE usage chunk for streaming responses.\n * OpenAI emits this as the final chunk before [DONE] when\n * stream_options.include_usage is true. It has an empty choices array.\n */\nexport function buildUsageChunk(\n id: string,\n model: string,\n created: number,\n usage: { prompt_tokens: number; completion_tokens: number; total_tokens: number },\n fingerprint?: string,\n): SSEChunk {\n return {\n id,\n object: \"chat.completion.chunk\",\n created,\n model,\n choices: [],\n usage,\n ...(fingerprint !== undefined && { system_fingerprint: fingerprint }),\n };\n}\n\nexport function buildTextChunks(\n content: string,\n model: string,\n chunkSize: number,\n reasoning?: string,\n overrides?: ResponseOverrides,\n): SSEChunk[] {\n const id = overrides?.id ?? generateId();\n const created = overrides?.created ?? Math.floor(Date.now() / 1000);\n const effectiveModel = overrides?.model ?? model;\n const chunks: SSEChunk[] = [];\n const fingerprint = overrides?.systemFingerprint;\n\n // Reasoning chunks (emitted before content, OpenRouter format)\n if (reasoning) {\n for (let i = 0; i < reasoning.length; i += chunkSize) {\n const slice = reasoning.slice(i, i + chunkSize);\n chunks.push({\n id,\n object: \"chat.completion.chunk\",\n created,\n model: effectiveModel,\n choices: [\n { index: 0, delta: { reasoning_content: slice }, logprobs: null, finish_reason: null },\n ],\n ...(fingerprint !== undefined && { system_fingerprint: fingerprint }),\n });\n }\n }\n\n // Role chunk\n chunks.push({\n id,\n object: \"chat.completion.chunk\",\n created,\n model: effectiveModel,\n choices: [\n {\n index: 0,\n delta: { role: overrides?.role ?? \"assistant\", content: \"\" },\n logprobs: null,\n finish_reason: null,\n },\n ],\n ...(fingerprint !== undefined && { system_fingerprint: fingerprint }),\n });\n\n // Content chunks\n for (let i = 0; i < content.length; i += chunkSize) {\n const slice = content.slice(i, i + chunkSize);\n chunks.push({\n id,\n object: \"chat.completion.chunk\",\n created,\n model: effectiveModel,\n choices: [{ index: 0, delta: { content: slice }, logprobs: null, finish_reason: null }],\n ...(fingerprint !== undefined && { system_fingerprint: fingerprint }),\n });\n }\n\n // Finish chunk\n chunks.push({\n id,\n object: \"chat.completion.chunk\",\n created,\n model: effectiveModel,\n choices: [\n { index: 0, delta: {}, logprobs: null, finish_reason: overrides?.finishReason ?? \"stop\" },\n ],\n ...(fingerprint !== undefined && { system_fingerprint: fingerprint }),\n });\n\n return chunks;\n}\n\n/** K9 exposure is selected by the provider path, never the echoed response model. */\nexport function buildOpenAIReasoningChunks(\n reasoning: string,\n model: string,\n chunkSize: number,\n exposeReasoning: boolean,\n overrides?: ResponseOverrides,\n): SSEChunk[] {\n return buildTextChunks(exposeReasoning ? reasoning : \"\", model, chunkSize, undefined, {\n ...overrides,\n role: \"assistant\",\n finishReason: \"length\",\n }).map((chunk) => ({\n ...chunk,\n choices: chunk.choices.map((choice) => ({\n ...choice,\n delta:\n choice.delta.role !== undefined\n ? { role: \"assistant\", content: null }\n : typeof choice.delta.content === \"string\"\n ? {\n role: \"assistant\",\n content: \"\",\n reasoning: choice.delta.content,\n reasoning_details: [\n {\n type: \"reasoning.text\" as const,\n text: choice.delta.content,\n format: \"unknown\" as const,\n index: 0,\n },\n ],\n }\n : {},\n })),\n }));\n}\n\n/** Render exposed reasoning or native OpenAI's empty length completion. */\nexport function buildOpenAIReasoningCompletion(\n reasoning: string,\n model: string,\n exposeReasoning: boolean,\n overrides?: ResponseOverrides,\n requestMessages?: ChatCompletionRequest[\"messages\"],\n): ChatCompletion {\n const visible = exposeReasoning ? reasoning : \"\";\n const completion = buildTextCompletion(\n visible,\n model,\n undefined,\n {\n ...overrides,\n role: \"assistant\",\n finishReason: \"length\",\n usage: undefined,\n },\n requestMessages,\n );\n return {\n ...completion,\n choices: completion.choices.map((choice) => ({\n ...choice,\n message: {\n role: \"assistant\",\n content: null,\n refusal: null,\n ...(exposeReasoning\n ? {\n reasoning: visible,\n reasoning_details: [\n {\n type: \"reasoning.text\" as const,\n text: visible,\n format: \"unknown\" as const,\n index: 0,\n },\n ],\n }\n : {}),\n },\n })),\n };\n}\n\n/** Render withheld output with only the role and native content-filter terminal. */\nexport function buildOpenAIContentFilterChunks(\n model: string,\n overrides?: ResponseOverrides,\n): SSEChunk[] {\n return buildTextChunks(\"\", model, 1, undefined, {\n ...overrides,\n role: \"assistant\",\n finishReason: \"content_filter\",\n }).map((chunk) => ({\n ...chunk,\n choices: chunk.choices.map((choice) => ({\n ...choice,\n delta: choice.delta.role !== undefined ? { role: \"assistant\", content: null } : {},\n })),\n }));\n}\n\n/** Non-streaming filtered output contains no answer, refusal text, reasoning, or calls. */\nexport function buildOpenAIContentFilterCompletion(\n model: string,\n overrides?: ResponseOverrides,\n requestMessages?: ChatCompletionRequest[\"messages\"],\n): ChatCompletion {\n const completion = buildTextCompletion(\n \"\",\n model,\n undefined,\n {\n ...overrides,\n role: \"assistant\",\n finishReason: \"content_filter\",\n usage: undefined,\n },\n requestMessages,\n );\n return {\n ...completion,\n choices: completion.choices.map((choice) => ({\n ...choice,\n message: { role: \"assistant\", content: null, refusal: null },\n })),\n };\n}\n\n/** Render an applied refusal on its own channel without ordinary content or reasoning. */\nexport function buildOpenAIRefusalChunks(\n refusal: string,\n model: string,\n chunkSize: number,\n overrides?: ResponseOverrides,\n) {\n return buildTextChunks(refusal, model, chunkSize, undefined, {\n ...overrides,\n role: \"assistant\",\n finishReason: \"stop\",\n }).map((chunk) => ({\n ...chunk,\n choices: chunk.choices.map((choice) => {\n const delta: typeof choice.delta & { refusal?: string } =\n choice.delta.role !== undefined\n ? { role: \"assistant\", content: null }\n : typeof choice.delta.content === \"string\"\n ? { refusal: choice.delta.content }\n : {};\n return { ...choice, delta };\n }),\n }));\n}\n\n/** Refusal-only counterpart of the ordinary text completion builder. */\nexport function buildOpenAIRefusalCompletion(\n refusal: string,\n model: string,\n overrides?: ResponseOverrides,\n requestMessages?: ChatCompletionRequest[\"messages\"],\n): ChatCompletion {\n const completion = buildTextCompletion(\n refusal,\n model,\n undefined,\n {\n ...overrides,\n role: \"assistant\",\n finishReason: \"stop\",\n usage: undefined,\n },\n requestMessages,\n );\n return {\n ...completion,\n choices: completion.choices.map((choice) => ({\n ...choice,\n message: { role: \"assistant\", content: null, refusal },\n })),\n };\n}\n\nexport function buildToolCallChunks(\n toolCalls: ToolCall[],\n model: string,\n chunkSize: number,\n reasoning?: string,\n overrides?: ResponseOverrides,\n): SSEChunk[] {\n const id = overrides?.id ?? generateId();\n const created = overrides?.created ?? Math.floor(Date.now() / 1000);\n const effectiveModel = overrides?.model ?? model;\n const chunks: SSEChunk[] = [];\n const fingerprint = overrides?.systemFingerprint;\n\n // Reasoning chunks (emitted before tool calls, OpenRouter format)\n if (reasoning) {\n for (let i = 0; i < reasoning.length; i += chunkSize) {\n const slice = reasoning.slice(i, i + chunkSize);\n chunks.push({\n id,\n object: \"chat.completion.chunk\",\n created,\n model: effectiveModel,\n choices: [\n { index: 0, delta: { reasoning_content: slice }, logprobs: null, finish_reason: null },\n ],\n ...(fingerprint !== undefined && { system_fingerprint: fingerprint }),\n });\n }\n }\n\n // Role chunk\n chunks.push({\n id,\n object: \"chat.completion.chunk\",\n created,\n model: effectiveModel,\n choices: [\n {\n index: 0,\n delta: { role: overrides?.role ?? \"assistant\", content: null },\n logprobs: null,\n finish_reason: null,\n },\n ],\n ...(fingerprint !== undefined && { system_fingerprint: fingerprint }),\n });\n\n // Tool call chunks — one initial chunk per tool call, then argument chunks\n for (let tcIdx = 0; tcIdx < toolCalls.length; tcIdx++) {\n const tc = toolCalls[tcIdx];\n const tcId = tc.id || generateToolCallId();\n\n // Initial tool call chunk (id + function name)\n chunks.push({\n id,\n object: \"chat.completion.chunk\",\n created,\n model: effectiveModel,\n choices: [\n {\n index: 0,\n delta: {\n tool_calls: [\n {\n index: tcIdx,\n id: tcId,\n type: \"function\",\n function: { name: tc.name, arguments: \"\" },\n },\n ],\n },\n logprobs: null,\n finish_reason: null,\n },\n ],\n ...(fingerprint !== undefined && { system_fingerprint: fingerprint }),\n });\n\n // Argument streaming chunks\n const args = tc.arguments;\n for (let i = 0; i < args.length; i += chunkSize) {\n const slice = args.slice(i, i + chunkSize);\n chunks.push({\n id,\n object: \"chat.completion.chunk\",\n created,\n model: effectiveModel,\n choices: [\n {\n index: 0,\n delta: {\n tool_calls: [{ index: tcIdx, function: { arguments: slice } }],\n },\n logprobs: null,\n finish_reason: null,\n },\n ],\n ...(fingerprint !== undefined && { system_fingerprint: fingerprint }),\n });\n }\n }\n\n // Finish chunk\n chunks.push({\n id,\n object: \"chat.completion.chunk\",\n created,\n model: effectiveModel,\n choices: [\n {\n index: 0,\n delta: {},\n logprobs: null,\n finish_reason: overrides?.finishReason ?? \"tool_calls\",\n },\n ],\n ...(fingerprint !== undefined && { system_fingerprint: fingerprint }),\n });\n\n return chunks;\n}\n\n// Non-streaming response builders\n\nexport function buildTextCompletion(\n content: string,\n model: string,\n reasoning?: string,\n overrides?: ResponseOverrides,\n requestMessages?: ChatCompletionRequest[\"messages\"],\n): ChatCompletion {\n const promptText = requestMessages\n ? requestMessages\n .map((m) =>\n typeof m.content === \"string\"\n ? m.content\n : Array.isArray(m.content)\n ? m.content.map((p) => p.text ?? \"\").join(\"\")\n : \"\",\n )\n .join(\"\")\n : \"\";\n return {\n id: overrides?.id ?? generateId(),\n object: \"chat.completion\",\n created: overrides?.created ?? Math.floor(Date.now() / 1000),\n model: overrides?.model ?? model,\n choices: [\n {\n index: 0,\n message: {\n role: overrides?.role ?? \"assistant\",\n content,\n refusal: null,\n ...(reasoning ? { reasoning_content: reasoning } : {}),\n },\n logprobs: null,\n finish_reason: overrides?.finishReason ?? \"stop\",\n },\n ],\n usage: resolveUsage(overrides, promptText, content),\n ...(overrides?.systemFingerprint !== undefined && {\n system_fingerprint: overrides.systemFingerprint,\n }),\n };\n}\n\nexport function buildToolCallCompletion(\n toolCalls: ToolCall[],\n model: string,\n reasoning?: string,\n overrides?: ResponseOverrides,\n requestMessages?: ChatCompletionRequest[\"messages\"],\n): ChatCompletion {\n const promptText = requestMessages\n ? requestMessages\n .map((m) =>\n typeof m.content === \"string\"\n ? m.content\n : Array.isArray(m.content)\n ? m.content.map((p) => p.text ?? \"\").join(\"\")\n : \"\",\n )\n .join(\"\")\n : \"\";\n const completionText = toolCalls.map((tc) => tc.name + tc.arguments).join(\"\");\n return {\n id: overrides?.id ?? generateId(),\n object: \"chat.completion\",\n created: overrides?.created ?? Math.floor(Date.now() / 1000),\n model: overrides?.model ?? model,\n choices: [\n {\n index: 0,\n message: {\n role: overrides?.role ?? \"assistant\",\n content: null,\n refusal: null,\n ...(reasoning ? { reasoning_content: reasoning } : {}),\n tool_calls: toolCalls.map((tc) => ({\n id: tc.id || generateToolCallId(),\n type: \"function\" as const,\n function: { name: tc.name, arguments: tc.arguments },\n })),\n },\n logprobs: null,\n finish_reason: overrides?.finishReason ?? \"tool_calls\",\n },\n ],\n usage: resolveUsage(overrides, promptText, completionText),\n ...(overrides?.systemFingerprint !== undefined && {\n system_fingerprint: overrides.systemFingerprint,\n }),\n };\n}\n\nexport function buildContentWithToolCallsChunks(\n content: string,\n toolCalls: ToolCall[],\n model: string,\n chunkSize: number,\n reasoning?: string,\n overrides?: ResponseOverrides,\n blocks?: FixtureBlock[],\n): SSEChunk[] {\n const id = overrides?.id ?? generateId();\n const created = overrides?.created ?? Math.floor(Date.now() / 1000);\n const effectiveModel = overrides?.model ?? model;\n const chunks: SSEChunk[] = [];\n const fingerprint = overrides?.systemFingerprint;\n\n if (blocks && blocks.length > 0) {\n // NEW: emit chunks in fixture block array order.\n //\n // DEGENERATE PROVIDER NOTE: in OpenAI chat-completions, `delta.content` and\n // `delta.tool_calls` are SEPARATE channels that the client merges with no\n // positional interleaving. So \"tool-call-before-text\" is NOT semantically\n // observable to a real client — it reassembles content and tool calls into\n // their own buckets regardless of chunk order. We still emit honest\n // array-order chunks (the SSE chunk SEQUENCE is the contract this path\n // asserts), but we do NOT fake interleaving the channel cannot express.\n const outcome = resolveFixtureBlockOutcome(blocks);\n const ordered = outcome.ordered;\n\n // Reasoning chunks (emitted first, OpenRouter format) — unchanged from legacy.\n if (reasoning) {\n for (let i = 0; i < reasoning.length; i += chunkSize) {\n const slice = reasoning.slice(i, i + chunkSize);\n chunks.push({\n id,\n object: \"chat.completion.chunk\",\n created,\n model: effectiveModel,\n choices: [\n {\n index: 0,\n delta: {\n ...(i === 0 && { role: overrides?.role ?? \"assistant\" }),\n reasoning_content: slice,\n },\n logprobs: null,\n finish_reason: null,\n },\n ],\n ...(fingerprint !== undefined && { system_fingerprint: fingerprint }),\n });\n }\n }\n\n // Role chunk — preserved exactly as the legacy path.\n chunks.push({\n id,\n object: \"chat.completion.chunk\",\n created,\n model: effectiveModel,\n choices: [\n {\n index: 0,\n delta: { role: overrides?.role ?? \"assistant\", content: \"\" },\n logprobs: null,\n finish_reason: null,\n },\n ],\n ...(fingerprint !== undefined && { system_fingerprint: fingerprint }),\n });\n\n // Tool-call `index` is assigned in encounter order across the block array.\n let tcIdx = 0;\n for (const block of ordered) {\n if (block.type === \"text\") {\n for (let i = 0; i < block.text.length; i += chunkSize) {\n const slice = block.text.slice(i, i + chunkSize);\n chunks.push({\n id,\n object: \"chat.completion.chunk\",\n created,\n model: effectiveModel,\n choices: [{ index: 0, delta: { content: slice }, logprobs: null, finish_reason: null }],\n ...(fingerprint !== undefined && { system_fingerprint: fingerprint }),\n });\n }\n } else {\n const tcId = block.id || generateToolCallId();\n\n // Initial tool call chunk (id + function name)\n chunks.push({\n id,\n object: \"chat.completion.chunk\",\n created,\n model: effectiveModel,\n choices: [\n {\n index: 0,\n delta: {\n tool_calls: [\n {\n index: tcIdx,\n id: tcId,\n type: \"function\",\n function: { name: block.name, arguments: \"\" },\n },\n ],\n },\n logprobs: null,\n finish_reason: null,\n },\n ],\n ...(fingerprint !== undefined && { system_fingerprint: fingerprint }),\n });\n\n // Argument streaming chunks\n const args = block.arguments;\n for (let i = 0; i < args.length; i += chunkSize) {\n const slice = args.slice(i, i + chunkSize);\n chunks.push({\n id,\n object: \"chat.completion.chunk\",\n created,\n model: effectiveModel,\n choices: [\n {\n index: 0,\n delta: {\n tool_calls: [{ index: tcIdx, function: { arguments: slice } }],\n },\n logprobs: null,\n finish_reason: null,\n },\n ],\n ...(fingerprint !== undefined && { system_fingerprint: fingerprint }),\n });\n }\n tcIdx++;\n }\n }\n\n // Derive the default terminal from the blocks actually emitted.\n chunks.push({\n id,\n object: \"chat.completion.chunk\",\n created,\n model: effectiveModel,\n choices: [\n {\n index: 0,\n delta: {},\n logprobs: null,\n finish_reason: overrides?.finishReason ?? (outcome.hasToolCalls ? \"tool_calls\" : \"stop\"),\n },\n ],\n ...(fingerprint !== undefined && { system_fingerprint: fingerprint }),\n });\n\n return chunks;\n }\n\n // EXISTING legacy code, byte-for-byte UNCHANGED.\n // Reasoning chunks (emitted before content, OpenRouter format)\n if (reasoning) {\n for (let i = 0; i < reasoning.length; i += chunkSize) {\n const slice = reasoning.slice(i, i + chunkSize);\n chunks.push({\n id,\n object: \"chat.completion.chunk\",\n created,\n model: effectiveModel,\n choices: [\n {\n index: 0,\n delta: {\n ...(i === 0 && { role: overrides?.role ?? \"assistant\" }),\n reasoning_content: slice,\n },\n logprobs: null,\n finish_reason: null,\n },\n ],\n ...(fingerprint !== undefined && { system_fingerprint: fingerprint }),\n });\n }\n }\n\n // Role chunk\n chunks.push({\n id,\n object: \"chat.completion.chunk\",\n created,\n model: effectiveModel,\n choices: [\n {\n index: 0,\n delta: { role: overrides?.role ?? \"assistant\", content: \"\" },\n logprobs: null,\n finish_reason: null,\n },\n ],\n ...(fingerprint !== undefined && { system_fingerprint: fingerprint }),\n });\n\n // Content chunks\n for (let i = 0; i < content.length; i += chunkSize) {\n const slice = content.slice(i, i + chunkSize);\n chunks.push({\n id,\n object: \"chat.completion.chunk\",\n created,\n model: effectiveModel,\n choices: [{ index: 0, delta: { content: slice }, logprobs: null, finish_reason: null }],\n ...(fingerprint !== undefined && { system_fingerprint: fingerprint }),\n });\n }\n\n // Tool call chunks — one initial chunk per tool call, then argument chunks\n for (let tcIdx = 0; tcIdx < toolCalls.length; tcIdx++) {\n const tc = toolCalls[tcIdx];\n const tcId = tc.id || generateToolCallId();\n\n // Initial tool call chunk (id + function name)\n chunks.push({\n id,\n object: \"chat.completion.chunk\",\n created,\n model: effectiveModel,\n choices: [\n {\n index: 0,\n delta: {\n tool_calls: [\n {\n index: tcIdx,\n id: tcId,\n type: \"function\",\n function: { name: tc.name, arguments: \"\" },\n },\n ],\n },\n logprobs: null,\n finish_reason: null,\n },\n ],\n ...(fingerprint !== undefined && { system_fingerprint: fingerprint }),\n });\n\n // Argument streaming chunks\n const args = tc.arguments;\n for (let i = 0; i < args.length; i += chunkSize) {\n const slice = args.slice(i, i + chunkSize);\n chunks.push({\n id,\n object: \"chat.completion.chunk\",\n created,\n model: effectiveModel,\n choices: [\n {\n index: 0,\n delta: {\n tool_calls: [{ index: tcIdx, function: { arguments: slice } }],\n },\n logprobs: null,\n finish_reason: null,\n },\n ],\n ...(fingerprint !== undefined && { system_fingerprint: fingerprint }),\n });\n }\n }\n\n // Finish chunk\n chunks.push({\n id,\n object: \"chat.completion.chunk\",\n created,\n model: effectiveModel,\n choices: [\n {\n index: 0,\n delta: {},\n logprobs: null,\n finish_reason: overrides?.finishReason ?? \"tool_calls\",\n },\n ],\n ...(fingerprint !== undefined && { system_fingerprint: fingerprint }),\n });\n\n return chunks;\n}\n\n// NOTE (#274): this NON-streaming OpenAI chat-completions builder is\n// intentionally degenerate w.r.t. `blocks` ordering. A chat.completion puts\n// `message.content` and `message.tool_calls` in SEPARATE fields on a single\n// message object — they are NOT a positionally-observable array, so a\n// tool-first `blocks` fixture cannot be expressed in the wire shape. Honoring\n// block order here would be a no-op, so the legacy content+tool_calls fields\n// are unchanged. (Order-observable surfaces — Claude `content[]`, Gemini\n// `parts[]`, Responses `output[]` — DO honor block order; see those builders.)\nexport function buildContentWithToolCallsCompletion(\n content: string,\n toolCalls: ToolCall[],\n model: string,\n reasoning?: string,\n overrides?: ResponseOverrides,\n requestMessages?: ChatCompletionRequest[\"messages\"],\n): ChatCompletion {\n const promptText = requestMessages\n ? requestMessages\n .map((m) =>\n typeof m.content === \"string\"\n ? m.content\n : Array.isArray(m.content)\n ? m.content.map((p) => p.text ?? \"\").join(\"\")\n : \"\",\n )\n .join(\"\")\n : \"\";\n const completionText = content + toolCalls.map((tc) => tc.name + tc.arguments).join(\"\");\n return {\n id: overrides?.id ?? generateId(),\n object: \"chat.completion\",\n created: overrides?.created ?? Math.floor(Date.now() / 1000),\n model: overrides?.model ?? model,\n choices: [\n {\n index: 0,\n message: {\n role: overrides?.role ?? \"assistant\",\n content,\n refusal: null,\n ...(reasoning ? { reasoning_content: reasoning } : {}),\n tool_calls: toolCalls.map((tc) => ({\n id: tc.id || generateToolCallId(),\n type: \"function\" as const,\n function: { name: tc.name, arguments: tc.arguments },\n })),\n },\n logprobs: null,\n finish_reason: overrides?.finishReason ?? \"tool_calls\",\n },\n ],\n usage: resolveUsage(overrides, promptText, completionText),\n ...(overrides?.systemFingerprint !== undefined && {\n system_fingerprint: overrides.systemFingerprint,\n }),\n };\n}\n\n// ─── HTTP helpers ─────────────────────────────────────────────────────────\n\nconst DEFAULT_MAX_BODY_BYTES = 10 * 1024 * 1024; // 10 MB\n\n/**\n * A body read that stopped because the CLIENT sent more bytes than the route\n * allows. The socket is already destroyed when this rejects, so no status line\n * ever reaches the caller — but the fault is the caller's, and error arms that\n * journal and log the failure need to say so. Carrying the classification on\n * the error type (rather than re-matching the message text) is what lets them:\n * the message is unchanged from the plain `Error` this replaced.\n */\nexport class RequestBodyTooLargeError extends Error {\n constructor(readonly maxBytes: number) {\n super(`Request body exceeded size limit of ${maxBytes} bytes`);\n this.name = \"RequestBodyTooLargeError\";\n }\n}\n\n/**\n * Read a request body as raw bytes, preserving every octet.\n *\n * This is the byte-level primitive {@link readBody} is built on: routes that\n * must not lose bytes (binary file uploads) take the Buffer, everything else\n * keeps taking the decoded string. Splitting it this way leaves the text path\n * byte-identical — `readBody` performs exactly the same `Buffer.concat(...)`\n * + default (utf8) `toString()` it always did, just one call later.\n */\nexport function readBodyBuffer(\n req: http.IncomingMessage,\n maxBytes: number = DEFAULT_MAX_BODY_BYTES,\n): Promise<Buffer> {\n return new Promise((resolve, reject) => {\n const chunks: Buffer[] = [];\n let totalBytes = 0;\n let settled = false;\n req.on(\"data\", (chunk: Buffer) => {\n if (settled) return;\n totalBytes += chunk.length;\n if (totalBytes > maxBytes) {\n settled = true;\n req.destroy();\n reject(new RequestBodyTooLargeError(maxBytes));\n return;\n }\n chunks.push(chunk);\n });\n req.on(\"end\", () => {\n if (!settled) {\n settled = true;\n resolve(Buffer.concat(chunks));\n }\n });\n req.on(\"error\", (err) => {\n if (!settled) {\n settled = true;\n reject(err);\n }\n });\n });\n}\n\n/**\n * A body read with a buffering bound that does NOT cost the caller its socket.\n *\n * `buffer` is `null` when the body went over the buffering bound: the bytes\n * read so far were released and the rest was drained, so the request still\n * reaches `end` and the route can answer a real status (with CORS) instead of\n * the client seeing an `ECONNRESET`.\n */\nexport interface BoundedBody {\n buffer: Buffer | null;\n /** Bytes seen on the wire, counted past the buffering bound. */\n bytesRead: number;\n}\n\n/**\n * Read a request body that may legitimately be over-size, without either\n * buffering it all or dropping the socket.\n *\n * {@link readBodyBuffer} answers an over-size body with `req.destroy()`: no\n * status line, no body, no CORS headers. That is the right default for routes\n * whose limit is a pure DoS bound, but it is the wrong answer for a route that\n * owes the caller a `400` describing what was wrong — the caller cannot read an\n * error it never receives. So this variant splits the two concerns:\n *\n * - `maxBytes` bounds MEMORY. Past it the buffered chunks are dropped and the\n * body is only counted, so peak retention never exceeds `maxBytes` no matter\n * how large the body is. The caller gets `buffer: null` and answers itself.\n * - `drainMaxBytes` bounds WORK, and is the only socket drop here: a body that\n * keeps coming past it is not worth draining and rejects as `readBodyBuffer`\n * would.\n */\nexport function readBodyBufferBounded(\n req: http.IncomingMessage,\n maxBytes: number,\n drainMaxBytes: number,\n): Promise<BoundedBody> {\n return new Promise((resolve, reject) => {\n let chunks: Buffer[] | null = [];\n let totalBytes = 0;\n let settled = false;\n req.on(\"data\", (chunk: Buffer) => {\n if (settled) return;\n totalBytes += chunk.length;\n if (chunks !== null && totalBytes > maxBytes) {\n // Release on the FIRST chunk over the bound, before pushing it: the\n // bytes are unusable now, and holding them is what would make peak\n // memory track the attacker's body size instead of `maxBytes`.\n chunks = null;\n }\n if (totalBytes > drainMaxBytes) {\n settled = true;\n req.destroy();\n reject(new RequestBodyTooLargeError(drainMaxBytes));\n return;\n }\n if (chunks !== null) chunks.push(chunk);\n });\n req.on(\"end\", () => {\n if (settled) return;\n settled = true;\n resolve({ buffer: chunks === null ? null : Buffer.concat(chunks), bytesRead: totalBytes });\n });\n req.on(\"error\", (err) => {\n if (!settled) {\n settled = true;\n reject(err);\n }\n });\n });\n}\n\nexport function readBody(\n req: http.IncomingMessage,\n maxBytes: number = DEFAULT_MAX_BODY_BYTES,\n): Promise<string> {\n return readBodyBuffer(req, maxBytes).then((buf) => buf.toString());\n}\n\n// ─── Pattern matching ─────────────────────────────────────────────────────\n\n/**\n * Case-insensitive substring/regex match used for search, rerank, and\n * moderation endpoints where exact casing rarely matters. String patterns\n * are lowercased on both sides before comparison.\n *\n * Note: This intentionally differs from the case-sensitive matching in\n * {@link matchFixture} (router.ts), where fixture authors expect exact\n * string matching against chat completion user messages.\n */\nexport function matchesPattern(text: string, pattern: string | RegExp): boolean {\n if (typeof pattern === \"string\") {\n return text.toLowerCase().includes(pattern.toLowerCase());\n }\n // A global/sticky RegExp carries mutable `lastIndex` state that `.test()`\n // advances. Save and restore it so callers reusing the same regex object\n // (search/rerank/moderation filter loops) are not left with mutated state\n // and get consistent results across repeated calls.\n const savedLastIndex = pattern.lastIndex;\n pattern.lastIndex = 0;\n const result = pattern.test(text);\n pattern.lastIndex = savedLastIndex;\n return result;\n}\n\n/**\n * Which test LLM and other HTTP-route traffic belongs to: the `X-Test-Id`\n * header (raw, not percent-decoded) wins, then `?testId=` in the query string\n * (read through `URLSearchParams`, so percent-decoded and with `+` as a\n * space), then `DEFAULT_TEST_ID`. Every per-test axis of those routes\n * (fixture match-counts, the journal filter, chaos scoping) resolves through\n * this. MCP mounts resolve the test id with {@link resolveMcpIdentity}\n * instead, which percent-decodes the header, so `X-Test-Id: a%20b` is `a%20b`\n * here and `a b` there. Both read a `+` in the query as a space.\n */\nexport function resolveTestId(headers: IncomingHttpHeaders, url: string | undefined): string {\n const headerValue = headers[\"x-test-id\"];\n if (Array.isArray(headerValue)) {\n if (headerValue.length > 0 && headerValue[0]) return headerValue[0];\n } else if (typeof headerValue === \"string\" && headerValue) {\n return headerValue;\n }\n\n const qIdx = (url ?? \"/\").indexOf(\"?\");\n if (qIdx !== -1) {\n const params = new URLSearchParams((url ?? \"/\").slice(qIdx + 1));\n const queryValue = params.get(\"testId\");\n if (queryValue) return queryValue;\n }\n\n return DEFAULT_TEST_ID;\n}\n\nexport function getTestId(req: http.IncomingMessage): string {\n return resolveTestId(req.headers, req.url);\n}\n\nexport function getContext(req: http.IncomingMessage): string | undefined {\n const headerValue = req.headers[\"x-aimock-context\"];\n if (Array.isArray(headerValue)) {\n if (headerValue.length > 0 && headerValue[0]) return headerValue[0];\n } else if (typeof headerValue === \"string\" && headerValue) {\n return headerValue;\n }\n return undefined;\n}\n\n/**\n * Decode one MCP identity value: an `X-Test-Id` / `X-AIMock-Context` header\n * value, and also each query name and (through {@link decodeMcpQueryValue})\n * each `?testId=` / `?context=` / `?undeclared=` query value. Clients send `encodeURIComponent(id)` because\n * `fetch` rejects characters such as `›` in a header. A value that is not\n * valid percent-encoding (a raw `applies 50% discount`) is used as-is, with\n * `fellBack: true` so the caller can log the fallback. Only that `URIError`\n * is caught; any other throw propagates. MCP requests only: LLM routes read\n * the header raw and the query through `URLSearchParams` (see\n * {@link resolveTestId}).\n */\nexport function decodeMcpHeaderValue(raw: string): { value: string; fellBack: boolean } {\n try {\n return { value: decodeURIComponent(raw), fellBack: false };\n } catch (err) {\n if (!(err instanceof URIError)) throw err;\n return { value: raw, fellBack: true };\n }\n}\n\n/**\n * Decode one MCP query value (`?testId=`, `?context=`, `?undeclared=`) the way\n * `URLSearchParams` reads it (spec I1): a `+` is a space, then the value is\n * percent-decoded by the {@link decodeMcpHeaderValue} rule. So a query built\n * with `URL.searchParams.set(...)` (a space sent as `+`) and one built with\n * `encodeURIComponent` both resolve to the id itself. A value that is not\n * valid percent-encoding is used as-is (its `+` still a space), with\n * `fellBack: true`.\n */\nfunction decodeMcpQueryValue(raw: string): { value: string; fellBack: boolean } {\n return decodeMcpHeaderValue(raw.replace(/\\+/g, \" \"));\n}\n\n/** The parts of a request that {@link resolveMcpIdentity} reads. */\nexport type McpIdentityRequest = Pick<http.IncomingMessage, \"headers\" | \"url\"> &\n Partial<Pick<http.IncomingMessage, \"headersDistinct\">>;\n\n/**\n * Every value of the header `name`, one per header line: `[]` when absent.\n * Node joins a repeated header into one comma-joined `req.headers` string, so\n * the values come from `req.headersDistinct` (one element per line) when the\n * request has it. A caller without it (a hand-built request) is read from\n * `req.headers`: one element for a string, each element for an array.\n */\nfunction headerValues(req: McpIdentityRequest, name: string): string[] {\n const distinct = req.headersDistinct?.[name];\n if (distinct !== undefined) return distinct;\n const value = req.headers[name];\n if (value === undefined) return [];\n return Array.isArray(value) ? value : [value];\n}\n\n/** One `name=value` pair of a query string, its name decoded. */\ninterface McpQueryPair {\n /** The decoded name, or `null` when the name is not valid percent-encoding. */\n name: string | null;\n /** The raw (still percent-encoded) value; `\"\"` for a bare name with no `=`. */\n value: string;\n}\n\n/**\n * Every pair of a query string, in order, each name decoded by the\n * `decodeMcpHeaderValue` rule. A bare `name` with no `=` counts as an\n * occurrence with an empty value, so `?testId&testId=t1` has two. Each value\n * is left raw for the caller to decode with {@link decodeMcpQueryValue}.\n * A name that is not valid percent-encoding (`test%Id`) gets `name: null`: it\n * matches no field, and the caller counts it.\n */\nfunction queryPairs(query: string): McpQueryPair[] {\n if (query === \"\") return [];\n return query.split(\"&\").map((pair) => {\n const eq = pair.indexOf(\"=\");\n const key = decodeMcpHeaderValue(eq === -1 ? pair : pair.slice(0, eq));\n return { name: key.fellBack ? null : key.value, value: eq === -1 ? \"\" : pair.slice(eq + 1) };\n });\n}\n\n/**\n * A request value as a message part, the way the message builder quotes every\n * user string: JSON-quoted (quotes and control characters escaped), then cut\n * once as JSON text, so the whole echo, quotes, escapes and the\n * `… (<n> more chars)` count included, fits in `MCP_FAKES_ECHO_LIMIT` and the\n * cut never splits a JSON escape or a surrogate pair.\n */\nfunction echoIdentityValue(value: string): MessageValue {\n return quote(value, MCP_FAKES_ECHO_LIMIT);\n}\n\n/** The session fields an MCP session binds at `initialize`. */\ntype McpIdentitySession = Pick<MCPSession, \"testId\" | \"context\" | \"undeclared\">;\n\n/** An MCP identity input: the test id, the context or the undeclared override. */\nexport type McpIdentityField = \"testId\" | \"context\" | \"undeclared\";\n\n/**\n * Why an MCP request's identity inputs cannot be honored. The MCP mount is\n * meant to answer `status` with `{ \"error\": message }` and serve nothing.\n * - `MCP_INVALID_UNDECLARED`: an undeclared override (`X-AIMock-MCP-Undeclared`\n * / `?undeclared=`) other than `allow` / `deny`, so a typo such as `denied`\n * can never turn a `deny` scenario into the permissive default. `raw` is\n * the value as sent (the query value still percent-encoded), not quoted,\n * cut by `cutText` to `MCP_FAKES_ECHO_LIMIT` chars, so a huge value is\n * never carried in full. A query value that\n * is not valid percent-encoding is also an error, and the message says so.\n * - `MCP_DUPLICATE_IDENTITY`: one field sent more than once on one path (a\n * header array with more than one element, or a repeated query name, empty\n * or not). No value is picked. `count` is how many were sent.\n */\nexport type McpIdentityError =\n | {\n code: \"MCP_INVALID_UNDECLARED\";\n status: 400;\n field: \"undeclared\";\n source: \"header\" | \"query\";\n raw: string;\n message: string;\n }\n | {\n code: \"MCP_DUPLICATE_IDENTITY\";\n status: 400;\n field: McpIdentityField;\n source: \"header\" | \"query\";\n count: number;\n message: string;\n };\n\n/** The result of {@link resolveMcpIdentity}: a resolved identity, or an error to answer with. */\nexport type McpIdentityResolution =\n | {\n ok: true;\n identity: McpFakeIdentity;\n /** Whether the request itself carried each field (raw header/query, not the session). */\n supplied: { testId: boolean; context: boolean; undeclared: boolean };\n /**\n * Whether a header or query value sent for each field was not valid\n * percent-encoding and fell back to the raw value. Set also when the\n * header supplied the field and the ignored query value did not decode,\n * so bad encoding is never silent. For the MCP mount to log a warning,\n * once per session per field.\n */\n fellBack: { testId: boolean; context: boolean };\n /**\n * How many query names were not valid percent-encoding (`?test%Id=`).\n * Such a name matches no field. Present only when at least one was\n * found, for the MCP mount to log.\n */\n undecodedQueryNames?: number;\n }\n | { ok: false; error: McpIdentityError };\n\nconst MCP_IDENTITY_INPUTS: Record<\n McpIdentityField,\n { header: string; query: string; label: string }\n> = {\n testId: { header: \"x-test-id\", query: \"testId\", label: \"X-Test-Id\" },\n context: { header: \"x-aimock-context\", query: \"context\", label: \"X-AIMock-Context\" },\n undeclared: {\n header: \"x-aimock-mcp-undeclared\",\n query: \"undeclared\",\n label: \"X-AIMock-MCP-Undeclared\",\n },\n};\n\n/**\n * Resolve the identity (test id, context, undeclared override) of one MCP request.\n * Each field resolves on its own, first of: the header, the query parameter,\n * the session value bound at `initialize`. A field sent more than once on\n * either path (more than one header line, read from `headersDistinct` because\n * Node joins repeated lines into one `headers` string; a header array from a\n * hand-built request; a repeated query name) is an error\n * (`MCP_DUPLICATE_IDENTITY`), even when the other path also carries it. For\n * the test id and the context, a header value is decoded with\n * `decodeURIComponent` (raw value on failure, with `fellBack` set) and a query\n * value the way `URLSearchParams` reads it (spec I1): a `+` is a space, then\n * the same `decodeURIComponent` rule. So `encodeURIComponent` output (what\n * clients send) and a query built with `URL.searchParams` both decode to the\n * id itself, while a raw `+` is a `+` in the header and a space in the query.\n * When both paths carry a test id or a context, the header is used and the\n * query value is ignored, even if the two differ, but an ignored query value\n * that does not decode still sets `fellBack`. The undeclared header is not\n * percent-decoded; its query value is, by the same query rule. Both are\n * trimmed and compared case-insensitively. `supplied` says whether the\n * request itself carried a non-empty header or query value for the field,\n * never `resolveTestId`'s result (which returns `\"__default__\"` when nothing\n * is sent). Blank values: an empty value is not supplied and falls through;\n * it is never rejected. A whitespace-only value is not supplied for the\n * undeclared override (it is trimmed), but is a real value for the test id\n * and the context, which match exactly and are never trimmed (Node trims a\n * header value, so only the query or a hand-built request can carry one). A\n * lone query name with no `=` carries no value. An empty session value counts\n * as none, and a missing `req.url` as `/`. A field with no value anywhere is\n * `null`. An unrecognized undeclared override on the header or the query is\n * an error (`ok: false`), not a fall-through: both are checked before the\n * header one is used, so a valid header does not hide a mistyped query value.\n * Bad percent-encoding is never silent: a test id or context value that does\n * not decode sets `fellBack`, on either path, used or ignored; an undeclared\n * query value that does not decode is that error, with the message saying\n * so; and a query name that does not decode matches no field and is counted\n * in `undecodedQueryNames`. `resolveTestId` / `getContext` stay unchanged for\n * LLM routes.\n */\nexport function resolveMcpIdentity(\n req: McpIdentityRequest,\n session?: McpIdentitySession,\n): McpIdentityResolution {\n const url = req.url ?? \"/\";\n const qIdx = url.indexOf(\"?\");\n const pairs = queryPairs(qIdx === -1 ? \"\" : url.slice(qIdx + 1));\n\n // Collect each field's one raw value per path, rejecting any repetition first.\n const raw = {} as Record<McpIdentityField, { header?: string; query?: string }>;\n for (const field of [\"testId\", \"context\", \"undeclared\"] as const) {\n const input = MCP_IDENTITY_INPUTS[field];\n const sent = [\n [\"header\", headerValues(req, input.header)],\n [\"query\", pairs.filter((p) => p.name === input.query).map((p) => p.value)],\n ] as const;\n for (const [source, values] of sent) {\n if (values.length > 1) {\n const label =\n source === \"header\"\n ? msg`${fixed(input.label)} header`\n : msg`?${fixed(input.query)}= query parameter`;\n return {\n ok: false,\n error: {\n code: \"MCP_DUPLICATE_IDENTITY\",\n status: 400,\n field,\n source,\n count: values.length,\n message: build(msg`Duplicate ${label}: ${values.length} values sent, expected one`),\n },\n };\n }\n }\n raw[field] = { header: sent[0][1][0], query: sent[1][1][0] };\n }\n\n const fromRequest = (\n field: \"testId\" | \"context\",\n ): { value: string | null; fellBack: boolean } => {\n const { header, query: q } = raw[field];\n const fromQuery = q ? decodeMcpQueryValue(q) : null;\n if (header) {\n // The header wins; an ignored query value that does not decode still flags.\n const used = decodeMcpHeaderValue(header);\n return { value: used.value, fellBack: used.fellBack || (fromQuery?.fellBack ?? false) };\n }\n return fromQuery ?? { value: null, fellBack: false };\n };\n\n const testId = fromRequest(\"testId\");\n const context = fromRequest(\"context\");\n\n // Check every supplied undeclared value before using one: the header wins,\n // but a mistyped query value next to it is still an error.\n let undeclared: McpFakeUndeclaredPolicy | null = null;\n for (const source of [\"header\", \"query\"] as const) {\n const sent = raw.undeclared[source];\n if (sent === undefined) continue;\n // Header decoding covers only X-Test-Id / X-AIMock-Context; this header is\n // not percent-decoded. The query value is decoded.\n const decoded =\n source === \"header\" ? { value: sent, fellBack: false } : decodeMcpQueryValue(sent);\n if (decoded.value.trim() === \"\") continue;\n const parsed = parseMcpUndeclared(decoded.value);\n if (parsed === null) {\n const echoed = echoIdentityValue(sent);\n const label =\n source === \"header\"\n ? msg`X-AIMock-MCP-Undeclared header`\n : msg`?undeclared= query parameter`;\n const note = decoded.fellBack ? msg` (not valid percent-encoding)` : msg``;\n return {\n ok: false,\n error: {\n code: \"MCP_INVALID_UNDECLARED\",\n status: 400,\n field: \"undeclared\",\n source,\n raw: cutText(sent),\n message: build(msg`Invalid ${label} value ${echoed}${note}: expected allow or deny`),\n },\n };\n }\n undeclared ??= parsed;\n }\n\n const undecodedQueryNames = pairs.filter((p) => p.name === null).length;\n return {\n ok: true,\n identity: {\n testId: testId.value ?? (session?.testId || null),\n context: context.value ?? (session?.context || null),\n undeclared: undeclared ?? session?.undeclared ?? null,\n },\n supplied: {\n testId: testId.value !== null,\n context: context.value !== null,\n undeclared: undeclared !== null,\n },\n fellBack: { testId: testId.fellBack, context: context.fellBack },\n ...(undecodedQueryNames > 0 ? { undecodedQueryNames } : {}),\n };\n}\n\n// ─── Snapshot recording helpers ──────────────────────────────────────────────\n\n/**\n * Convert a test ID (e.g. Playwright titlePath) into a filesystem-safe slug\n * suitable for use as a directory name in snapshot-style recording.\n */\nexport function slugifyTestId(testId: string): string {\n return testId\n .replace(/^.*?\\.(?:spec|test|e2e)\\.(?:tsx|ts|jsx|js|mjs|cjs)(?=\\s|›|$)\\s*›?\\s*/i, \"\") // strip test file extension prefix\n .replace(/\\s*[›>]\\s*/g, \"--\") // Playwright titlePath separator → double dash\n .replace(/[^\\w-]/g, \"-\") // non-word chars → dash\n .replace(/-{3,}/g, \"--\") // collapse 3+ dashes to double\n .replace(/^-+|-+$/g, \"\") // trim leading/trailing dashes\n .toLowerCase();\n}\n\n/**\n * Make a request context (the `X-AIMock-Context` header value) safe to use as\n * a single directory segment in a recorded-fixture path. The header is\n * attacker-controllable, so a raw value containing `../`, path separators, or\n * an absolute-path prefix would let the written fixture escape the configured\n * fixtures base directory. Mirrors `slugifyTestId`: non-word characters\n * (including `/`, `\\`, and `.`) collapse to dashes, so the result is always a\n * single flat segment with no traversal semantics. Returns \"\" when the value\n * sanitizes to nothing (caller treats that as \"no context segment\").\n */\nexport function slugifyContext(context: string): string {\n return context\n .replace(/[^\\w-]/g, \"-\") // non-word chars (incl. / \\ . :) → dash\n .replace(/-{3,}/g, \"--\") // collapse 3+ dashes to double\n .replace(/^-+|-+$/g, \"\") // trim leading/trailing dashes\n .toLowerCase();\n}\n\n// ─── Request shape validation ──────────────────────────────────────────────\n\n/**\n * Validate the shape of a chat `messages` array (Cohere / Ollama / Bedrock\n * style: entries with `content` and optional `tool_calls`).\n *\n * Returns an error detail string when the shape is wrong, or null when the\n * provider converter can safely consume it. Wrong types here used to throw\n * inside the converters (`content.filter` on a number, `.map` on a string\n * `tools`, `msg.role` on a null entry) and surface as 500s. `null` content\n * is accepted — assistant messages legitimately carry `content: null`\n * alongside tool calls, and the converters coerce it to \"\".\n *\n * `checkToolCalls` must be false for providers whose converter never reads\n * `msg.tool_calls` (Bedrock: tool calls are `tool_use` content blocks and\n * `tool_calls` is not a field of the Anthropic Messages body at all). There a\n * stray `tool_calls` is inert — rejecting it turns a request that used to\n * match a fixture and return 200 into a 400.\n */\nexport function validateChatMessages(\n messages: unknown,\n { checkToolCalls = true }: { checkToolCalls?: boolean } = {},\n): string | null {\n // Matches the historic \"messages array is required\" detail so handlers can\n // compose the exact legacy message for a missing/non-array field.\n if (!Array.isArray(messages)) return \"messages array is required\";\n for (let i = 0; i < messages.length; i++) {\n const msg = messages[i] as Record<string, unknown> | null;\n if (msg === null || typeof msg !== \"object\" || Array.isArray(msg)) {\n return `messages[${i}] must be an object`;\n }\n const content = msg.content;\n if (\n content !== undefined &&\n content !== null &&\n typeof content !== \"string\" &&\n !Array.isArray(content)\n ) {\n return `messages[${i}].content must be a string or an array`;\n }\n if (Array.isArray(content) && content.some((p) => p === null || p === undefined)) {\n return `messages[${i}].content must be a string or an array`;\n }\n const toolCalls = msg.tool_calls;\n if (checkToolCalls && toolCalls !== undefined && toolCalls !== null) {\n if (!Array.isArray(toolCalls)) {\n return `messages[${i}].tool_calls must be an array`;\n }\n for (const tc of toolCalls) {\n if (tc === null || typeof tc !== \"object\" || Array.isArray(tc)) {\n return `messages[${i}].tool_calls entries must be objects`;\n }\n const fn = (tc as Record<string, unknown>).function;\n if (fn === null || typeof fn !== \"object\" || Array.isArray(fn)) {\n return `messages[${i}].tool_calls entries must have a function object`;\n }\n }\n }\n }\n return null;\n}\n\n/**\n * Validate an optional top-level `tools` field. A string (or other\n * non-array) value passes the `req.tools && req.tools.length` gate\n * (`\"hi\".length > 0`) and then throws on `.map` — a 500 instead of a 400.\n * Returns an error detail string, or null when absent or a valid array.\n */\nexport function validateToolsField(tools: unknown): string | null {\n if (tools !== undefined && tools !== null && !Array.isArray(tools)) {\n return \"tools must be an array\";\n }\n if (Array.isArray(tools)) {\n for (let i = 0; i < tools.length; i++) {\n const t = tools[i] as Record<string, unknown> | null;\n if (t === null || typeof t !== \"object\" || Array.isArray(t)) {\n return `tools[${i}] must be an object`;\n }\n }\n }\n return null;\n}\n\n// ─── Request body helpers ──────────────────────────────────────────────────\n\n/**\n * True when a parsed JSON request body is a plain object.\n *\n * `JSON.parse(\"null\")` yields `null`, and `\"123\"` / `\"\\\"hi\\\"\"` yield\n * non-objects — every handler that then reads `body.field` throws a TypeError\n * that surfaces as a 500. Call this right after `JSON.parse` and answer 400\n * (\"Request body must be a JSON object\") instead. Arrays are rejected too:\n * `[].field` reads as `undefined` today, which degrades into a misleading\n * \"missing parameter\" 400 — rejecting up front names the real problem.\n */\nexport function isJsonObject(body: unknown): body is Record<string, unknown> {\n return body !== null && typeof body === \"object\" && !Array.isArray(body);\n}\n\n// ─── Embedding helpers ─────────────────────────────────────────────────────\n\nconst DEFAULT_EMBEDDING_DIMENSIONS = 1536;\n\n/**\n * Maximum embedding dimensions accepted by POST /v1/embeddings.\n *\n * A serialization budget, not an allocation bound. The ECMAScript array-length\n * bound (2**32 - 1) is where `new Array(n)` throws RangeError, but the handler\n * does not stop at allocating: `generateDeterministicEmbedding` fills the array\n * and the response is JSON-serialized, so at that bound a single request aborts\n * the process (\"FATAL ERROR: CALL_AND_RETRY_LAST Allocation failed - JavaScript\n * heap out of memory\") before any response is written — taking every other test\n * sharing the server with it. A cap the server cannot serve is not a cap.\n *\n * Derivation, measured on this tree: a response body costs 19.58 bytes per\n * dimension (4096 -> 80,456 B; 100,000 -> 1,958,539 B; 1,000,000 ->\n * 19,583,034 B). At 100,000 the body is ~1.96 MB, built in 7 ms at 83 MB RSS\n * under a 1 GB heap — comfortably serviceable.\n *\n * Deliberately still not a model width: /v1/embeddings also serves Azure and\n * every OpenAI-compatible server routed through COMPAT_SUFFIXES, where\n * 4096-dimension models are ordinary. 100,000 leaves >12x headroom over the\n * widest width in circulation while keeping every accepted request answerable.\n */\nexport const MAX_EMBEDDING_DIMENSIONS = 100_000;\n\n/**\n * Validate an embeddings `dimensions` parameter.\n * Returns the effective dimensions (default 1536) or null when invalid.\n * Valid: undefined/null (→ default), integer in [1, MAX_EMBEDDING_DIMENSIONS].\n */\nexport function validateEmbeddingDimensions(raw: unknown): number | null {\n // null means \"unset\" too — the previous code was `dimensions ?? 1536`.\n if (raw === undefined || raw === null) return 1536;\n if (typeof raw !== \"number\" || !Number.isInteger(raw)) return null;\n if (raw < 1 || raw > MAX_EMBEDDING_DIMENSIONS) return null;\n return raw;\n}\n\n/**\n * Normalize an embeddings `input` into the texts to embed.\n * Accepts every shape EmbeddingCreateParams.input declares\n * (openai/resources/embeddings.d.ts): `string | string[] | number[] | number[][]`,\n * the last two being pre-tokenized input. Returns null for shapes the API\n * rejects (non-string scalars, objects, mixed arrays).\n */\nexport function normalizeEmbeddingInput(raw: unknown): string[] | null {\n if (typeof raw === \"string\") return [raw];\n if (Array.isArray(raw)) {\n if (raw.every((el) => typeof el === \"string\")) return raw as string[];\n // number[] — one pre-tokenized input.\n if (raw.every((el) => typeof el === \"number\")) return [raw.join(\" \")];\n // number[][] — a batch of pre-tokenized inputs.\n if (raw.every((el) => Array.isArray(el) && el.every((t) => typeof t === \"number\"))) {\n return (raw as number[][]).map((tokens) => tokens.join(\" \"));\n }\n return null;\n }\n return null;\n}\n\n/**\n * Normalize a free-text field (moderation input, search/rerank query).\n * Accepts a string, or any array — joined with \" \" as before, so no array that\n * used to return 200 starts failing, and ModerationCreateParams' multimodal\n * parts (openai/resources/moderations.d.ts) contribute their `.text`. Returns\n * null only for non-string, non-array values, which used to 500.\n */\nexport function normalizeTextInput(raw: unknown): string | null {\n if (typeof raw === \"string\") return raw;\n if (Array.isArray(raw)) {\n return raw\n .map((el) => {\n if (typeof el === \"string\") return el;\n if (el === null || el === undefined) return \"\";\n if (typeof el === \"object\") {\n const part = el as { type?: unknown; text?: unknown };\n return part.type === \"text\" && typeof part.text === \"string\" ? part.text : \"\";\n }\n return String(el);\n })\n .join(\" \");\n }\n return null;\n}\n\n/**\n * Generate a deterministic embedding vector from input text.\n * Hashes the input with SHA-256 and spreads the hash bytes across\n * the requested number of dimensions, producing values in [-1, 1].\n */\nexport function generateDeterministicEmbedding(\n input: string,\n dimensions: number = DEFAULT_EMBEDDING_DIMENSIONS,\n): number[] {\n let currentHash = createHash(\"sha256\").update(input).digest();\n const embedding: number[] = new Array(dimensions);\n for (let i = 0; i < dimensions; i++) {\n if (i > 0 && i % 32 === 0) {\n currentHash = createHash(\"sha256\").update(currentHash).digest();\n }\n // Map 0-255 → -1.0 to 1.0\n embedding[i] = currentHash[i % 32] / 127.5 - 1;\n }\n return embedding;\n}\n\nexport interface EmbeddingAPIResponse {\n object: \"list\";\n data: { object: \"embedding\"; index: number; embedding: number[] }[];\n model: string;\n usage: { prompt_tokens: number; total_tokens: number };\n}\n\n/**\n * Build an OpenAI-format embeddings API response for one or more inputs.\n */\nexport function buildEmbeddingResponse(\n embeddings: number[][],\n model: string,\n usage?: { prompt_tokens?: number; total_tokens?: number },\n): EmbeddingAPIResponse {\n return {\n object: \"list\",\n data: embeddings.map((embedding, index) => ({\n object: \"embedding\" as const,\n index,\n embedding,\n })),\n model,\n usage: { prompt_tokens: usage?.prompt_tokens ?? 0, total_tokens: usage?.total_tokens ?? 0 },\n };\n}\n\n/**\n * Build a stable, human-readable identifier for a fixture's match shape, for\n * any log line that has to name WHICH fixture it means. The `Fixture` type\n * carries no `id`/`name`, so the matchers are the only handle a reader has.\n *\n * Used by the relaxed-turnIndex warning (`router.ts`) and by the chaos\n * rejected-value warning (`chaos.ts`); one implementation so the two name the\n * same fixture the same way. The obvious `JSON.stringify(match)` is unfit: it\n * DROPS `predicate` functions (non-serialisable) and serialises any RegExp\n * matcher to `{}`, so a predicate- or regex-gated fixture's warning collapsed to\n * an uninformative \"served fixture {}\" / `{\"userMessage\":{}}` blob.\n *\n * Instead we list the PRESENT matcher keys in declaration order, annotating each\n * by VALUE KIND so predicates and regexes survive: `predicate(fn)`,\n * `userMessage(regex)`, `userMessage(\"hello\")`, `turnIndex=0`, etc. The\n * fixture's array `index` is prefixed as a stable positional identifier when\n * the caller knows it (i.e. `>= 0`); callers holding only the fixture object\n * pass `-1` and get the matcher summary alone. String/number values are shown\n * inline (truncated) so a content match remains recognisable; the whole string\n * is capped to keep the log line bounded.\n */\nconst DESCRIBE_MATCH_MAX = 160;\n\nexport function describeMatch(match: FixtureMatch, index: number): string {\n const parts: string[] = [];\n for (const [key, value] of Object.entries(match)) {\n if (value === undefined) continue;\n if (typeof value === \"function\") {\n parts.push(key === \"predicate\" ? \"[predicate]\" : `${key}[fn]`);\n } else if (value instanceof RegExp) {\n parts.push(`${key}(${value})`);\n } else if (typeof value === \"string\") {\n const v = value.length > 40 ? `${value.slice(0, 40)}…` : value;\n parts.push(`${key}(${JSON.stringify(v)})`);\n } else if (Array.isArray(value)) {\n parts.push(`${key}(${value.length} item${value.length === 1 ? \"\" : \"s\"})`);\n } else if (typeof value === \"object\" && value !== null) {\n // `String(obj)` is \"[object Object]\"; show the (bounded) JSON instead.\n const json = JSON.stringify(value);\n parts.push(`${key}(${json.length > 40 ? `${json.slice(0, 40)}…` : json})`);\n } else {\n parts.push(`${key}=${String(value)}`);\n }\n }\n const keys = parts.length > 0 ? parts.join(\", \") : \"no matchers\";\n const prefix = index >= 0 ? `#${index} ` : \"\";\n const full = `${prefix}{ ${keys} }`;\n if (full.length <= DESCRIBE_MATCH_MAX) return full;\n // Cut at a matcher boundary (never mid-token) and mark the elision; a lone\n // oversized matcher is hard-cut but still marked.\n const head = full.slice(0, DESCRIBE_MATCH_MAX - 4);\n const cut = head.lastIndexOf(\", \");\n return `${cut > 0 ? head.slice(0, cut) : head}, … }`;\n}\n\n// ─── MCP fakes: undeclared-tool override ────────────────────────────────────\n\n/**\n * Parse an `X-AIMock-MCP-Undeclared` header or `?undeclared=` value.\n * Modeled on {@link resolveStrictMode}: case-insensitive, surrounding\n * whitespace trimmed. Returns `null` when unrecognised; unlike the strict\n * header, {@link resolveMcpIdentity} turns that into an error rather than a\n * fall-through, because falling through would turn a mistyped `deny` into\n * the permissive default.\n */\nfunction parseMcpUndeclared(raw: string): McpFakeUndeclaredPolicy | null {\n const val = raw.trim().toLowerCase();\n if (val === \"allow\" || val === \"deny\") return val;\n return null;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"helpers.js","names":["estimateUsageTokens","estimateUsagePromptTokens","renderOpenAIUsage","resolveOpenAIUsage"],"sources":["../src/helpers.ts"],"sourcesContent":["import {\n resolve as resolveOpenAIUsage,\n render as renderOpenAIUsage,\n} from \"./response/usage/codecs/openai.js\";\nimport type { MisbehaviorPlan, ServedMisbehaviorToolCall } from \"./misbehavior.js\";\nimport type { LiveFixtureResponse } from \"./live-types.js\";\nimport { AsyncLocalStorage } from \"node:async_hooks\";\nimport { createHash, randomBytes } from \"node:crypto\";\nimport type * as http from \"node:http\";\nimport { renderError } from \"./response/errors.js\";\nimport type { IncomingHttpHeaders } from \"node:http\";\nimport { DEFAULT_TEST_ID, MCP_FAKES_ECHO_LIMIT } from \"./constants.js\";\nimport { cutText } from \"./echo-text.js\";\nimport { build, fixed, msg, quote, type MessageValue } from \"./message-text.js\";\nimport type { Logger } from \"./logger.js\";\nimport { isReasoningModel } from \"./model-utils.js\";\nimport { isRecognizedApiKeyHeader } from \"./api-key-auth.js\";\nimport {\n resolveStrictMode as resolvePolicyStrictMode,\n strictOverrideField as policyStrictOverrideField,\n} from \"./response/policy.js\";\nimport {\n estimatePromptTokens as estimateUsagePromptTokens,\n estimateTokens as estimateUsageTokens,\n} from \"./response/usage/estimate.js\";\nimport type {\n ChatCompletionRequest,\n Fixture,\n FixtureMatch,\n FixtureResponse,\n ResponseFactory,\n TextResponse,\n ToolCallResponse,\n ContentWithToolCallsResponse,\n ErrorResponse,\n EmbeddingResponse,\n ImageResponse,\n AudioResponse,\n TranscriptionResponse,\n VideoResponse,\n RawJSONResponse,\n SSEChunk,\n ToolCall,\n FixtureToolCall,\n CustomToolCall,\n JournalEntry,\n FixtureBlock,\n FixtureFileBlock,\n FixtureFileResponsesBlock,\n ResponsesFixtureBlock,\n ChatCompletion,\n ResponseOverrides,\n RecordConfig,\n RecordProviderKey,\n McpFakeIdentity,\n McpFakeUndeclaredPolicy,\n ResponsesToolsMode,\n} from \"./types.js\";\nimport type { MCPSession } from \"./mcp-types.js\";\n\n/** Preserve authored text for string wires without silently repairing invalid JSON. */\nexport function toolArgsForWire(tc: Pick<ToolCall, \"name\" | \"arguments\">) {\n const raw = tc.arguments || \"{}\";\n try {\n const value: unknown = JSON.parse(raw);\n return { kind: \"parsed\" as const, value, text: JSON.stringify(value), raw };\n } catch {\n return { kind: \"verbatim\" as const, raw };\n }\n}\n\n/** Object wires reject the verbatim branch; string wires never need this error. */\nexport class InvalidToolArgumentsError extends Error {\n readonly toolName: string;\n readonly parseDiagnostic: string;\n\n constructor(tc: Pick<ToolCall, \"name\" | \"arguments\">) {\n let parseDiagnostic = \"\";\n try {\n JSON.parse(tc.arguments || \"{}\");\n } catch (error) {\n parseDiagnostic = error instanceof Error ? error.message : String(error);\n }\n super(\n `aimock: fixture tool call \"${tc.name}\" has invalid JSON arguments; this wire carries arguments as an object. ` +\n `Use a wire that carries tool arguments as a string to test malformed JSON. (${parseDiagnostic})`,\n );\n this.name = \"InvalidToolArgumentsError\";\n this.toolName = tc.name;\n this.parseDiagnostic = parseDiagnostic;\n }\n}\n\n/**\n * The `strictToolArguments` server option and logger in effect for the request\n * being served. The server enters it once per HTTP request and once per Gemini\n * Live message, so response builders read it without extra parameters.\n */\ninterface ToolArgumentsScope {\n strict: boolean;\n logger?: Logger;\n}\n\nconst toolArgumentsScope = new AsyncLocalStorage<ToolArgumentsScope>();\n\n/** Run `fn` with the `strictToolArguments` setting and logger of one request. */\nexport function runWithToolArgumentsScope<T>(scope: ToolArgumentsScope, fn: () => T): T {\n return toolArgumentsScope.run(scope, fn);\n}\n\n/** Whether `strictToolArguments` is on for the request being served (default off). */\nexport function strictToolArgumentsEnabled(): boolean {\n return toolArgumentsScope.getStore()?.strict === true;\n}\n\n/**\n * Serve the rest of this request's tool arguments as authored (the\n * `strictToolArguments` behavior). An applied misbehavior fault calls this so\n * that the malformed arguments it injects are not replaced with `{}`; the\n * planner only applies a fault to a fixture whose own arguments are valid\n * wherever the default would replace them.\n */\nexport function keepAuthoredToolArguments(): void {\n const scope = toolArgumentsScope.getStore();\n if (scope) scope.strict = true;\n}\n\n/**\n * The fixture tool-call arguments a wire serves on its normal (no misbehavior)\n * path. Valid JSON gives the parsed value, its re-serialized text, and the\n * authored text. Invalid JSON logs one warning and, by default, is served as\n * `{}` / `\"{}\"`, as in 1.44.0. With `strictToolArguments`, an object wire\n * (`\"object\"`) throws {@link InvalidToolArgumentsError} and a string wire\n * (`\"string\"`) serves the authored text unchanged.\n */\nexport function servedToolArgs(\n tc: Pick<ToolCall, \"name\" | \"arguments\">,\n wire: \"object\" | \"string\",\n logger?: Logger,\n): { value: unknown; text: string; raw: string } {\n const args = toolArgsForWire(tc);\n if (args.kind === \"parsed\") return args;\n const scope = toolArgumentsScope.getStore();\n (logger ?? scope?.logger)?.warn(\n `Malformed JSON in fixture tool call arguments for \"${tc.name}\": ${tc.arguments}`,\n );\n if (scope?.strict !== true) return { value: {}, text: \"{}\", raw: \"{}\" };\n if (wire === \"object\") throw new InvalidToolArgumentsError(tc);\n return { value: undefined, text: args.raw, raw: args.raw };\n}\n\n/**\n * A custom (freeform) tool call reached a wire that has no faithful projection\n * for it. Only the OpenAI Responses API (HTTP and WebSocket) emits custom tool\n * calls, from a fixture's `customToolCalls` or a `customToolCall` block in its\n * `responsesBlocks`. The handlers of the other wires raise this through\n * {@link rejectResponsesOnlyToolCalls} (usually via\n * {@link requireEmittedFunctionToolCalls}) before they write any content.\n * Ollama `/api/generate` is the exception: it rejects every tool-call fixture\n * with a 400 before this check.\n */\nexport class UnsupportedToolCallError extends Error {\n readonly toolName: string;\n readonly wire: string;\n\n constructor(toolName: string, wire: string) {\n super(\n `aimock: fixture tool call \"${toolName}\" is a custom tool call, which only the OpenAI Responses API supports; ` +\n `this request arrived on ${wire}`,\n );\n this.name = \"UnsupportedToolCallError\";\n this.toolName = toolName;\n this.wire = wire;\n }\n}\n\n/**\n * A fixture tool call is malformed: a `toolCalls` entry that is not an object,\n * a malformed `customToolCalls` entry, or a malformed tool block in\n * `responsesBlocks`. Programmatic and factory fixtures skip load validation,\n * so this request-time guard is their validation for the rules\n * {@link assertResponsesToolCalls} and {@link resolveResponsesBlocks} check.\n * An HTTP request then fails with a 500 that carries this message and `code`;\n * a WebSocket turn gets the transport's error message with the code. A\n * malformed legacy `blocks` entry keeps the plain Error of\n * {@link resolveFixtureBlocks}.\n */\nexport class InvalidFixtureToolCallError extends Error {\n readonly code = \"aimock_invalid_fixture_tool_call\";\n\n constructor(\n index: number,\n problem: string,\n carrier: \"toolCalls\" | \"customToolCalls\" | \"blocks\" = \"toolCalls\",\n ) {\n super(\n carrier === \"blocks\"\n ? `Invalid fixture block at index ${index}: ${problem}`\n : `Invalid fixture tool call: ${problem} (${carrier}[${index}])`,\n );\n this.name = \"InvalidFixtureToolCallError\";\n }\n}\n\n/**\n * Request-time guard for one custom tool call (a `customToolCalls` entry).\n * Programmatic and factory fixtures skip load validation, so this is their\n * validation: an object with a non-empty string `name`, a string `input`, no\n * `arguments`, a string `id` when present and a non-empty string `namespace`\n * when present.\n */\nfunction assertCustomToolCall(call: unknown, index: number): void {\n const fail = (problem: string): never => {\n throw new InvalidFixtureToolCallError(index, problem, \"customToolCalls\");\n };\n if (!isPlainObject(call)) fail(\"expected an object\");\n const tc = call as Record<string, unknown>;\n if (tc.namespace !== undefined && (typeof tc.namespace !== \"string\" || tc.namespace === \"\")) {\n fail('\"namespace\" must be a non-empty string when present');\n }\n if (typeof tc.name !== \"string\" || tc.name === \"\") {\n fail('custom tool call requires a non-empty string \"name\" field');\n }\n if (typeof tc.input !== \"string\") fail('\"input\" must be a string for a custom tool call');\n if (tc.arguments !== undefined) fail('custom tool call takes \"input\", not \"arguments\"');\n if (tc.id !== undefined && typeof tc.id !== \"string\") {\n fail('custom tool call \"id\" must be a string when present');\n }\n}\n\n/**\n * Validate a response's `customToolCalls` (OpenAI Responses only) before it is\n * served. Throws {@link InvalidFixtureToolCallError} on the first malformed\n * entry. The key is new, so no fixture of an earlier release reaches this.\n */\nexport function assertCustomToolCalls(response: object): void {\n const list = (response as { customToolCalls?: unknown }).customToolCalls;\n if (list === undefined) return;\n if (!Array.isArray(list)) {\n throw new InvalidFixtureToolCallError(0, \"customToolCalls must be an array\", \"customToolCalls\");\n }\n list.forEach((call, i) => assertCustomToolCall(call, i));\n}\n\n/**\n * Validate the custom calls among the tool calls the OpenAI Responses API is\n * about to serve (see {@link servedToolCalls}). Function calls are not\n * checked here: they keep the handling of earlier releases (a non-string\n * `arguments` fails with the uncoded error of the Responses builders).\n */\nexport function assertResponsesToolCalls(calls: readonly FixtureToolCall[]): void {\n let customIndex = 0;\n for (const tc of calls) {\n if (isPlainObject(tc) && tc.type === \"custom\") assertCustomToolCall(tc, customIndex++);\n }\n}\n\n/**\n * A fixture's `toolCalls` for a wire that cannot carry custom tool calls.\n * Every entry is a function call and passes through unchanged, exactly as in\n * earlier releases (the builders read its `name`, `arguments` and `id`; its\n * Responses-only `namespace` is not emitted). Custom calls live in\n * `customToolCalls`, which {@link rejectResponsesOnlyToolCalls} rejects.\n */\nexport function requireFunctionToolCalls(calls: ToolCall[]): ToolCall[] {\n return calls;\n}\n\n/**\n * Reject the Responses-only custom tool calls on a wire that cannot carry\n * them: a non-empty `customToolCalls`, or a `customToolCall` block in\n * `responsesBlocks`. Throws {@link UnsupportedToolCallError} naming `wire`.\n * Both keys are new, so no fixture of an earlier release reaches this error.\n */\nexport function rejectResponsesOnlyToolCalls(response: object, wire: string): void {\n const r = response as { customToolCalls?: unknown; responsesBlocks?: unknown };\n if (Array.isArray(r.customToolCalls) && r.customToolCalls.length > 0) {\n const first: unknown = r.customToolCalls[0];\n const name = isPlainObject(first) && typeof first.name === \"string\" ? first.name : \"\";\n throw new UnsupportedToolCallError(name, wire);\n }\n if (Array.isArray(r.responsesBlocks)) {\n for (const block of r.responsesBlocks as unknown[]) {\n if (isPlainObject(block) && block.type === \"customToolCall\") {\n throw new UnsupportedToolCallError(typeof block.name === \"string\" ? block.name : \"\", wire);\n }\n }\n }\n}\n\n/**\n * {@link rejectResponsesOnlyToolCalls}, then {@link requireFunctionToolCalls}\n * on the response's `toolCalls` (ignoring any `blocks`).\n */\nexport function requireServedFunctionToolCalls(\n response: { toolCalls?: ToolCall[] },\n wire: string,\n): ToolCall[] {\n rejectResponsesOnlyToolCalls(response, wire);\n return requireFunctionToolCalls(response.toolCalls ?? []);\n}\n\n/**\n * The function calls a non-Responses wire emits for this response, after\n * {@link rejectResponsesOnlyToolCalls}. Non-empty ordered `blocks` are\n * authoritative: the builders stream them (and validate them through\n * {@link resolveFixtureBlocks}) and never emit the legacy `toolCalls`, so the\n * result is empty. Without blocks, `toolCalls` is checked by\n * {@link requireFunctionToolCalls}.\n */\nexport function requireEmittedFunctionToolCalls(\n response: { toolCalls?: ToolCall[]; blocks?: FixtureFileBlock[] },\n wire: string,\n): ToolCall[] {\n rejectResponsesOnlyToolCalls(response, wire);\n if (Array.isArray(response.blocks) && response.blocks.length > 0) return [];\n return requireFunctionToolCalls(response.toolCalls ?? []);\n}\n\n/** A fixture tool-call error that a handler surfaces as a coded 500. */\nexport type FixtureToolCallError =\n | InvalidToolArgumentsError\n | UnsupportedToolCallError\n | InvalidFixtureToolCallError;\n\n/** True for the fixture tool-call errors a handler surfaces as a coded 500. */\nexport function isFixtureToolCallError(error: unknown): error is FixtureToolCallError {\n return (\n error instanceof InvalidToolArgumentsError ||\n error instanceof UnsupportedToolCallError ||\n error instanceof InvalidFixtureToolCallError\n );\n}\n\n/** The aimock error code carried in every provider envelope for a fixture tool-call error. */\nexport function fixtureToolCallErrorCode(\n error: FixtureToolCallError,\n):\n | \"aimock_unsupported_tool_call\"\n | \"aimock_invalid_tool_arguments\"\n | \"aimock_invalid_fixture_tool_call\" {\n if (error instanceof UnsupportedToolCallError) return \"aimock_unsupported_tool_call\";\n if (error instanceof InvalidFixtureToolCallError) return error.code;\n return \"aimock_invalid_tool_arguments\";\n}\n\n/**\n * Google RPC error `details` carrying an aimock fixture tool-call code, for the\n * Gemini, Vertex AI and Gemini Live envelopes (`google.rpc.ErrorInfo`: an\n * UPPER_SNAKE `reason`, a `domain`, and string `metadata`).\n */\nexport function googleFixtureToolCallErrorDetails(code: string) {\n return [\n {\n \"@type\": \"type.googleapis.com/google.rpc.ErrorInfo\",\n reason: code.toUpperCase(),\n domain: \"aimock\",\n metadata: { code },\n },\n ];\n}\n\n/**\n * Run a WebSocket preflight `check` that may raise a fixture tool-call error\n * ({@link InvalidToolArgumentsError}, {@link UnsupportedToolCallError} or\n * {@link InvalidFixtureToolCallError}).\n * Such an error marks the already-journaled entry as a 500 carrying the\n * message, then propagates to the transport's error handler.\n */\nexport function journalFixtureToolCallError<T>(journalEntry: JournalEntry, check: () => T): T {\n try {\n return check();\n } catch (error) {\n if (isFixtureToolCallError(error)) {\n journalEntry.response.status = 500;\n journalEntry.response.error = error.message;\n }\n throw error;\n }\n}\n\n/**\n * Resolve effective strict mode from per-request header and server default.\n * Header values override the server default — same precedence pattern as chaos\n * config headers (see resolveChaosConfig in chaos.ts).\n *\n * Header: `X-AIMock-Strict` — \"true\"/\"1\" → strict on, \"false\"/\"0\" → strict off\n * (case-insensitive, surrounding whitespace trimmed).\n * When absent or unrecognised, falls back to the server-level default.\n */\nexport function resolveStrictMode(\n serverDefault: boolean | undefined,\n rawHeaders?: IncomingHttpHeaders,\n): boolean {\n return resolvePolicyStrictMode(serverDefault, rawHeaders);\n}\n\nconst STRICT_INTEGER_RE = /^\\d+$/;\n\n/**\n * Strict decimal-integer grammar shared by the query/list parsers that grew\n * their own local copy on separate branches (files `limit`, fine-tuning\n * `limit`, chaos integer fields): an unsigned digit run and nothing else — no\n * sign, no exponent, no radix prefix, no surrounding whitespace (callers that\n * accept padded input trim before calling).\n *\n * Deliberately narrower than `Number()`, which reads `0x10` as 16, `1e3` as\n * 1000 and strips whitespace and a leading `+`. Returns the value, or `null`\n * when the text is not a safe integer literal. Range checks and error\n * messages stay with the callers — this is the grammar gate only, so sharing\n * it cannot change any surface's accepted range or its 400 text.\n */\nexport function parseStrictIntegerText(text: string): number | null {\n if (!STRICT_INTEGER_RE.test(text)) return null;\n const n = Number(text);\n return Number.isSafeInteger(n) ? n : null;\n}\n\n/**\n * Would a fixture miss on this request have been forwarded upstream? This is\n * the ONE rule behind the no-fixture chaos journal `source` label: \"proxy\"\n * only when record mode has an upstream for THIS provider and strict mode is\n * not refusing the miss first. `effectiveStrict` is the header-resolved value\n * from {@link resolveStrictMode}. `record.providers` is read guarded — a JS\n * caller can hand over `record: {}`.\n */\nexport function wouldProxyMiss(\n effectiveStrict: boolean,\n record: RecordConfig | undefined,\n providerKey: RecordProviderKey | undefined,\n): boolean {\n if (effectiveStrict || providerKey === undefined) return false;\n return Boolean(record?.providers?.[providerKey]);\n}\n\n/**\n * Returns `true` or `false` when the X-AIMock-Strict header overrides the\n * server default, or `undefined` when it doesn't. Designed to be spread\n * directly into a journal entry's `response` object:\n *\n * response: { status, fixture, ...strictOverrideField(defaults.strict, req.headers) }\n */\nexport function strictOverrideField(\n serverDefault: boolean | undefined,\n rawHeaders?: IncomingHttpHeaders,\n): { strictOverride?: boolean } {\n return policyStrictOverrideField(serverDefault, rawHeaders);\n}\n\n/**\n * Build the strict-mode 503 error message, distinguishing a true no-match from\n * a sequence/turn-exhausted miss.\n *\n * `skippedBySequenceOrTurn` is the count reported by `matchFixtureDiagnostic`\n * (router.ts): the number of fixtures that matched the request SHAPE but were\n * rejected ONLY by their `sequenceIndex`/`turnIndex` count state.\n *\n * - `0` → `\"Strict mode: no fixture matched\"` (no candidate had a matching shape)\n * - `>0` → `\"Strict mode: N candidate fixture(s) skipped by sequence/turn state\"`\n *\n * The HTTP status (503) and error envelope shape are unchanged at every call\n * site — only this message string differs. Endpoints with no sequence/turn\n * gates always pass `0` and therefore see the generic message.\n */\nexport function strictNoMatchMessage(skippedBySequenceOrTurn: number): string {\n if (skippedBySequenceOrTurn > 0) {\n return `Strict mode: ${skippedBySequenceOrTurn} candidate fixture(s) skipped by sequence/turn state`;\n }\n return \"Strict mode: no fixture matched\";\n}\n\n/**\n * Build the strict-mode error LOG line, mirroring {@link strictNoMatchMessage}'s\n * disambiguation so the error log distinguishes the two miss kinds too.\n */\nexport function strictNoMatchLogLine(\n method: string,\n url: string,\n skippedBySequenceOrTurn: number,\n): string {\n if (skippedBySequenceOrTurn > 0) {\n return `STRICT: ${skippedBySequenceOrTurn} candidate fixture(s) skipped by sequence/turn state for ${method} ${url}`;\n }\n return `STRICT: No fixture matched for ${method} ${url}`;\n}\n\n/**\n * Resolve the reasoning string to actually emit for a given model.\n *\n * aimock synthesizes a reasoning channel whenever a fixture carries a\n * `reasoning` string, regardless of the requested model. But a non-reasoning\n * model (e.g. `gpt-4.1`) would emit no reasoning against the real provider, so\n * replaying it is a false green (see aimock#254). This gates the emission on\n * the requested model's capability:\n *\n * - no fixture reasoning → undefined (no-op, short-circuit)\n * - reasoning-capable model → emit unchanged, no log\n * - non-reasoning model, strict OFF → `logger.warn`, still emit (preserves\n * current behavior)\n * - non-reasoning model, strict ON → `logger.error`, suppress (return undefined)\n *\n * Capability is decided from the REQUESTED model id (what the backend was wired\n * to), not any `overrides.model` echoed in the payload.\n */\nexport function resolveReasoningForModel(\n reasoning: string | undefined,\n model: string | undefined,\n strict: boolean,\n logger: Logger,\n): string | undefined {\n if (!reasoning) return undefined;\n if (isReasoningModel(model)) return reasoning;\n if (strict) {\n logger.error(\n `Strict mode: fixture has a reasoning channel but model \"${model}\" is not reasoning-capable — suppressing reasoning emission`,\n );\n return undefined;\n }\n logger.warn(\n `Fixture has a reasoning channel but model \"${model}\" is not reasoning-capable — the real provider would emit no reasoning. Emitting anyway (set X-AIMock-Strict: true to suppress).`,\n );\n return reasoning;\n}\n\n/**\n * Resolve the encrypted reasoning artifacts (`reasoningSignature` and\n * `redactedThinking`) to actually emit for a given model.\n *\n * `redacted_thinking` blocks and a thinking `signature` ARE part of the\n * reasoning channel — they are just the encrypted form of it — so they must be\n * gated on the same model-capability resolution as the plaintext `reasoning`\n * string (see resolveReasoningForModel). Gating only the plaintext channel\n * leaves a half-gated reasoning path: replaying a fixture recorded from a\n * reasoning model against a non-reasoning model would strip the `thinking`\n * block but still emit `redacted_thinking` blocks, which the real provider for\n * that model would never produce.\n *\n * Capability is decided from the REQUESTED model id, independently of whether a\n * plaintext `reasoning` string is present — a fixture may carry only\n * `redactedThinking` with no plaintext reasoning.\n *\n * - reasoning-capable model → emit both unchanged, no log\n * - non-reasoning model, no artifacts → no-op, nothing to suppress\n * - non-reasoning model, strict OFF → `logger.warn`, still emit (preserves\n * current behavior)\n * - non-reasoning model, strict ON → `logger.error`, suppress both\n *\n * Must be invoked alongside resolveReasoningForModel with identical model/strict\n * inputs so the plaintext and encrypted channels stay suppressed together.\n */\nexport function resolveReasoningArtifactsForModel(\n reasoningSignature: string | undefined,\n redactedThinking: string[] | undefined,\n model: string | undefined,\n strict: boolean,\n logger: Logger,\n): { reasoningSignature?: string; redactedThinking?: string[] } {\n const hasArtifacts = reasoningSignature !== undefined || (redactedThinking?.length ?? 0) > 0;\n if (!hasArtifacts || isReasoningModel(model)) {\n return { reasoningSignature, redactedThinking };\n }\n if (strict) {\n logger.error(\n `Strict mode: fixture has encrypted reasoning artifacts (redacted_thinking/signature) but model \"${model}\" is not reasoning-capable — suppressing reasoning emission`,\n );\n return {};\n }\n logger.warn(\n `Fixture has encrypted reasoning artifacts (redacted_thinking/signature) but model \"${model}\" is not reasoning-capable — the real provider would emit no reasoning. Emitting anyway (set X-AIMock-Strict: true to suppress).`,\n );\n return { reasoningSignature, redactedThinking };\n}\n\nexport function flattenHeaders(headers: http.IncomingHttpHeaders): Record<string, string> {\n const flat: Record<string, string> = {};\n for (const [key, value] of Object.entries(headers)) {\n if (value === undefined) continue;\n if (isRecognizedApiKeyHeader(key)) {\n flat[key] = \"[REDACTED]\";\n } else {\n flat[key] = Array.isArray(value) ? value.join(\", \") : value;\n }\n }\n return flat;\n}\n\n/** Structural discriminator; registration performs full transcript validation. */\nexport function isLiveResponse(value: unknown): value is LiveFixtureResponse {\n return value !== null && typeof value === \"object\" && !Array.isArray(value) && \"live\" in value;\n}\n\nexport function isResponseFactory(r: FixtureResponse | ResponseFactory): r is ResponseFactory {\n return typeof r === \"function\";\n}\n\n/** A server's own view of the caller's fixture array: see {@link createServerFixtureList}. */\ninterface ServerFixtureList {\n /** The caller's array, which every read and write reaches. */\n target: Fixture[];\n /** The server's `responsesTools` mode, read live. */\n mode: () => ResponsesToolsMode | undefined;\n}\n\nconst serverFixtureLists = new WeakMap<object, ServerFixtureList>();\n\n/**\n * Array methods that read elements and write them back. On a server list\n * they run on the caller's array, so the extended view of an element is\n * never written into it.\n */\nconst ELEMENT_MOVING_METHODS = new Set<PropertyKey>([\n \"copyWithin\",\n \"fill\",\n \"pop\",\n \"push\",\n \"reverse\",\n \"shift\",\n \"sort\",\n \"splice\",\n \"unshift\",\n]);\n\n/**\n * The fixture list one server reads and writes: a live view of the caller's\n * `fixtures` array, with its own identity. The matcher reads the server's\n * `responsesTools` mode from the list it is given, so every handler on every\n * wire sees that server's mode without extra parameters, and two servers that\n * share one array each keep their own mode. With `\"extended\"`, reading an\n * element gives `extendedView(element)` (the fixture with its held keys\n * applied); the caller's array and its fixtures are never changed for it.\n * Writes (push, splice, `length = 0`, ...) reach the caller's array.\n */\nexport function createServerFixtureList(\n fixtures: Fixture[],\n mode: () => ResponsesToolsMode | undefined,\n extendedView: (fixture: Fixture) => Fixture,\n): Fixture[] {\n const boundMethods = new Map<PropertyKey, unknown>();\n const list = new Proxy(fixtures, {\n get(target, key) {\n const value: unknown = Reflect.get(target, key);\n if (typeof value === \"function\") {\n if (!ELEMENT_MOVING_METHODS.has(key)) return value;\n let bound = boundMethods.get(key);\n if (bound === undefined) {\n bound = (value as (...args: unknown[]) => unknown).bind(target);\n boundMethods.set(key, bound);\n }\n return bound;\n }\n if (typeof key !== \"string\" || mode() !== \"extended\") return value;\n const first = key.charCodeAt(0);\n if (first < 48 || first > 57 || value === null || typeof value !== \"object\") return value;\n return extendedView(value as Fixture);\n },\n });\n serverFixtureLists.set(list, { target: fixtures, mode });\n return list;\n}\n\n/** Fixture arrays that only their own server reads: see {@link markServerOwnedFixtures}. */\nconst serverOwnedFixtureArrays = new WeakSet<Fixture[]>();\n\n/**\n * @internal Mark `fixtures` as an array no other server reads (LLMock's own\n * array). A server may change the fixtures it makes itself (the control API)\n * in place only in such an array; in a caller's array, which another server\n * may share, they stay as made and an extended server reads them through its\n * view.\n */\nexport function markServerOwnedFixtures(fixtures: Fixture[]): void {\n serverOwnedFixtureArrays.add(fixtures);\n}\n\n/** Whether the array behind `fixtures` (a server list or an array) is server-owned. */\nexport function isServerOwnedFixtures(fixtures: Fixture[]): boolean {\n return serverOwnedFixtureArrays.has(fixtureListTarget(fixtures));\n}\n\n/** Whether `fixtures` is the list of a server running with `responsesTools: \"extended\"`. */\nexport function isExtendedResponsesToolsList(fixtures: readonly Fixture[]): boolean {\n return serverFixtureLists.get(fixtures)?.mode() === \"extended\";\n}\n\n/** The caller's array behind a server list; any other array is itself. */\nexport function fixtureListTarget(fixtures: Fixture[]): Fixture[] {\n return serverFixtureLists.get(fixtures)?.target ?? fixtures;\n}\n\n/**\n * Requests matched against an extended fixture list. {@link resolveResponse}\n * reads it, so the response a handler serves follows the same mode as the\n * match that chose its fixture.\n */\nconst extendedResponsesToolsRequests = new WeakSet<object>();\n\n/** Mark `request` as matched with `responsesTools: \"extended\"`. */\nexport function markExtendedResponsesToolsRequest(request: ChatCompletionRequest): void {\n extendedResponsesToolsRequests.add(request);\n}\n\n/**\n * Drop `customToolCalls` and `responsesBlocks`, for responses served without\n * `responsesTools: \"extended\"`. Earlier releases ignored both keys, so a\n * fixture that carries them serves exactly as before. Returns the same object\n * when neither key is set.\n */\nexport function withoutResponsesToolKeys<T extends FixtureResponse>(response: T): T {\n if (!isPlainObject(response)) return response;\n if (!(\"customToolCalls\" in response) && !(\"responsesBlocks\" in response)) return response;\n const copy: Record<string, unknown> = { ...(response as Record<string, unknown>) };\n delete copy.customToolCalls;\n delete copy.responsesBlocks;\n return copy as unknown as T;\n}\n\nexport async function resolveResponse(\n fixture: Fixture,\n request: ChatCompletionRequest,\n): Promise<FixtureResponse> {\n const served = (response: FixtureResponse): FixtureResponse =>\n sanitizeFixtureResponse(\n extendedResponsesToolsRequests.has(request) ? response : withoutResponsesToolKeys(response),\n );\n if (typeof fixture.response === \"function\") {\n let normalized: FixtureResponse;\n try {\n const raw = await fixture.response(request);\n normalized = normalizeFactoryResponse(raw);\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n throw new Error(`Response factory threw: ${msg}`, { cause: err });\n }\n return served(normalized);\n }\n return served(fixture.response);\n}\n\nexport function isPlainObject(value: unknown): value is Record<string, unknown> {\n return value !== null && typeof value === \"object\" && !Array.isArray(value);\n}\n\n/** A non-empty string, the only valid value of a tool call's `namespace`. */\nfunction isValidNamespace(value: unknown): value is string {\n return typeof value === \"string\" && value !== \"\";\n}\n\n/**\n * Drop the keys a served `toolCalls` entry or `toolCall` block ignores, so\n * no handler reads them: on a `toolCalls` entry, `type` and `input` (a\n * `toolCalls` entry is always a function call; custom calls live in\n * `customToolCalls`) and an invalid `namespace` (not a non-empty string); on a\n * `toolCall` block, `input` and an invalid `namespace`. Earlier releases\n * ignored all of these, so fixtures that carry them serve exactly as before.\n * Returns the same object when nothing needs dropping. `customToolCalls` and\n * `responsesBlocks` are new keys and are validated, not sanitized.\n */\nexport function sanitizeFixtureResponse(response: FixtureResponse): FixtureResponse {\n if (!isPlainObject(response)) return response;\n const r = response as Record<string, unknown>;\n const dropKeys = (entry: unknown, keys: readonly string[]): unknown => {\n if (!isPlainObject(entry)) return entry;\n const drop = keys.filter(\n (key) => key in entry && (key !== \"namespace\" || !isValidNamespace(entry.namespace)),\n );\n if (drop.length === 0) return entry;\n const copy: Record<string, unknown> = { ...entry };\n for (const key of drop) delete copy[key];\n return copy;\n };\n const sanitizeList = (list: unknown, pick: (entry: unknown) => unknown): unknown => {\n if (!Array.isArray(list)) return list;\n const next = list.map(pick);\n return next.some((entry, k) => entry !== list[k]) ? next : list;\n };\n const toolCalls = sanitizeList(r.toolCalls, (entry) =>\n dropKeys(entry, [\"type\", \"input\", \"namespace\"]),\n );\n let blocks = sanitizeList(r.blocks, (entry) =>\n isPlainObject(entry) && entry.type === \"toolCall\"\n ? dropKeys(entry, [\"input\", \"namespace\"])\n : entry,\n );\n // A custom-free `responsesBlocks` with no `blocks` beside it is served as\n // `blocks` by the wires that do not read `responsesBlocks`. (The OpenAI\n // Responses API still prefers `responsesBlocks`; a fixture with a custom\n // call in it is rejected off that API.)\n if (\n !(Array.isArray(r.blocks) && r.blocks.length > 0) &&\n Array.isArray(r.responsesBlocks) &&\n r.responsesBlocks.length > 0 &&\n !r.responsesBlocks.some((entry) => isPlainObject(entry) && entry.type === \"customToolCall\")\n ) {\n blocks = r.responsesBlocks;\n }\n if (toolCalls === r.toolCalls && blocks === r.blocks) return response;\n const copy: Record<string, unknown> = { ...r };\n if (toolCalls !== r.toolCalls) copy.toolCalls = toolCalls;\n if (blocks !== r.blocks) copy.blocks = blocks;\n return copy as unknown as FixtureResponse;\n}\n\n/**\n * Drop `namespace` from every `toolCalls` entry and `toolCall` block, for\n * OpenAI Responses requests served without `responsesTools: \"extended\"`:\n * earlier releases never emitted it. (Without that mode, resolveResponse has\n * already dropped `customToolCalls` and `responsesBlocks`.) Returns the same\n * object when no namespace is set.\n */\nexport function withoutLegacyNamespaces<T extends FixtureResponse>(response: T): T {\n if (!isPlainObject(response)) return response;\n const r = response as Record<string, unknown>;\n const strip = (list: unknown, isTarget: (entry: Record<string, unknown>) => boolean) => {\n if (!Array.isArray(list)) return list;\n if (!list.some((entry) => isPlainObject(entry) && isTarget(entry) && \"namespace\" in entry)) {\n return list;\n }\n return list.map((entry) => {\n if (!isPlainObject(entry) || !isTarget(entry) || !(\"namespace\" in entry)) return entry;\n const copy: Record<string, unknown> = { ...entry };\n delete copy.namespace;\n return copy;\n });\n };\n const toolCalls = strip(r.toolCalls, () => true);\n const blocks = strip(r.blocks, (entry) => entry.type === \"toolCall\");\n if (toolCalls === r.toolCalls && blocks === r.blocks) return response;\n const copy: Record<string, unknown> = { ...r };\n if (toolCalls !== r.toolCalls) copy.toolCalls = toolCalls;\n if (blocks !== r.blocks) copy.blocks = blocks;\n return copy as unknown as T;\n}\n\nfunction normalizeFactoryResponse(raw: FixtureResponse): FixtureResponse {\n const r = { ...raw } as Record<string, unknown>;\n if (typeof r.content === \"object\" && r.content !== null) {\n r.content = JSON.stringify(r.content);\n }\n if (Array.isArray(r.toolCalls)) {\n r.toolCalls = (r.toolCalls as Array<Record<string, unknown>>).map((tc) => {\n if (typeof tc.arguments === \"object\" && tc.arguments !== null) {\n return { ...tc, arguments: JSON.stringify(tc.arguments) };\n }\n return { ...tc };\n });\n }\n // Mirror the toolCalls[].arguments idiom for the optional ordered `blocks`\n // array: auto-stringify object `arguments` on each `toolCall` block so a\n // programmatic ResponseFactory may return objects (resolveFixtureBlocks\n // requires string `arguments`). Text blocks and string arguments pass\n // through unchanged. Matches the loader's block handling.\n if (Array.isArray(r.blocks)) {\n r.blocks = (r.blocks as Array<Record<string, unknown>>).map((block) => {\n if (\n block != null &&\n block.type === \"toolCall\" &&\n typeof block.arguments === \"object\" &&\n block.arguments !== null\n ) {\n return { ...block, arguments: JSON.stringify(block.arguments) };\n }\n return { ...block };\n });\n }\n // `responsesBlocks` (new): the same idiom, but a non-object entry stays as it\n // is for resolveResponsesBlocks to reject. A custom call's `input` (in\n // `customToolCalls` or a `customToolCall` block) is free text and is never\n // stringified.\n if (Array.isArray(r.responsesBlocks)) {\n r.responsesBlocks = (r.responsesBlocks as unknown[]).map((block) =>\n isPlainObject(block) &&\n block.type === \"toolCall\" &&\n typeof block.arguments === \"object\" &&\n block.arguments !== null\n ? { ...block, arguments: JSON.stringify(block.arguments) }\n : block,\n );\n }\n return r as unknown as FixtureResponse;\n}\n\nexport function generateId(prefix = \"chatcmpl\"): string {\n return `${prefix}-${randomBytes(12).toString(\"base64url\")}`;\n}\n\n/**\n * Resolve the request id for this HTTP request.\n *\n * - When the caller sends a well-formed `X-Request-Id` (1-128 chars of\n * `A-Za-z0-9-_.:`), it is echoed verbatim so distributed traces correlate.\n * - Otherwise (absent, empty, too long, or illegal characters) a fresh\n * `req-…` id is generated — never trust an attacker-controlled correlation\n * value to be a valid log key.\n *\n * Returns `{ id, generated }`. The server normalizes\n * `req.headers[\"x-request-id\"]` to the resolved value, so every downstream\n * `flattenHeaders` journal snapshot carries it with zero per-handler edits —\n * which also means a MINTED id would otherwise ride the egress header set to\n * a real provider. The server therefore passes `generated` to\n * `markMintedRequestId` so `buildForwardHeaders` can drop it; a caller's own\n * id still forwards verbatim.\n */\nexport function resolveRequestId(rawHeaders: http.IncomingHttpHeaders): {\n id: string;\n generated: boolean;\n} {\n const raw = rawHeaders[\"x-request-id\"];\n const first = Array.isArray(raw) ? raw[0] : raw;\n if (typeof first === \"string\") {\n const trimmed = first.trim();\n if (trimmed.length >= 1 && trimmed.length <= 128 && /^[A-Za-z0-9\\-_.:]+$/.test(trimmed)) {\n return { id: trimmed, generated: false };\n }\n }\n return { id: generateId(\"req\"), generated: true };\n}\n\n/**\n * Requests whose `x-request-id` aimock MINTED (the caller sent none, or sent\n * an unusable one). The normalized header is indistinguishable from a\n * caller-supplied one by inspection, so the provenance is tracked out-of-band\n * and keyed on the request object, exactly like the egress auth marker.\n */\nconst mintedRequestIds = new WeakSet<http.IncomingMessage>();\n\n/** @internal Record that this request's `x-request-id` is aimock's own. */\nexport function markMintedRequestId(req: http.IncomingMessage, generated: boolean): void {\n if (generated) mintedRequestIds.add(req);\n}\n\n/**\n * @internal True when `x-request-id` on this request was minted by aimock.\n * Egress paths use it to avoid transmitting an invented correlation id to a\n * real provider.\n */\nexport function hasMintedRequestId(req: http.IncomingMessage): boolean {\n return mintedRequestIds.has(req);\n}\n\nexport function generateToolCallId(): string {\n return `call_${randomBytes(12).toString(\"base64url\")}`;\n}\n\nexport function generateMessageId(): string {\n return `msg_${randomBytes(12).toString(\"base64url\")}`;\n}\n\nexport function generateToolUseId(): string {\n return `toolu_${randomBytes(12).toString(\"base64url\")}`;\n}\n\nexport function isTextResponse(r: FixtureResponse): r is TextResponse {\n return \"content\" in r && typeof (r as TextResponse).content === \"string\" && !(\"toolCalls\" in r);\n}\n\nexport function isToolCallResponse(r: FixtureResponse): r is ToolCallResponse {\n return (\n \"toolCalls\" in r &&\n Array.isArray((r as ToolCallResponse).toolCalls) &&\n !(\"content\" in r && typeof (r as unknown as Record<string, unknown>).content === \"string\")\n );\n}\n\n/**\n * Whether `r` is a combined content + tool calls response: a string `content`\n * with a `toolCalls` array, or a non-empty `blocks` array. Public, with the\n * 1.44.0 result for every input; `responsesBlocks` does not count. aimock's\n * own handlers use {@link isCombinedFixtureResponse}, which also counts it.\n */\nexport function isContentWithToolCallsResponse(\n r: FixtureResponse,\n): r is ContentWithToolCallsResponse {\n const o = r as ContentWithToolCallsResponse;\n const hasContentAndToolCalls =\n \"content\" in r &&\n typeof o.content === \"string\" &&\n \"toolCalls\" in r &&\n Array.isArray(o.toolCalls);\n const hasNonEmptyBlocks = Array.isArray(o.blocks) && o.blocks.length > 0;\n return hasContentAndToolCalls || hasNonEmptyBlocks;\n}\n\n/**\n * @internal The combined-response guard aimock's handlers, validation and\n * misbehavior use: {@link isContentWithToolCallsResponse} plus a non-empty\n * `responsesBlocks`. In legacy mode those keys are dropped before serving, so\n * only `responsesTools: \"extended\"` sees the extra clause.\n */\nexport function isCombinedFixtureResponse(r: FixtureResponse): r is ContentWithToolCallsResponse {\n const o = r as ContentWithToolCallsResponse;\n // LEGACY / COMBINED shape — BOTH content (string) + toolCalls (array). This\n // clause is byte-identical to the original guard, so every fixture that\n // matched before still matches here and is classified exactly as before.\n const hasContentAndToolCalls =\n \"content\" in r &&\n typeof o.content === \"string\" &&\n \"toolCalls\" in r &&\n Array.isArray(o.toolCalls);\n // BLOCKS-ONLY shape (additive, #274 F0) — a non-empty `blocks` array with no\n // content/toolCalls. This is a pure RELAXATION: it recognizes MORE, never\n // reclassifies an existing fixture. A blocks-only fixture cannot be claimed by\n // any earlier/looser guard in the dispatch order — `isTextResponse` requires a\n // string `content` AND `!(\"toolCalls\" in r)`, and `isToolCallResponse`\n // requires a `toolCalls` array — so it would otherwise fall through to 500.\n // `isAudioResponse` (checked first everywhere) requires an `audio` field, which\n // blocks-only lacks, so there is no overlap there either.\n const hasNonEmptyBlocks = Array.isArray(o.blocks) && o.blocks.length > 0;\n // RESPONSES-BLOCKS shape (new key): a non-empty `responsesBlocks` array. The\n // OpenAI Responses API serves it; other wires serve a custom-free one as\n // `blocks` (see sanitizeFixtureResponse) and reject a custom call in it.\n const hasNonEmptyResponsesBlocks =\n Array.isArray(o.responsesBlocks) && o.responsesBlocks.length > 0;\n return hasContentAndToolCalls || hasNonEmptyBlocks || hasNonEmptyResponsesBlocks;\n}\n\n/**\n * Validate and pass through the ordered `blocks` field of a combined\n * content+toolCalls fixture. Used ONLY on the new block-iteration path (when a\n * fixture explicitly sets `blocks`); it is NOT a legacy-order reconstructor —\n * fixtures without `blocks` never reach this function and keep their unchanged\n * text-first path.\n *\n * An EMPTY `blocks` array is treated as \"no blocks\" by every builder's\n * streaming gate (`blocks && blocks.length > 0`), so it falls back to the\n * legacy `{content, toolCalls}` path and never reaches this function — the gate\n * is the single source of truth for \"has blocks\". This validator therefore only\n * ever runs on a non-empty array.\n *\n * Accepts the relaxed on-disk {@link FixtureFileBlock} input shape — a\n * `toolCall` block's `arguments` may be a string OR a JSON object/array — which\n * makes the object-tolerance below type-visible and mirrors how\n * normalizeResponse types its file-form input (see {@link FixtureFileBlock} /\n * {@link FixtureFileContentWithToolCallsResponse}). The in-memory\n * {@link FixtureBlock} form (string `arguments`) is a structural subtype, so\n * existing callers that pass `FixtureBlock[]` continue to type-check.\n *\n * Returns the blocks in array order, NORMALIZED to {@link FixtureBlock}: a\n * `text` block with a string `text`, or a `toolCall` block with string `name` +\n * string `arguments` (object/array `arguments` is JSON.stringified) and an\n * optional string `id`. The return type guarantees `arguments: string` — every\n * caller relies on that. Throws on a malformed array or entry — same fail-fast\n * idiom as the other fixture validators in this module (see e.g. the factory\n * guard at {@link resolveResponse}). A `customToolCall` block is not a\n * `blocks` entry (it belongs in `responsesBlocks`, see\n * {@link resolveResponsesBlocks}) and fails as an unknown type.\n */\nexport function resolveFixtureBlocks(blocks: FixtureFileBlock[]): FixtureBlock[] {\n if (!Array.isArray(blocks)) {\n throw new Error(`Invalid fixture blocks: expected an array, got ${typeof blocks}`);\n }\n // Validate each block and return a normalized COPY. Builders iterate the\n // result and must not observe later mutations of — nor be able to mutate —\n // the caller's stored fixture array, and block objects are consumed read-only\n // downstream, so we never mutate the input in place: any normalization (e.g.\n // stringifying object `arguments`) is applied to a fresh per-block copy.\n return blocks.map((block, i) => {\n if (block === null || typeof block !== \"object\") {\n throw new Error(`Invalid fixture block at index ${i}: expected an object`);\n }\n const b = block as Record<string, unknown>;\n if (b.type === \"text\") {\n if (typeof b.text !== \"string\") {\n throw new Error(\n `Invalid fixture block at index ${i}: \"text\" block requires a string \"text\" field`,\n );\n }\n return { type: \"text\", text: b.text };\n } else if (b.type === \"toolCall\") {\n if (typeof b.name !== \"string\") {\n throw new Error(\n `Invalid fixture block at index ${i}: \"toolCall\" block requires a string \"name\" field`,\n );\n }\n if (b.id !== undefined && typeof b.id !== \"string\") {\n throw new Error(\n `Invalid fixture block at index ${i}: \"toolCall\" block \"id\" must be a string when present`,\n );\n }\n // `arguments` is a JSON string in normalized (file-load) form. The\n // programmatic path (addFixture/addFixtures/prependFixture) stores RAW\n // fixtures with no normalizeResponse pass, so an OBJECT `arguments` can\n // reach here. Be tolerant: stringify an object/array (mirroring\n // normalizeResponse's `JSON.stringify`) into a fresh block copy so the\n // programmatic path is safe and the caller's stored fixture is untouched.\n // A string stays byte-identical (file-load path unchanged); any other\n // type is still rejected.\n if (typeof b.arguments === \"object\" && b.arguments !== null) {\n return { ...b, arguments: JSON.stringify(b.arguments) } as unknown as FixtureBlock;\n }\n if (typeof b.arguments !== \"string\") {\n throw new Error(\n `Invalid fixture block at index ${i}: \"toolCall\" block requires a string or object \"arguments\" field`,\n );\n }\n return { ...b, type: \"toolCall\", name: b.name, arguments: b.arguments } as FixtureBlock;\n } else {\n throw new Error(\n `Invalid fixture block at index ${i}: unknown type ${JSON.stringify(b.type)} (expected \"text\" or \"toolCall\")`,\n );\n }\n });\n}\n\n/** A normalized block on a wire that cannot carry custom tool calls. */\nexport type FunctionFixtureBlock = FixtureBlock;\n\n/**\n * Validate and normalize a fixture's `responsesBlocks` (OpenAI Responses\n * only). Same rules and normalized copy as {@link resolveFixtureBlocks}, plus:\n * a `customToolCall` block with a non-empty string `name`, a string `input`\n * (never parsed or stringified), no `arguments` and an optional string `id`;\n * on either tool block, `namespace` must be a non-empty string when present,\n * and a `toolCall` block carries no `input`. The key is new, so a malformed\n * tool block throws the coded {@link InvalidFixtureToolCallError}; a malformed\n * text block or an unknown type throws a plain Error.\n */\nexport function resolveResponsesBlocks(\n blocks: readonly (FixtureFileResponsesBlock | ResponsesFixtureBlock)[],\n): ResponsesFixtureBlock[] {\n if (!Array.isArray(blocks)) {\n throw new Error(`Invalid fixture responsesBlocks: expected an array, got ${typeof blocks}`);\n }\n return blocks.map((block, i): ResponsesFixtureBlock => {\n if (!isPlainObject(block)) {\n throw new Error(`Invalid fixture block at index ${i}: expected an object`);\n }\n const b = block as Record<string, unknown>;\n const failToolCall = (problem: string): never => {\n throw new InvalidFixtureToolCallError(i, problem, \"blocks\");\n };\n if (\n (b.type === \"toolCall\" || b.type === \"customToolCall\") &&\n b.namespace !== undefined &&\n (typeof b.namespace !== \"string\" || b.namespace === \"\")\n ) {\n failToolCall('\"namespace\" must be a non-empty string when present');\n }\n const namespace = typeof b.namespace === \"string\" ? { namespace: b.namespace } : {};\n const id = typeof b.id === \"string\" ? { id: b.id } : {};\n if (b.type === \"text\") {\n if (typeof b.text !== \"string\") {\n throw new Error(\n `Invalid fixture block at index ${i}: \"text\" block requires a string \"text\" field`,\n );\n }\n return { type: \"text\", text: b.text };\n }\n if (b.type === \"toolCall\") {\n if (b.input !== undefined) failToolCall('\"input\" is only valid on a \"customToolCall\" block');\n if (typeof b.name !== \"string\")\n failToolCall('\"toolCall\" block requires a string \"name\" field');\n if (b.id !== undefined && typeof b.id !== \"string\") {\n failToolCall('\"toolCall\" block \"id\" must be a string when present');\n }\n const args =\n typeof b.arguments === \"object\" && b.arguments !== null\n ? JSON.stringify(b.arguments)\n : b.arguments;\n if (typeof args !== \"string\") {\n failToolCall('\"toolCall\" block requires a string or object \"arguments\" field');\n }\n return {\n type: \"toolCall\",\n name: b.name as string,\n arguments: args as string,\n ...id,\n ...namespace,\n };\n }\n if (b.type === \"customToolCall\") {\n if (typeof b.name !== \"string\" || b.name === \"\") {\n failToolCall('\"customToolCall\" block requires a non-empty string \"name\" field');\n }\n if (typeof b.input !== \"string\") {\n failToolCall('\"customToolCall\" block requires a string \"input\" field');\n }\n if (b.arguments !== undefined) {\n failToolCall('\"customToolCall\" block takes \"input\", not \"arguments\"');\n }\n if (b.id !== undefined && typeof b.id !== \"string\") {\n failToolCall('\"customToolCall\" block \"id\" must be a string when present');\n }\n return {\n type: \"customToolCall\",\n name: b.name as string,\n input: b.input as string,\n ...id,\n ...namespace,\n };\n }\n throw new Error(\n `Invalid fixture block at index ${i}: unknown type ${JSON.stringify(b.type)} (expected \"text\", \"toolCall\" or \"customToolCall\")`,\n );\n });\n}\n\n/** Allocate the exact OpenAI call identities once for both wire output and observations. */\nexport function prepareOpenAIChatMisbehavior(plan: MisbehaviorPlan): MisbehaviorPlan {\n const response = plan.response;\n const combined = isCombinedFixtureResponse(response);\n // Custom tool calls reach here only on the OpenAI Responses API; the planner\n // skips every other wire's custom-call fixture so its guard rejects it.\n const outcome = combined ? resolveServedBlockOutcome(response) : undefined;\n const calls =\n outcome?.toolCalls ??\n (combined || isToolCallResponse(response) ? servedToolCalls(response) : []);\n const duplicate = plan.duplicateId;\n const toolCalls = calls.map((call, index) => ({\n ...call,\n ...(index === duplicate?.destinationIndex ? {} : { id: call.id || generateToolCallId() }),\n }));\n if (duplicate) {\n toolCalls[duplicate.destinationIndex].id = toolCalls[duplicate.sourceIndex].id;\n }\n let preparedResponse = { ...response };\n if (\"usage\" in preparedResponse) delete preparedResponse.usage;\n if (plan.stop === \"length\") preparedResponse = { ...preparedResponse, finishReason: \"length\" };\n if (outcome) {\n preparedResponse = withServedToolCalls(\n { ...preparedResponse, content: outcome.content },\n toolCalls,\n rebuildOrderedBlocks(outcome.ordered, toolCalls),\n );\n } else if (combined || isToolCallResponse(response)) {\n preparedResponse = withServedToolCalls(preparedResponse, toolCalls);\n }\n return {\n ...plan,\n response: preparedResponse,\n summary: {\n ...plan.summary,\n servedToolCalls: toolCalls.map(servedMisbehaviorToolCall),\n },\n };\n}\n\n/**\n * The journaled form of a served misbehavior tool call: `{ name, arguments,\n * id }`. A Responses custom tool call adds `type: \"custom\"` and carries its\n * free-text `input` in `arguments`; `namespace` is recorded only when set.\n */\nexport function servedMisbehaviorToolCall(call: FixtureToolCall): ServedMisbehaviorToolCall {\n const namespace = call.namespace !== undefined ? { namespace: call.namespace } : {};\n return call.type === \"custom\"\n ? { type: \"custom\", name: call.name, arguments: call.input, id: call.id, ...namespace }\n : { name: call.name, arguments: call.arguments, id: call.id, ...namespace };\n}\n\n/** The text a tool call contributes to an estimated completion: name plus arguments or input. */\nexport function toolCallUsageText(call: FixtureToolCall): string {\n return call.name + (call.type === \"custom\" ? call.input : call.arguments);\n}\n\n/** Estimate once from the full applied output, before any delivery interruption. */\nexport function resolveOpenAIChatMisbehaviorUsage(\n plan: MisbehaviorPlan,\n request: ChatCompletionRequest,\n exposeReasoning = true,\n): ReturnType<typeof resolveUsage> {\n const response = plan.response;\n const combined = isCombinedFixtureResponse(response);\n const outcome = combined ? resolveServedBlockOutcome(response) : undefined;\n const calls =\n outcome?.toolCalls ??\n (combined || isToolCallResponse(response) ? servedToolCalls(response) : []);\n const content =\n outcome?.content ??\n (\"content\" in response && typeof response.content === \"string\" ? response.content : \"\");\n const reasoning =\n plan.reasoning ??\n (\"reasoning\" in response && typeof response.reasoning === \"string\" ? response.reasoning : \"\");\n const promptText = request.messages\n .map((message) =>\n typeof message.content === \"string\"\n ? message.content\n : Array.isArray(message.content)\n ? message.content.map((part) => part.text ?? \"\").join(\"\")\n : \"\",\n )\n .join(\"\");\n const completionText =\n content +\n calls.map(toolCallUsageText).join(\"\") +\n (plan.refusal ?? \"\") +\n (exposeReasoning ? reasoning : \"\");\n return resolveUsage(undefined, promptText, completionText);\n}\n\n/** Project normalized ordered blocks without changing the fixture or generating ids. */\nexport function resolveFixtureBlockOutcome(blocks: FixtureFileBlock[]) {\n const ordered = resolveFixtureBlocks(blocks);\n let content = \"\";\n const toolCalls: ToolCall[] = [];\n for (const block of ordered) {\n if (block.type === \"text\") content += block.text;\n else\n toolCalls.push({\n name: block.name,\n arguments: block.arguments,\n ...(block.id !== undefined ? { id: block.id } : {}),\n });\n }\n return { ordered, content, toolCalls, hasToolCalls: toolCalls.length > 0 };\n}\n\n/**\n * Every tool call a response serves, in order: the function calls of\n * `toolCalls`, then the custom calls of `customToolCalls` (each tagged\n * `type: \"custom\"`). Only the OpenAI Responses API may serve the custom ones;\n * the other wires reject a non-empty `customToolCalls` first\n * ({@link rejectResponsesOnlyToolCalls}).\n */\nexport function servedToolCalls(response: {\n toolCalls?: ToolCall[];\n customToolCalls?: CustomToolCall[];\n}): FixtureToolCall[] {\n const calls: FixtureToolCall[] = [...(response.toolCalls ?? [])];\n for (const call of Array.isArray(response.customToolCalls) ? response.customToolCalls : []) {\n calls.push(isPlainObject(call) ? { ...call, type: \"custom\" } : call);\n }\n return calls;\n}\n\n/**\n * Resolve the ordered blocks a response serves, with each tool block projected\n * to its {@link FixtureToolCall}: a non-empty `responsesBlocks` (OpenAI\n * Responses only, through {@link resolveResponsesBlocks}), else a non-empty\n * legacy `blocks` (through {@link resolveFixtureBlocks}, which rejects a\n * `customToolCall` block with the error of earlier releases). Undefined when\n * the response has neither.\n */\nexport function resolveServedBlockOutcome(response: object):\n | {\n ordered: ResponsesFixtureBlock[];\n content: string;\n toolCalls: FixtureToolCall[];\n hasToolCalls: boolean;\n source: \"responsesBlocks\" | \"blocks\";\n }\n | undefined {\n const r = response as {\n blocks?: FixtureFileBlock[];\n responsesBlocks?: FixtureFileResponsesBlock[];\n };\n let ordered: ResponsesFixtureBlock[];\n let source: \"responsesBlocks\" | \"blocks\";\n if (Array.isArray(r.responsesBlocks) && r.responsesBlocks.length > 0) {\n ordered = resolveResponsesBlocks(r.responsesBlocks);\n source = \"responsesBlocks\";\n } else if (Array.isArray(r.blocks) && r.blocks.length > 0) {\n ordered = resolveFixtureBlocks(r.blocks);\n source = \"blocks\";\n } else return undefined;\n let content = \"\";\n const toolCalls: FixtureToolCall[] = [];\n for (const block of ordered) {\n if (block.type === \"text\") content += block.text;\n else toolCalls.push(fixtureBlockToolCall(block));\n }\n return { ordered, content, toolCalls, hasToolCalls: toolCalls.length > 0, source };\n}\n\n/**\n * Write rewritten served tool calls (and, with `ordered`, rebuilt ordered\n * blocks) back into a copy of `response`, split by carrier: function calls go\n * to `toolCalls`, custom calls to `customToolCalls`, and the blocks to\n * `responsesBlocks` when the response served those, else to `blocks`.\n */\nexport function withServedToolCalls<T extends object>(\n response: T,\n toolCalls: readonly FixtureToolCall[],\n ordered?: ResponsesFixtureBlock[],\n): T {\n const functionCalls: ToolCall[] = [];\n const customCalls: CustomToolCall[] = [];\n for (const call of toolCalls) {\n if (call.type === \"custom\") customCalls.push(call);\n else functionCalls.push(call);\n }\n const out: Record<string, unknown> = { ...response, toolCalls: functionCalls };\n if (customCalls.length > 0 || \"customToolCalls\" in response) out.customToolCalls = customCalls;\n if (ordered) {\n const r = response as { responsesBlocks?: unknown[] };\n if (Array.isArray(r.responsesBlocks) && r.responsesBlocks.length > 0) {\n out.responsesBlocks = ordered;\n } else {\n out.blocks = ordered;\n }\n }\n return out as T;\n}\n\n/** Project one normalized tool block to the matching served tool call. */\nexport function fixtureBlockToolCall(\n block: Exclude<ResponsesFixtureBlock, { type: \"text\" }>,\n): FixtureToolCall {\n const id = block.id !== undefined ? { id: block.id } : {};\n const namespace = block.namespace !== undefined ? { namespace: block.namespace } : {};\n return block.type === \"customToolCall\"\n ? { type: \"custom\", name: block.name, input: block.input, ...id, ...namespace }\n : { name: block.name, arguments: block.arguments, ...id, ...namespace };\n}\n\n/** Project one served tool call back to its ordered tool block. */\nexport function toolCallFixtureBlock(\n call: FixtureToolCall,\n): Exclude<ResponsesFixtureBlock, { type: \"text\" }> {\n const id = call.id !== undefined ? { id: call.id } : {};\n const namespace = call.namespace !== undefined ? { namespace: call.namespace } : {};\n return call.type === \"custom\"\n ? { type: \"customToolCall\", name: call.name, input: call.input, ...id, ...namespace }\n : { type: \"toolCall\", name: call.name, arguments: call.arguments, ...id, ...namespace };\n}\n\n/**\n * Rebuild ordered blocks after their tool calls were rewritten one-for-one:\n * text blocks are copied and the Nth tool block becomes `toolCalls[N]`.\n */\nexport function rebuildOrderedBlocks(\n ordered: readonly ResponsesFixtureBlock[],\n toolCalls: readonly FixtureToolCall[],\n): ResponsesFixtureBlock[] {\n let callIndex = 0;\n return ordered.map((block) =>\n block.type === \"text\" ? { ...block } : toolCallFixtureBlock(toolCalls[callIndex++]),\n );\n}\n\nexport function isErrorResponse(r: FixtureResponse): r is ErrorResponse {\n return (\n \"error\" in r &&\n (r as ErrorResponse).error !== null &&\n typeof (r as ErrorResponse).error === \"object\" &&\n \"message\" in ((r as ErrorResponse).error as Record<string, unknown>) &&\n typeof ((r as ErrorResponse).error as Record<string, unknown>).message === \"string\"\n );\n}\n\n/**\n * Serialize an ErrorResponse to JSON, stripping the internal-only `status`\n * field that controls the HTTP status code but should never appear in the\n * response body. Real LLM APIs don't include it.\n */\nexport function serializeErrorResponse(response: ErrorResponse): string {\n return renderError(\"openai\", \"fixture\", response.error).body;\n}\n\nexport function isEmbeddingResponse(r: FixtureResponse): r is EmbeddingResponse {\n return \"embedding\" in r && Array.isArray((r as EmbeddingResponse).embedding);\n}\n\nexport function isImageResponse(r: FixtureResponse): r is ImageResponse {\n return (\n (\"image\" in r && typeof r.image === \"object\" && r.image != null) ||\n (\"images\" in r && Array.isArray((r as ImageResponse).images))\n );\n}\n\nexport function isAudioResponse(r: FixtureResponse): r is AudioResponse {\n if (!(\"audio\" in r)) return false;\n const a = (r as AudioResponse).audio;\n return typeof a === \"string\" || (typeof a === \"object\" && a !== null && \"b64Json\" in a);\n}\n\n/**\n * Map audio format shorthand to MIME content types.\n * Shared between speech, ElevenLabs, and fal audio handlers.\n */\nexport const FORMAT_TO_CONTENT_TYPE: Record<string, string> = {\n mp3: \"audio/mpeg\",\n opus: \"audio/opus\",\n aac: \"audio/aac\",\n flac: \"audio/flac\",\n wav: \"audio/wav\",\n pcm: \"audio/pcm\",\n};\n\n/**\n * Resolve a format string (e.g. \"mp3\", \"opus\") to its MIME content type.\n * Falls back to \"application/octet-stream\" for unknown formats.\n */\nexport function formatToMime(format: string): string {\n return FORMAT_TO_CONTENT_TYPE[format] ?? \"application/octet-stream\";\n}\n\nexport function isTranscriptionResponse(r: FixtureResponse): r is TranscriptionResponse {\n return (\n \"transcription\" in r &&\n (r as TranscriptionResponse).transcription != null &&\n typeof (r as TranscriptionResponse).transcription === \"object\"\n );\n}\n\nexport function isVideoResponse(r: FixtureResponse): r is VideoResponse {\n return (\n \"video\" in r &&\n (r as VideoResponse).video != null &&\n typeof (r as VideoResponse).video === \"object\"\n );\n}\n\nexport function isJSONResponse(r: FixtureResponse): r is RawJSONResponse {\n return \"json\" in r && (r as RawJSONResponse).json !== undefined;\n}\n\nexport function extractOverrides(\n response: TextResponse | ToolCallResponse | ContentWithToolCallsResponse,\n): ResponseOverrides {\n const r = response;\n return {\n ...(r.id !== undefined && { id: r.id }),\n ...(r.created !== undefined && { created: r.created }),\n ...(r.model !== undefined && { model: r.model }),\n ...(r.usage !== undefined && { usage: r.usage }),\n ...(r.systemFingerprint !== undefined && { systemFingerprint: r.systemFingerprint }),\n ...(r.finishReason !== undefined && { finishReason: r.finishReason }),\n ...(r.role !== undefined && { role: r.role }),\n ...(r.phase !== undefined && { phase: r.phase }),\n ...(r.provider !== undefined && { provider: r.provider }),\n ...(r.nativeFinishReason !== undefined && { nativeFinishReason: r.nativeFinishReason }),\n };\n}\n\n// ─── Token estimation ────────────────────────────────────────────────────\n\n/**\n * Rough token count estimation based on character length.\n * Uses the ~4 characters per token heuristic common for English text.\n */\nexport function estimateTokens(text: string): number {\n return estimateUsageTokens(text);\n}\n\n/**\n * Estimate prompt tokens from a request's messages array.\n */\nexport function estimatePromptTokens(messages: ChatCompletionRequest[\"messages\"]): number {\n return estimateUsagePromptTokens(messages);\n}\n\n/**\n * Build usage object: use explicit overrides if provided, otherwise estimate.\n *\n * Shared by BOTH the non-streaming completion builders (below) and the\n * streaming usage-chunk sites in server.ts — the single source of truth for\n * \"explicit token override wins, cost-only override still estimates\". Exported\n * so the streaming path does not re-implement the estimation logic.\n */\nexport function resolveUsage(\n overrides: ResponseOverrides | undefined,\n promptText: string,\n completionText: string,\n): { prompt_tokens: number; completion_tokens: number; total_tokens: number } {\n return renderOpenAIUsage(resolveOpenAIUsage(overrides, { promptText, completionText }));\n}\n/**\n * Build an SSE usage chunk for streaming responses.\n * OpenAI emits this as the final chunk before [DONE] when\n * stream_options.include_usage is true. It has an empty choices array.\n */\nexport function buildUsageChunk(\n id: string,\n model: string,\n created: number,\n usage: { prompt_tokens: number; completion_tokens: number; total_tokens: number },\n fingerprint?: string,\n): SSEChunk {\n return {\n id,\n object: \"chat.completion.chunk\",\n created,\n model,\n choices: [],\n usage,\n ...(fingerprint !== undefined && { system_fingerprint: fingerprint }),\n };\n}\n\nexport function buildTextChunks(\n content: string,\n model: string,\n chunkSize: number,\n reasoning?: string,\n overrides?: ResponseOverrides,\n): SSEChunk[] {\n const id = overrides?.id ?? generateId();\n const created = overrides?.created ?? Math.floor(Date.now() / 1000);\n const effectiveModel = overrides?.model ?? model;\n const chunks: SSEChunk[] = [];\n const fingerprint = overrides?.systemFingerprint;\n\n // Reasoning chunks (emitted before content, OpenRouter format)\n if (reasoning) {\n for (let i = 0; i < reasoning.length; i += chunkSize) {\n const slice = reasoning.slice(i, i + chunkSize);\n chunks.push({\n id,\n object: \"chat.completion.chunk\",\n created,\n model: effectiveModel,\n choices: [\n { index: 0, delta: { reasoning_content: slice }, logprobs: null, finish_reason: null },\n ],\n ...(fingerprint !== undefined && { system_fingerprint: fingerprint }),\n });\n }\n }\n\n // Role chunk\n chunks.push({\n id,\n object: \"chat.completion.chunk\",\n created,\n model: effectiveModel,\n choices: [\n {\n index: 0,\n delta: { role: overrides?.role ?? \"assistant\", content: \"\" },\n logprobs: null,\n finish_reason: null,\n },\n ],\n ...(fingerprint !== undefined && { system_fingerprint: fingerprint }),\n });\n\n // Content chunks\n for (let i = 0; i < content.length; i += chunkSize) {\n const slice = content.slice(i, i + chunkSize);\n chunks.push({\n id,\n object: \"chat.completion.chunk\",\n created,\n model: effectiveModel,\n choices: [{ index: 0, delta: { content: slice }, logprobs: null, finish_reason: null }],\n ...(fingerprint !== undefined && { system_fingerprint: fingerprint }),\n });\n }\n\n // Finish chunk\n chunks.push({\n id,\n object: \"chat.completion.chunk\",\n created,\n model: effectiveModel,\n choices: [\n { index: 0, delta: {}, logprobs: null, finish_reason: overrides?.finishReason ?? \"stop\" },\n ],\n ...(fingerprint !== undefined && { system_fingerprint: fingerprint }),\n });\n\n return chunks;\n}\n\n/** K9 exposure is selected by the provider path, never the echoed response model. */\nexport function buildOpenAIReasoningChunks(\n reasoning: string,\n model: string,\n chunkSize: number,\n exposeReasoning: boolean,\n overrides?: ResponseOverrides,\n): SSEChunk[] {\n return buildTextChunks(exposeReasoning ? reasoning : \"\", model, chunkSize, undefined, {\n ...overrides,\n role: \"assistant\",\n finishReason: \"length\",\n }).map((chunk) => ({\n ...chunk,\n choices: chunk.choices.map((choice) => ({\n ...choice,\n delta:\n choice.delta.role !== undefined\n ? { role: \"assistant\", content: null }\n : typeof choice.delta.content === \"string\"\n ? {\n role: \"assistant\",\n content: \"\",\n reasoning: choice.delta.content,\n reasoning_details: [\n {\n type: \"reasoning.text\" as const,\n text: choice.delta.content,\n format: \"unknown\" as const,\n index: 0,\n },\n ],\n }\n : {},\n })),\n }));\n}\n\n/** Render exposed reasoning or native OpenAI's empty length completion. */\nexport function buildOpenAIReasoningCompletion(\n reasoning: string,\n model: string,\n exposeReasoning: boolean,\n overrides?: ResponseOverrides,\n requestMessages?: ChatCompletionRequest[\"messages\"],\n): ChatCompletion {\n const visible = exposeReasoning ? reasoning : \"\";\n const completion = buildTextCompletion(\n visible,\n model,\n undefined,\n {\n ...overrides,\n role: \"assistant\",\n finishReason: \"length\",\n usage: undefined,\n },\n requestMessages,\n );\n return {\n ...completion,\n choices: completion.choices.map((choice) => ({\n ...choice,\n message: {\n role: \"assistant\",\n content: null,\n refusal: null,\n ...(exposeReasoning\n ? {\n reasoning: visible,\n reasoning_details: [\n {\n type: \"reasoning.text\" as const,\n text: visible,\n format: \"unknown\" as const,\n index: 0,\n },\n ],\n }\n : {}),\n },\n })),\n };\n}\n\n/** Render withheld output with only the role and native content-filter terminal. */\nexport function buildOpenAIContentFilterChunks(\n model: string,\n overrides?: ResponseOverrides,\n): SSEChunk[] {\n return buildTextChunks(\"\", model, 1, undefined, {\n ...overrides,\n role: \"assistant\",\n finishReason: \"content_filter\",\n }).map((chunk) => ({\n ...chunk,\n choices: chunk.choices.map((choice) => ({\n ...choice,\n delta: choice.delta.role !== undefined ? { role: \"assistant\", content: null } : {},\n })),\n }));\n}\n\n/** Non-streaming filtered output contains no answer, refusal text, reasoning, or calls. */\nexport function buildOpenAIContentFilterCompletion(\n model: string,\n overrides?: ResponseOverrides,\n requestMessages?: ChatCompletionRequest[\"messages\"],\n): ChatCompletion {\n const completion = buildTextCompletion(\n \"\",\n model,\n undefined,\n {\n ...overrides,\n role: \"assistant\",\n finishReason: \"content_filter\",\n usage: undefined,\n },\n requestMessages,\n );\n return {\n ...completion,\n choices: completion.choices.map((choice) => ({\n ...choice,\n message: { role: \"assistant\", content: null, refusal: null },\n })),\n };\n}\n\n/** Render an applied refusal on its own channel without ordinary content or reasoning. */\nexport function buildOpenAIRefusalChunks(\n refusal: string,\n model: string,\n chunkSize: number,\n overrides?: ResponseOverrides,\n) {\n return buildTextChunks(refusal, model, chunkSize, undefined, {\n ...overrides,\n role: \"assistant\",\n finishReason: \"stop\",\n }).map((chunk) => ({\n ...chunk,\n choices: chunk.choices.map((choice) => {\n const delta: typeof choice.delta & { refusal?: string } =\n choice.delta.role !== undefined\n ? { role: \"assistant\", content: null }\n : typeof choice.delta.content === \"string\"\n ? { refusal: choice.delta.content }\n : {};\n return { ...choice, delta };\n }),\n }));\n}\n\n/** Refusal-only counterpart of the ordinary text completion builder. */\nexport function buildOpenAIRefusalCompletion(\n refusal: string,\n model: string,\n overrides?: ResponseOverrides,\n requestMessages?: ChatCompletionRequest[\"messages\"],\n): ChatCompletion {\n const completion = buildTextCompletion(\n refusal,\n model,\n undefined,\n {\n ...overrides,\n role: \"assistant\",\n finishReason: \"stop\",\n usage: undefined,\n },\n requestMessages,\n );\n return {\n ...completion,\n choices: completion.choices.map((choice) => ({\n ...choice,\n message: { role: \"assistant\", content: null, refusal },\n })),\n };\n}\n\nexport function buildToolCallChunks(\n toolCalls: ToolCall[],\n model: string,\n chunkSize: number,\n reasoning?: string,\n overrides?: ResponseOverrides,\n): SSEChunk[] {\n const id = overrides?.id ?? generateId();\n const created = overrides?.created ?? Math.floor(Date.now() / 1000);\n const effectiveModel = overrides?.model ?? model;\n const chunks: SSEChunk[] = [];\n const fingerprint = overrides?.systemFingerprint;\n\n // Reasoning chunks (emitted before tool calls, OpenRouter format)\n if (reasoning) {\n for (let i = 0; i < reasoning.length; i += chunkSize) {\n const slice = reasoning.slice(i, i + chunkSize);\n chunks.push({\n id,\n object: \"chat.completion.chunk\",\n created,\n model: effectiveModel,\n choices: [\n { index: 0, delta: { reasoning_content: slice }, logprobs: null, finish_reason: null },\n ],\n ...(fingerprint !== undefined && { system_fingerprint: fingerprint }),\n });\n }\n }\n\n // Role chunk\n chunks.push({\n id,\n object: \"chat.completion.chunk\",\n created,\n model: effectiveModel,\n choices: [\n {\n index: 0,\n delta: { role: overrides?.role ?? \"assistant\", content: null },\n logprobs: null,\n finish_reason: null,\n },\n ],\n ...(fingerprint !== undefined && { system_fingerprint: fingerprint }),\n });\n\n // Tool call chunks — one initial chunk per tool call, then argument chunks\n for (let tcIdx = 0; tcIdx < toolCalls.length; tcIdx++) {\n const tc = toolCalls[tcIdx];\n const tcId = tc.id || generateToolCallId();\n\n // Initial tool call chunk (id + function name)\n chunks.push({\n id,\n object: \"chat.completion.chunk\",\n created,\n model: effectiveModel,\n choices: [\n {\n index: 0,\n delta: {\n tool_calls: [\n {\n index: tcIdx,\n id: tcId,\n type: \"function\",\n function: { name: tc.name, arguments: \"\" },\n },\n ],\n },\n logprobs: null,\n finish_reason: null,\n },\n ],\n ...(fingerprint !== undefined && { system_fingerprint: fingerprint }),\n });\n\n // Argument streaming chunks\n const args = tc.arguments;\n for (let i = 0; i < args.length; i += chunkSize) {\n const slice = args.slice(i, i + chunkSize);\n chunks.push({\n id,\n object: \"chat.completion.chunk\",\n created,\n model: effectiveModel,\n choices: [\n {\n index: 0,\n delta: {\n tool_calls: [{ index: tcIdx, function: { arguments: slice } }],\n },\n logprobs: null,\n finish_reason: null,\n },\n ],\n ...(fingerprint !== undefined && { system_fingerprint: fingerprint }),\n });\n }\n }\n\n // Finish chunk\n chunks.push({\n id,\n object: \"chat.completion.chunk\",\n created,\n model: effectiveModel,\n choices: [\n {\n index: 0,\n delta: {},\n logprobs: null,\n finish_reason: overrides?.finishReason ?? \"tool_calls\",\n },\n ],\n ...(fingerprint !== undefined && { system_fingerprint: fingerprint }),\n });\n\n return chunks;\n}\n\n// Non-streaming response builders\n\nexport function buildTextCompletion(\n content: string,\n model: string,\n reasoning?: string,\n overrides?: ResponseOverrides,\n requestMessages?: ChatCompletionRequest[\"messages\"],\n): ChatCompletion {\n const promptText = requestMessages\n ? requestMessages\n .map((m) =>\n typeof m.content === \"string\"\n ? m.content\n : Array.isArray(m.content)\n ? m.content.map((p) => p.text ?? \"\").join(\"\")\n : \"\",\n )\n .join(\"\")\n : \"\";\n return {\n id: overrides?.id ?? generateId(),\n object: \"chat.completion\",\n created: overrides?.created ?? Math.floor(Date.now() / 1000),\n model: overrides?.model ?? model,\n choices: [\n {\n index: 0,\n message: {\n role: overrides?.role ?? \"assistant\",\n content,\n refusal: null,\n ...(reasoning ? { reasoning_content: reasoning } : {}),\n },\n logprobs: null,\n finish_reason: overrides?.finishReason ?? \"stop\",\n },\n ],\n usage: resolveUsage(overrides, promptText, content),\n ...(overrides?.systemFingerprint !== undefined && {\n system_fingerprint: overrides.systemFingerprint,\n }),\n };\n}\n\nexport function buildToolCallCompletion(\n toolCalls: ToolCall[],\n model: string,\n reasoning?: string,\n overrides?: ResponseOverrides,\n requestMessages?: ChatCompletionRequest[\"messages\"],\n): ChatCompletion {\n const promptText = requestMessages\n ? requestMessages\n .map((m) =>\n typeof m.content === \"string\"\n ? m.content\n : Array.isArray(m.content)\n ? m.content.map((p) => p.text ?? \"\").join(\"\")\n : \"\",\n )\n .join(\"\")\n : \"\";\n const completionText = toolCalls.map((tc) => tc.name + tc.arguments).join(\"\");\n return {\n id: overrides?.id ?? generateId(),\n object: \"chat.completion\",\n created: overrides?.created ?? Math.floor(Date.now() / 1000),\n model: overrides?.model ?? model,\n choices: [\n {\n index: 0,\n message: {\n role: overrides?.role ?? \"assistant\",\n content: null,\n refusal: null,\n ...(reasoning ? { reasoning_content: reasoning } : {}),\n tool_calls: toolCalls.map((tc) => ({\n id: tc.id || generateToolCallId(),\n type: \"function\" as const,\n function: { name: tc.name, arguments: tc.arguments },\n })),\n },\n logprobs: null,\n finish_reason: overrides?.finishReason ?? \"tool_calls\",\n },\n ],\n usage: resolveUsage(overrides, promptText, completionText),\n ...(overrides?.systemFingerprint !== undefined && {\n system_fingerprint: overrides.systemFingerprint,\n }),\n };\n}\n\nexport function buildContentWithToolCallsChunks(\n content: string,\n toolCalls: ToolCall[],\n model: string,\n chunkSize: number,\n reasoning?: string,\n overrides?: ResponseOverrides,\n blocks?: FixtureBlock[],\n): SSEChunk[] {\n const id = overrides?.id ?? generateId();\n const created = overrides?.created ?? Math.floor(Date.now() / 1000);\n const effectiveModel = overrides?.model ?? model;\n const chunks: SSEChunk[] = [];\n const fingerprint = overrides?.systemFingerprint;\n\n if (blocks && blocks.length > 0) {\n // NEW: emit chunks in fixture block array order.\n //\n // DEGENERATE PROVIDER NOTE: in OpenAI chat-completions, `delta.content` and\n // `delta.tool_calls` are SEPARATE channels that the client merges with no\n // positional interleaving. So \"tool-call-before-text\" is NOT semantically\n // observable to a real client — it reassembles content and tool calls into\n // their own buckets regardless of chunk order. We still emit honest\n // array-order chunks (the SSE chunk SEQUENCE is the contract this path\n // asserts), but we do NOT fake interleaving the channel cannot express.\n const outcome = resolveFixtureBlockOutcome(blocks);\n const ordered = outcome.ordered;\n\n // Reasoning chunks (emitted first, OpenRouter format) — unchanged from legacy.\n if (reasoning) {\n for (let i = 0; i < reasoning.length; i += chunkSize) {\n const slice = reasoning.slice(i, i + chunkSize);\n chunks.push({\n id,\n object: \"chat.completion.chunk\",\n created,\n model: effectiveModel,\n choices: [\n {\n index: 0,\n delta: {\n ...(i === 0 && { role: overrides?.role ?? \"assistant\" }),\n reasoning_content: slice,\n },\n logprobs: null,\n finish_reason: null,\n },\n ],\n ...(fingerprint !== undefined && { system_fingerprint: fingerprint }),\n });\n }\n }\n\n // Role chunk — preserved exactly as the legacy path.\n chunks.push({\n id,\n object: \"chat.completion.chunk\",\n created,\n model: effectiveModel,\n choices: [\n {\n index: 0,\n delta: { role: overrides?.role ?? \"assistant\", content: \"\" },\n logprobs: null,\n finish_reason: null,\n },\n ],\n ...(fingerprint !== undefined && { system_fingerprint: fingerprint }),\n });\n\n // Tool-call `index` is assigned in encounter order across the block array.\n let tcIdx = 0;\n for (const block of ordered) {\n if (block.type === \"text\") {\n for (let i = 0; i < block.text.length; i += chunkSize) {\n const slice = block.text.slice(i, i + chunkSize);\n chunks.push({\n id,\n object: \"chat.completion.chunk\",\n created,\n model: effectiveModel,\n choices: [{ index: 0, delta: { content: slice }, logprobs: null, finish_reason: null }],\n ...(fingerprint !== undefined && { system_fingerprint: fingerprint }),\n });\n }\n } else {\n const tcId = block.id || generateToolCallId();\n\n // Initial tool call chunk (id + function name)\n chunks.push({\n id,\n object: \"chat.completion.chunk\",\n created,\n model: effectiveModel,\n choices: [\n {\n index: 0,\n delta: {\n tool_calls: [\n {\n index: tcIdx,\n id: tcId,\n type: \"function\",\n function: { name: block.name, arguments: \"\" },\n },\n ],\n },\n logprobs: null,\n finish_reason: null,\n },\n ],\n ...(fingerprint !== undefined && { system_fingerprint: fingerprint }),\n });\n\n // Argument streaming chunks\n const args = block.arguments;\n for (let i = 0; i < args.length; i += chunkSize) {\n const slice = args.slice(i, i + chunkSize);\n chunks.push({\n id,\n object: \"chat.completion.chunk\",\n created,\n model: effectiveModel,\n choices: [\n {\n index: 0,\n delta: {\n tool_calls: [{ index: tcIdx, function: { arguments: slice } }],\n },\n logprobs: null,\n finish_reason: null,\n },\n ],\n ...(fingerprint !== undefined && { system_fingerprint: fingerprint }),\n });\n }\n tcIdx++;\n }\n }\n\n // Derive the default terminal from the blocks actually emitted.\n chunks.push({\n id,\n object: \"chat.completion.chunk\",\n created,\n model: effectiveModel,\n choices: [\n {\n index: 0,\n delta: {},\n logprobs: null,\n finish_reason: overrides?.finishReason ?? (outcome.hasToolCalls ? \"tool_calls\" : \"stop\"),\n },\n ],\n ...(fingerprint !== undefined && { system_fingerprint: fingerprint }),\n });\n\n return chunks;\n }\n\n // EXISTING legacy code, byte-for-byte UNCHANGED.\n // Reasoning chunks (emitted before content, OpenRouter format)\n if (reasoning) {\n for (let i = 0; i < reasoning.length; i += chunkSize) {\n const slice = reasoning.slice(i, i + chunkSize);\n chunks.push({\n id,\n object: \"chat.completion.chunk\",\n created,\n model: effectiveModel,\n choices: [\n {\n index: 0,\n delta: {\n ...(i === 0 && { role: overrides?.role ?? \"assistant\" }),\n reasoning_content: slice,\n },\n logprobs: null,\n finish_reason: null,\n },\n ],\n ...(fingerprint !== undefined && { system_fingerprint: fingerprint }),\n });\n }\n }\n\n // Role chunk\n chunks.push({\n id,\n object: \"chat.completion.chunk\",\n created,\n model: effectiveModel,\n choices: [\n {\n index: 0,\n delta: { role: overrides?.role ?? \"assistant\", content: \"\" },\n logprobs: null,\n finish_reason: null,\n },\n ],\n ...(fingerprint !== undefined && { system_fingerprint: fingerprint }),\n });\n\n // Content chunks\n for (let i = 0; i < content.length; i += chunkSize) {\n const slice = content.slice(i, i + chunkSize);\n chunks.push({\n id,\n object: \"chat.completion.chunk\",\n created,\n model: effectiveModel,\n choices: [{ index: 0, delta: { content: slice }, logprobs: null, finish_reason: null }],\n ...(fingerprint !== undefined && { system_fingerprint: fingerprint }),\n });\n }\n\n // Tool call chunks — one initial chunk per tool call, then argument chunks\n for (let tcIdx = 0; tcIdx < toolCalls.length; tcIdx++) {\n const tc = toolCalls[tcIdx];\n const tcId = tc.id || generateToolCallId();\n\n // Initial tool call chunk (id + function name)\n chunks.push({\n id,\n object: \"chat.completion.chunk\",\n created,\n model: effectiveModel,\n choices: [\n {\n index: 0,\n delta: {\n tool_calls: [\n {\n index: tcIdx,\n id: tcId,\n type: \"function\",\n function: { name: tc.name, arguments: \"\" },\n },\n ],\n },\n logprobs: null,\n finish_reason: null,\n },\n ],\n ...(fingerprint !== undefined && { system_fingerprint: fingerprint }),\n });\n\n // Argument streaming chunks\n const args = tc.arguments;\n for (let i = 0; i < args.length; i += chunkSize) {\n const slice = args.slice(i, i + chunkSize);\n chunks.push({\n id,\n object: \"chat.completion.chunk\",\n created,\n model: effectiveModel,\n choices: [\n {\n index: 0,\n delta: {\n tool_calls: [{ index: tcIdx, function: { arguments: slice } }],\n },\n logprobs: null,\n finish_reason: null,\n },\n ],\n ...(fingerprint !== undefined && { system_fingerprint: fingerprint }),\n });\n }\n }\n\n // Finish chunk\n chunks.push({\n id,\n object: \"chat.completion.chunk\",\n created,\n model: effectiveModel,\n choices: [\n {\n index: 0,\n delta: {},\n logprobs: null,\n finish_reason: overrides?.finishReason ?? \"tool_calls\",\n },\n ],\n ...(fingerprint !== undefined && { system_fingerprint: fingerprint }),\n });\n\n return chunks;\n}\n\n// NOTE (#274): this NON-streaming OpenAI chat-completions builder is\n// intentionally degenerate w.r.t. `blocks` ordering. A chat.completion puts\n// `message.content` and `message.tool_calls` in SEPARATE fields on a single\n// message object — they are NOT a positionally-observable array, so a\n// tool-first `blocks` fixture cannot be expressed in the wire shape. Honoring\n// block order here would be a no-op, so the legacy content+tool_calls fields\n// are unchanged. (Order-observable surfaces — Claude `content[]`, Gemini\n// `parts[]`, Responses `output[]` — DO honor block order; see those builders.)\nexport function buildContentWithToolCallsCompletion(\n content: string,\n toolCalls: ToolCall[],\n model: string,\n reasoning?: string,\n overrides?: ResponseOverrides,\n requestMessages?: ChatCompletionRequest[\"messages\"],\n): ChatCompletion {\n const promptText = requestMessages\n ? requestMessages\n .map((m) =>\n typeof m.content === \"string\"\n ? m.content\n : Array.isArray(m.content)\n ? m.content.map((p) => p.text ?? \"\").join(\"\")\n : \"\",\n )\n .join(\"\")\n : \"\";\n const completionText = content + toolCalls.map((tc) => tc.name + tc.arguments).join(\"\");\n return {\n id: overrides?.id ?? generateId(),\n object: \"chat.completion\",\n created: overrides?.created ?? Math.floor(Date.now() / 1000),\n model: overrides?.model ?? model,\n choices: [\n {\n index: 0,\n message: {\n role: overrides?.role ?? \"assistant\",\n content,\n refusal: null,\n ...(reasoning ? { reasoning_content: reasoning } : {}),\n tool_calls: toolCalls.map((tc) => ({\n id: tc.id || generateToolCallId(),\n type: \"function\" as const,\n function: { name: tc.name, arguments: tc.arguments },\n })),\n },\n logprobs: null,\n finish_reason: overrides?.finishReason ?? \"tool_calls\",\n },\n ],\n usage: resolveUsage(overrides, promptText, completionText),\n ...(overrides?.systemFingerprint !== undefined && {\n system_fingerprint: overrides.systemFingerprint,\n }),\n };\n}\n\n// ─── HTTP helpers ─────────────────────────────────────────────────────────\n\nconst DEFAULT_MAX_BODY_BYTES = 10 * 1024 * 1024; // 10 MB\n\n/**\n * A body read that stopped because the CLIENT sent more bytes than the route\n * allows. The socket is already destroyed when this rejects, so no status line\n * ever reaches the caller — but the fault is the caller's, and error arms that\n * journal and log the failure need to say so. Carrying the classification on\n * the error type (rather than re-matching the message text) is what lets them:\n * the message is unchanged from the plain `Error` this replaced.\n */\nexport class RequestBodyTooLargeError extends Error {\n constructor(readonly maxBytes: number) {\n super(`Request body exceeded size limit of ${maxBytes} bytes`);\n this.name = \"RequestBodyTooLargeError\";\n }\n}\n\n/**\n * Read a request body as raw bytes, preserving every octet.\n *\n * This is the byte-level primitive {@link readBody} is built on: routes that\n * must not lose bytes (binary file uploads) take the Buffer, everything else\n * keeps taking the decoded string. Splitting it this way leaves the text path\n * byte-identical — `readBody` performs exactly the same `Buffer.concat(...)`\n * + default (utf8) `toString()` it always did, just one call later.\n */\nexport function readBodyBuffer(\n req: http.IncomingMessage,\n maxBytes: number = DEFAULT_MAX_BODY_BYTES,\n): Promise<Buffer> {\n return new Promise((resolve, reject) => {\n const chunks: Buffer[] = [];\n let totalBytes = 0;\n let settled = false;\n req.on(\"data\", (chunk: Buffer) => {\n if (settled) return;\n totalBytes += chunk.length;\n if (totalBytes > maxBytes) {\n settled = true;\n req.destroy();\n reject(new RequestBodyTooLargeError(maxBytes));\n return;\n }\n chunks.push(chunk);\n });\n req.on(\"end\", () => {\n if (!settled) {\n settled = true;\n resolve(Buffer.concat(chunks));\n }\n });\n req.on(\"error\", (err) => {\n if (!settled) {\n settled = true;\n reject(err);\n }\n });\n });\n}\n\n/**\n * A body read with a buffering bound that does NOT cost the caller its socket.\n *\n * `buffer` is `null` when the body went over the buffering bound: the bytes\n * read so far were released and the rest was drained, so the request still\n * reaches `end` and the route can answer a real status (with CORS) instead of\n * the client seeing an `ECONNRESET`.\n */\nexport interface BoundedBody {\n buffer: Buffer | null;\n /** Bytes seen on the wire, counted past the buffering bound. */\n bytesRead: number;\n}\n\n/**\n * Read a request body that may legitimately be over-size, without either\n * buffering it all or dropping the socket.\n *\n * {@link readBodyBuffer} answers an over-size body with `req.destroy()`: no\n * status line, no body, no CORS headers. That is the right default for routes\n * whose limit is a pure DoS bound, but it is the wrong answer for a route that\n * owes the caller a `400` describing what was wrong — the caller cannot read an\n * error it never receives. So this variant splits the two concerns:\n *\n * - `maxBytes` bounds MEMORY. Past it the buffered chunks are dropped and the\n * body is only counted, so peak retention never exceeds `maxBytes` no matter\n * how large the body is. The caller gets `buffer: null` and answers itself.\n * - `drainMaxBytes` bounds WORK, and is the only socket drop here: a body that\n * keeps coming past it is not worth draining and rejects as `readBodyBuffer`\n * would.\n */\nexport function readBodyBufferBounded(\n req: http.IncomingMessage,\n maxBytes: number,\n drainMaxBytes: number,\n): Promise<BoundedBody> {\n return new Promise((resolve, reject) => {\n let chunks: Buffer[] | null = [];\n let totalBytes = 0;\n let settled = false;\n req.on(\"data\", (chunk: Buffer) => {\n if (settled) return;\n totalBytes += chunk.length;\n if (chunks !== null && totalBytes > maxBytes) {\n // Release on the FIRST chunk over the bound, before pushing it: the\n // bytes are unusable now, and holding them is what would make peak\n // memory track the attacker's body size instead of `maxBytes`.\n chunks = null;\n }\n if (totalBytes > drainMaxBytes) {\n settled = true;\n req.destroy();\n reject(new RequestBodyTooLargeError(drainMaxBytes));\n return;\n }\n if (chunks !== null) chunks.push(chunk);\n });\n req.on(\"end\", () => {\n if (settled) return;\n settled = true;\n resolve({ buffer: chunks === null ? null : Buffer.concat(chunks), bytesRead: totalBytes });\n });\n req.on(\"error\", (err) => {\n if (!settled) {\n settled = true;\n reject(err);\n }\n });\n });\n}\n\nexport function readBody(\n req: http.IncomingMessage,\n maxBytes: number = DEFAULT_MAX_BODY_BYTES,\n): Promise<string> {\n return readBodyBuffer(req, maxBytes).then((buf) => buf.toString());\n}\n\n// ─── Pattern matching ─────────────────────────────────────────────────────\n\n/**\n * Case-insensitive substring/regex match used for search, rerank, and\n * moderation endpoints where exact casing rarely matters. String patterns\n * are lowercased on both sides before comparison.\n *\n * Note: This intentionally differs from the case-sensitive matching in\n * {@link matchFixture} (router.ts), where fixture authors expect exact\n * string matching against chat completion user messages.\n */\nexport function matchesPattern(text: string, pattern: string | RegExp): boolean {\n if (typeof pattern === \"string\") {\n return text.toLowerCase().includes(pattern.toLowerCase());\n }\n // A global/sticky RegExp carries mutable `lastIndex` state that `.test()`\n // advances. Save and restore it so callers reusing the same regex object\n // (search/rerank/moderation filter loops) are not left with mutated state\n // and get consistent results across repeated calls.\n const savedLastIndex = pattern.lastIndex;\n pattern.lastIndex = 0;\n const result = pattern.test(text);\n pattern.lastIndex = savedLastIndex;\n return result;\n}\n\n/**\n * Which test LLM and other HTTP-route traffic belongs to: the `X-Test-Id`\n * header (raw, not percent-decoded) wins, then `?testId=` in the query string\n * (read through `URLSearchParams`, so percent-decoded and with `+` as a\n * space), then `DEFAULT_TEST_ID`. Every per-test axis of those routes\n * (fixture match-counts, the journal filter, chaos scoping) resolves through\n * this. MCP mounts resolve the test id with {@link resolveMcpIdentity}\n * instead, which percent-decodes the header, so `X-Test-Id: a%20b` is `a%20b`\n * here and `a b` there. Both read a `+` in the query as a space.\n */\nexport function resolveTestId(headers: IncomingHttpHeaders, url: string | undefined): string {\n const headerValue = headers[\"x-test-id\"];\n if (Array.isArray(headerValue)) {\n if (headerValue.length > 0 && headerValue[0]) return headerValue[0];\n } else if (typeof headerValue === \"string\" && headerValue) {\n return headerValue;\n }\n\n const qIdx = (url ?? \"/\").indexOf(\"?\");\n if (qIdx !== -1) {\n const params = new URLSearchParams((url ?? \"/\").slice(qIdx + 1));\n const queryValue = params.get(\"testId\");\n if (queryValue) return queryValue;\n }\n\n return DEFAULT_TEST_ID;\n}\n\nexport function getTestId(req: http.IncomingMessage): string {\n return resolveTestId(req.headers, req.url);\n}\n\nexport function getContext(req: http.IncomingMessage): string | undefined {\n const headerValue = req.headers[\"x-aimock-context\"];\n if (Array.isArray(headerValue)) {\n if (headerValue.length > 0 && headerValue[0]) return headerValue[0];\n } else if (typeof headerValue === \"string\" && headerValue) {\n return headerValue;\n }\n return undefined;\n}\n\n/**\n * Decode one MCP identity value: an `X-Test-Id` / `X-AIMock-Context` header\n * value, and also each query name and (through {@link decodeMcpQueryValue})\n * each `?testId=` / `?context=` / `?undeclared=` query value. Clients send `encodeURIComponent(id)` because\n * `fetch` rejects characters such as `›` in a header. A value that is not\n * valid percent-encoding (a raw `applies 50% discount`) is used as-is, with\n * `fellBack: true` so the caller can log the fallback. Only that `URIError`\n * is caught; any other throw propagates. MCP requests only: LLM routes read\n * the header raw and the query through `URLSearchParams` (see\n * {@link resolveTestId}).\n */\nexport function decodeMcpHeaderValue(raw: string): { value: string; fellBack: boolean } {\n try {\n return { value: decodeURIComponent(raw), fellBack: false };\n } catch (err) {\n if (!(err instanceof URIError)) throw err;\n return { value: raw, fellBack: true };\n }\n}\n\n/**\n * Decode one MCP query value (`?testId=`, `?context=`, `?undeclared=`) the way\n * `URLSearchParams` reads it (spec I1): a `+` is a space, then the value is\n * percent-decoded by the {@link decodeMcpHeaderValue} rule. So a query built\n * with `URL.searchParams.set(...)` (a space sent as `+`) and one built with\n * `encodeURIComponent` both resolve to the id itself. A value that is not\n * valid percent-encoding is used as-is (its `+` still a space), with\n * `fellBack: true`.\n */\nfunction decodeMcpQueryValue(raw: string): { value: string; fellBack: boolean } {\n return decodeMcpHeaderValue(raw.replace(/\\+/g, \" \"));\n}\n\n/** The parts of a request that {@link resolveMcpIdentity} reads. */\nexport type McpIdentityRequest = Pick<http.IncomingMessage, \"headers\" | \"url\"> &\n Partial<Pick<http.IncomingMessage, \"headersDistinct\">>;\n\n/**\n * Every value of the header `name`, one per header line: `[]` when absent.\n * Node joins a repeated header into one comma-joined `req.headers` string, so\n * the values come from `req.headersDistinct` (one element per line) when the\n * request has it. A caller without it (a hand-built request) is read from\n * `req.headers`: one element for a string, each element for an array.\n */\nfunction headerValues(req: McpIdentityRequest, name: string): string[] {\n const distinct = req.headersDistinct?.[name];\n if (distinct !== undefined) return distinct;\n const value = req.headers[name];\n if (value === undefined) return [];\n return Array.isArray(value) ? value : [value];\n}\n\n/** One `name=value` pair of a query string, its name decoded. */\ninterface McpQueryPair {\n /** The decoded name, or `null` when the name is not valid percent-encoding. */\n name: string | null;\n /** The raw (still percent-encoded) value; `\"\"` for a bare name with no `=`. */\n value: string;\n}\n\n/**\n * Every pair of a query string, in order, each name decoded by the\n * `decodeMcpHeaderValue` rule. A bare `name` with no `=` counts as an\n * occurrence with an empty value, so `?testId&testId=t1` has two. Each value\n * is left raw for the caller to decode with {@link decodeMcpQueryValue}.\n * A name that is not valid percent-encoding (`test%Id`) gets `name: null`: it\n * matches no field, and the caller counts it.\n */\nfunction queryPairs(query: string): McpQueryPair[] {\n if (query === \"\") return [];\n return query.split(\"&\").map((pair) => {\n const eq = pair.indexOf(\"=\");\n const key = decodeMcpHeaderValue(eq === -1 ? pair : pair.slice(0, eq));\n return { name: key.fellBack ? null : key.value, value: eq === -1 ? \"\" : pair.slice(eq + 1) };\n });\n}\n\n/**\n * A request value as a message part, the way the message builder quotes every\n * user string: JSON-quoted (quotes and control characters escaped), then cut\n * once as JSON text, so the whole echo, quotes, escapes and the\n * `… (<n> more chars)` count included, fits in `MCP_FAKES_ECHO_LIMIT` and the\n * cut never splits a JSON escape or a surrogate pair.\n */\nfunction echoIdentityValue(value: string): MessageValue {\n return quote(value, MCP_FAKES_ECHO_LIMIT);\n}\n\n/** The session fields an MCP session binds at `initialize`. */\ntype McpIdentitySession = Pick<MCPSession, \"testId\" | \"context\" | \"undeclared\">;\n\n/** An MCP identity input: the test id, the context or the undeclared override. */\nexport type McpIdentityField = \"testId\" | \"context\" | \"undeclared\";\n\n/**\n * Why an MCP request's identity inputs cannot be honored. The MCP mount is\n * meant to answer `status` with `{ \"error\": message }` and serve nothing.\n * - `MCP_INVALID_UNDECLARED`: an undeclared override (`X-AIMock-MCP-Undeclared`\n * / `?undeclared=`) other than `allow` / `deny`, so a typo such as `denied`\n * can never turn a `deny` scenario into the permissive default. `raw` is\n * the value as sent (the query value still percent-encoded), not quoted,\n * cut by `cutText` to `MCP_FAKES_ECHO_LIMIT` chars, so a huge value is\n * never carried in full. A query value that\n * is not valid percent-encoding is also an error, and the message says so.\n * - `MCP_DUPLICATE_IDENTITY`: one field sent more than once on one path (a\n * header array with more than one element, or a repeated query name, empty\n * or not). No value is picked. `count` is how many were sent.\n */\nexport type McpIdentityError =\n | {\n code: \"MCP_INVALID_UNDECLARED\";\n status: 400;\n field: \"undeclared\";\n source: \"header\" | \"query\";\n raw: string;\n message: string;\n }\n | {\n code: \"MCP_DUPLICATE_IDENTITY\";\n status: 400;\n field: McpIdentityField;\n source: \"header\" | \"query\";\n count: number;\n message: string;\n };\n\n/** The result of {@link resolveMcpIdentity}: a resolved identity, or an error to answer with. */\nexport type McpIdentityResolution =\n | {\n ok: true;\n identity: McpFakeIdentity;\n /** Whether the request itself carried each field (raw header/query, not the session). */\n supplied: { testId: boolean; context: boolean; undeclared: boolean };\n /**\n * Whether a header or query value sent for each field was not valid\n * percent-encoding and fell back to the raw value. Set also when the\n * header supplied the field and the ignored query value did not decode,\n * so bad encoding is never silent. For the MCP mount to log a warning,\n * once per session per field.\n */\n fellBack: { testId: boolean; context: boolean };\n /**\n * How many query names were not valid percent-encoding (`?test%Id=`).\n * Such a name matches no field. Present only when at least one was\n * found, for the MCP mount to log.\n */\n undecodedQueryNames?: number;\n }\n | { ok: false; error: McpIdentityError };\n\nconst MCP_IDENTITY_INPUTS: Record<\n McpIdentityField,\n { header: string; query: string; label: string }\n> = {\n testId: { header: \"x-test-id\", query: \"testId\", label: \"X-Test-Id\" },\n context: { header: \"x-aimock-context\", query: \"context\", label: \"X-AIMock-Context\" },\n undeclared: {\n header: \"x-aimock-mcp-undeclared\",\n query: \"undeclared\",\n label: \"X-AIMock-MCP-Undeclared\",\n },\n};\n\n/**\n * Resolve the identity (test id, context, undeclared override) of one MCP request.\n * Each field resolves on its own, first of: the header, the query parameter,\n * the session value bound at `initialize`. A field sent more than once on\n * either path (more than one header line, read from `headersDistinct` because\n * Node joins repeated lines into one `headers` string; a header array from a\n * hand-built request; a repeated query name) is an error\n * (`MCP_DUPLICATE_IDENTITY`), even when the other path also carries it. For\n * the test id and the context, a header value is decoded with\n * `decodeURIComponent` (raw value on failure, with `fellBack` set) and a query\n * value the way `URLSearchParams` reads it (spec I1): a `+` is a space, then\n * the same `decodeURIComponent` rule. So `encodeURIComponent` output (what\n * clients send) and a query built with `URL.searchParams` both decode to the\n * id itself, while a raw `+` is a `+` in the header and a space in the query.\n * When both paths carry a test id or a context, the header is used and the\n * query value is ignored, even if the two differ, but an ignored query value\n * that does not decode still sets `fellBack`. The undeclared header is not\n * percent-decoded; its query value is, by the same query rule. Both are\n * trimmed and compared case-insensitively. `supplied` says whether the\n * request itself carried a non-empty header or query value for the field,\n * never `resolveTestId`'s result (which returns `\"__default__\"` when nothing\n * is sent). Blank values: an empty value is not supplied and falls through;\n * it is never rejected. A whitespace-only value is not supplied for the\n * undeclared override (it is trimmed), but is a real value for the test id\n * and the context, which match exactly and are never trimmed (Node trims a\n * header value, so only the query or a hand-built request can carry one). A\n * lone query name with no `=` carries no value. An empty session value counts\n * as none, and a missing `req.url` as `/`. A field with no value anywhere is\n * `null`. An unrecognized undeclared override on the header or the query is\n * an error (`ok: false`), not a fall-through: both are checked before the\n * header one is used, so a valid header does not hide a mistyped query value.\n * Bad percent-encoding is never silent: a test id or context value that does\n * not decode sets `fellBack`, on either path, used or ignored; an undeclared\n * query value that does not decode is that error, with the message saying\n * so; and a query name that does not decode matches no field and is counted\n * in `undecodedQueryNames`. `resolveTestId` / `getContext` stay unchanged for\n * LLM routes.\n */\nexport function resolveMcpIdentity(\n req: McpIdentityRequest,\n session?: McpIdentitySession,\n): McpIdentityResolution {\n const url = req.url ?? \"/\";\n const qIdx = url.indexOf(\"?\");\n const pairs = queryPairs(qIdx === -1 ? \"\" : url.slice(qIdx + 1));\n\n // Collect each field's one raw value per path, rejecting any repetition first.\n const raw = {} as Record<McpIdentityField, { header?: string; query?: string }>;\n for (const field of [\"testId\", \"context\", \"undeclared\"] as const) {\n const input = MCP_IDENTITY_INPUTS[field];\n const sent = [\n [\"header\", headerValues(req, input.header)],\n [\"query\", pairs.filter((p) => p.name === input.query).map((p) => p.value)],\n ] as const;\n for (const [source, values] of sent) {\n if (values.length > 1) {\n const label =\n source === \"header\"\n ? msg`${fixed(input.label)} header`\n : msg`?${fixed(input.query)}= query parameter`;\n return {\n ok: false,\n error: {\n code: \"MCP_DUPLICATE_IDENTITY\",\n status: 400,\n field,\n source,\n count: values.length,\n message: build(msg`Duplicate ${label}: ${values.length} values sent, expected one`),\n },\n };\n }\n }\n raw[field] = { header: sent[0][1][0], query: sent[1][1][0] };\n }\n\n const fromRequest = (\n field: \"testId\" | \"context\",\n ): { value: string | null; fellBack: boolean } => {\n const { header, query: q } = raw[field];\n const fromQuery = q ? decodeMcpQueryValue(q) : null;\n if (header) {\n // The header wins; an ignored query value that does not decode still flags.\n const used = decodeMcpHeaderValue(header);\n return { value: used.value, fellBack: used.fellBack || (fromQuery?.fellBack ?? false) };\n }\n return fromQuery ?? { value: null, fellBack: false };\n };\n\n const testId = fromRequest(\"testId\");\n const context = fromRequest(\"context\");\n\n // Check every supplied undeclared value before using one: the header wins,\n // but a mistyped query value next to it is still an error.\n let undeclared: McpFakeUndeclaredPolicy | null = null;\n for (const source of [\"header\", \"query\"] as const) {\n const sent = raw.undeclared[source];\n if (sent === undefined) continue;\n // Header decoding covers only X-Test-Id / X-AIMock-Context; this header is\n // not percent-decoded. The query value is decoded.\n const decoded =\n source === \"header\" ? { value: sent, fellBack: false } : decodeMcpQueryValue(sent);\n if (decoded.value.trim() === \"\") continue;\n const parsed = parseMcpUndeclared(decoded.value);\n if (parsed === null) {\n const echoed = echoIdentityValue(sent);\n const label =\n source === \"header\"\n ? msg`X-AIMock-MCP-Undeclared header`\n : msg`?undeclared= query parameter`;\n const note = decoded.fellBack ? msg` (not valid percent-encoding)` : msg``;\n return {\n ok: false,\n error: {\n code: \"MCP_INVALID_UNDECLARED\",\n status: 400,\n field: \"undeclared\",\n source,\n raw: cutText(sent),\n message: build(msg`Invalid ${label} value ${echoed}${note}: expected allow or deny`),\n },\n };\n }\n undeclared ??= parsed;\n }\n\n const undecodedQueryNames = pairs.filter((p) => p.name === null).length;\n return {\n ok: true,\n identity: {\n testId: testId.value ?? (session?.testId || null),\n context: context.value ?? (session?.context || null),\n undeclared: undeclared ?? session?.undeclared ?? null,\n },\n supplied: {\n testId: testId.value !== null,\n context: context.value !== null,\n undeclared: undeclared !== null,\n },\n fellBack: { testId: testId.fellBack, context: context.fellBack },\n ...(undecodedQueryNames > 0 ? { undecodedQueryNames } : {}),\n };\n}\n\n// ─── Snapshot recording helpers ──────────────────────────────────────────────\n\n/**\n * Convert a test ID (e.g. Playwright titlePath) into a filesystem-safe slug\n * suitable for use as a directory name in snapshot-style recording.\n */\nexport function slugifyTestId(testId: string): string {\n return testId\n .replace(/^.*?\\.(?:spec|test|e2e)\\.(?:tsx|ts|jsx|js|mjs|cjs)(?=\\s|›|$)\\s*›?\\s*/i, \"\") // strip test file extension prefix\n .replace(/\\s*[›>]\\s*/g, \"--\") // Playwright titlePath separator → double dash\n .replace(/[^\\w-]/g, \"-\") // non-word chars → dash\n .replace(/-{3,}/g, \"--\") // collapse 3+ dashes to double\n .replace(/^-+|-+$/g, \"\") // trim leading/trailing dashes\n .toLowerCase();\n}\n\n/**\n * Make a request context (the `X-AIMock-Context` header value) safe to use as\n * a single directory segment in a recorded-fixture path. The header is\n * attacker-controllable, so a raw value containing `../`, path separators, or\n * an absolute-path prefix would let the written fixture escape the configured\n * fixtures base directory. Mirrors `slugifyTestId`: non-word characters\n * (including `/`, `\\`, and `.`) collapse to dashes, so the result is always a\n * single flat segment with no traversal semantics. Returns \"\" when the value\n * sanitizes to nothing (caller treats that as \"no context segment\").\n */\nexport function slugifyContext(context: string): string {\n return context\n .replace(/[^\\w-]/g, \"-\") // non-word chars (incl. / \\ . :) → dash\n .replace(/-{3,}/g, \"--\") // collapse 3+ dashes to double\n .replace(/^-+|-+$/g, \"\") // trim leading/trailing dashes\n .toLowerCase();\n}\n\n// ─── Request shape validation ──────────────────────────────────────────────\n\n/**\n * Validate the shape of a chat `messages` array (Cohere / Ollama / Bedrock\n * style: entries with `content` and optional `tool_calls`).\n *\n * Returns an error detail string when the shape is wrong, or null when the\n * provider converter can safely consume it. Wrong types here used to throw\n * inside the converters (`content.filter` on a number, `.map` on a string\n * `tools`, `msg.role` on a null entry) and surface as 500s. `null` content\n * is accepted — assistant messages legitimately carry `content: null`\n * alongside tool calls, and the converters coerce it to \"\".\n *\n * `checkToolCalls` must be false for providers whose converter never reads\n * `msg.tool_calls` (Bedrock: tool calls are `tool_use` content blocks and\n * `tool_calls` is not a field of the Anthropic Messages body at all). There a\n * stray `tool_calls` is inert — rejecting it turns a request that used to\n * match a fixture and return 200 into a 400.\n */\nexport function validateChatMessages(\n messages: unknown,\n { checkToolCalls = true }: { checkToolCalls?: boolean } = {},\n): string | null {\n // Matches the historic \"messages array is required\" detail so handlers can\n // compose the exact legacy message for a missing/non-array field.\n if (!Array.isArray(messages)) return \"messages array is required\";\n for (let i = 0; i < messages.length; i++) {\n const msg = messages[i] as Record<string, unknown> | null;\n if (msg === null || typeof msg !== \"object\" || Array.isArray(msg)) {\n return `messages[${i}] must be an object`;\n }\n const content = msg.content;\n if (\n content !== undefined &&\n content !== null &&\n typeof content !== \"string\" &&\n !Array.isArray(content)\n ) {\n return `messages[${i}].content must be a string or an array`;\n }\n if (Array.isArray(content) && content.some((p) => p === null || p === undefined)) {\n return `messages[${i}].content must be a string or an array`;\n }\n const toolCalls = msg.tool_calls;\n if (checkToolCalls && toolCalls !== undefined && toolCalls !== null) {\n if (!Array.isArray(toolCalls)) {\n return `messages[${i}].tool_calls must be an array`;\n }\n for (const tc of toolCalls) {\n if (tc === null || typeof tc !== \"object\" || Array.isArray(tc)) {\n return `messages[${i}].tool_calls entries must be objects`;\n }\n const fn = (tc as Record<string, unknown>).function;\n if (fn === null || typeof fn !== \"object\" || Array.isArray(fn)) {\n return `messages[${i}].tool_calls entries must have a function object`;\n }\n }\n }\n }\n return null;\n}\n\n/**\n * Validate an optional top-level `tools` field. A string (or other\n * non-array) value passes the `req.tools && req.tools.length` gate\n * (`\"hi\".length > 0`) and then throws on `.map` — a 500 instead of a 400.\n * Returns an error detail string, or null when absent or a valid array.\n */\nexport function validateToolsField(tools: unknown): string | null {\n if (tools !== undefined && tools !== null && !Array.isArray(tools)) {\n return \"tools must be an array\";\n }\n if (Array.isArray(tools)) {\n for (let i = 0; i < tools.length; i++) {\n const t = tools[i] as Record<string, unknown> | null;\n if (t === null || typeof t !== \"object\" || Array.isArray(t)) {\n return `tools[${i}] must be an object`;\n }\n }\n }\n return null;\n}\n\n// ─── Request body helpers ──────────────────────────────────────────────────\n\n/**\n * True when a parsed JSON request body is a plain object.\n *\n * `JSON.parse(\"null\")` yields `null`, and `\"123\"` / `\"\\\"hi\\\"\"` yield\n * non-objects — every handler that then reads `body.field` throws a TypeError\n * that surfaces as a 500. Call this right after `JSON.parse` and answer 400\n * (\"Request body must be a JSON object\") instead. Arrays are rejected too:\n * `[].field` reads as `undefined` today, which degrades into a misleading\n * \"missing parameter\" 400 — rejecting up front names the real problem.\n */\nexport function isJsonObject(body: unknown): body is Record<string, unknown> {\n return body !== null && typeof body === \"object\" && !Array.isArray(body);\n}\n\n// ─── Embedding helpers ─────────────────────────────────────────────────────\n\nconst DEFAULT_EMBEDDING_DIMENSIONS = 1536;\n\n/**\n * Maximum embedding dimensions accepted by POST /v1/embeddings.\n *\n * A serialization budget, not an allocation bound. The ECMAScript array-length\n * bound (2**32 - 1) is where `new Array(n)` throws RangeError, but the handler\n * does not stop at allocating: `generateDeterministicEmbedding` fills the array\n * and the response is JSON-serialized, so at that bound a single request aborts\n * the process (\"FATAL ERROR: CALL_AND_RETRY_LAST Allocation failed - JavaScript\n * heap out of memory\") before any response is written — taking every other test\n * sharing the server with it. A cap the server cannot serve is not a cap.\n *\n * Derivation, measured on this tree: a response body costs 19.58 bytes per\n * dimension (4096 -> 80,456 B; 100,000 -> 1,958,539 B; 1,000,000 ->\n * 19,583,034 B). At 100,000 the body is ~1.96 MB, built in 7 ms at 83 MB RSS\n * under a 1 GB heap — comfortably serviceable.\n *\n * Deliberately still not a model width: /v1/embeddings also serves Azure and\n * every OpenAI-compatible server routed through COMPAT_SUFFIXES, where\n * 4096-dimension models are ordinary. 100,000 leaves >12x headroom over the\n * widest width in circulation while keeping every accepted request answerable.\n */\nexport const MAX_EMBEDDING_DIMENSIONS = 100_000;\n\n/**\n * Validate an embeddings `dimensions` parameter.\n * Returns the effective dimensions (default 1536) or null when invalid.\n * Valid: undefined/null (→ default), integer in [1, MAX_EMBEDDING_DIMENSIONS].\n */\nexport function validateEmbeddingDimensions(raw: unknown): number | null {\n // null means \"unset\" too — the previous code was `dimensions ?? 1536`.\n if (raw === undefined || raw === null) return 1536;\n if (typeof raw !== \"number\" || !Number.isInteger(raw)) return null;\n if (raw < 1 || raw > MAX_EMBEDDING_DIMENSIONS) return null;\n return raw;\n}\n\n/**\n * Normalize an embeddings `input` into the texts to embed.\n * Accepts every shape EmbeddingCreateParams.input declares\n * (openai/resources/embeddings.d.ts): `string | string[] | number[] | number[][]`,\n * the last two being pre-tokenized input. Returns null for shapes the API\n * rejects (non-string scalars, objects, mixed arrays).\n */\nexport function normalizeEmbeddingInput(raw: unknown): string[] | null {\n if (typeof raw === \"string\") return [raw];\n if (Array.isArray(raw)) {\n if (raw.every((el) => typeof el === \"string\")) return raw as string[];\n // number[] — one pre-tokenized input.\n if (raw.every((el) => typeof el === \"number\")) return [raw.join(\" \")];\n // number[][] — a batch of pre-tokenized inputs.\n if (raw.every((el) => Array.isArray(el) && el.every((t) => typeof t === \"number\"))) {\n return (raw as number[][]).map((tokens) => tokens.join(\" \"));\n }\n return null;\n }\n return null;\n}\n\n/**\n * Normalize a free-text field (moderation input, search/rerank query).\n * Accepts a string, or any array — joined with \" \" as before, so no array that\n * used to return 200 starts failing, and ModerationCreateParams' multimodal\n * parts (openai/resources/moderations.d.ts) contribute their `.text`. Returns\n * null only for non-string, non-array values, which used to 500.\n */\nexport function normalizeTextInput(raw: unknown): string | null {\n if (typeof raw === \"string\") return raw;\n if (Array.isArray(raw)) {\n return raw\n .map((el) => {\n if (typeof el === \"string\") return el;\n if (el === null || el === undefined) return \"\";\n if (typeof el === \"object\") {\n const part = el as { type?: unknown; text?: unknown };\n return part.type === \"text\" && typeof part.text === \"string\" ? part.text : \"\";\n }\n return String(el);\n })\n .join(\" \");\n }\n return null;\n}\n\n/**\n * Generate a deterministic embedding vector from input text.\n * Hashes the input with SHA-256 and spreads the hash bytes across\n * the requested number of dimensions, producing values in [-1, 1].\n */\nexport function generateDeterministicEmbedding(\n input: string,\n dimensions: number = DEFAULT_EMBEDDING_DIMENSIONS,\n): number[] {\n let currentHash = createHash(\"sha256\").update(input).digest();\n const embedding: number[] = new Array(dimensions);\n for (let i = 0; i < dimensions; i++) {\n if (i > 0 && i % 32 === 0) {\n currentHash = createHash(\"sha256\").update(currentHash).digest();\n }\n // Map 0-255 → -1.0 to 1.0\n embedding[i] = currentHash[i % 32] / 127.5 - 1;\n }\n return embedding;\n}\n\nexport interface EmbeddingAPIResponse {\n object: \"list\";\n data: { object: \"embedding\"; index: number; embedding: number[] }[];\n model: string;\n usage: { prompt_tokens: number; total_tokens: number };\n}\n\n/**\n * Build an OpenAI-format embeddings API response for one or more inputs.\n */\nexport function buildEmbeddingResponse(\n embeddings: number[][],\n model: string,\n usage?: { prompt_tokens?: number; total_tokens?: number },\n): EmbeddingAPIResponse {\n return {\n object: \"list\",\n data: embeddings.map((embedding, index) => ({\n object: \"embedding\" as const,\n index,\n embedding,\n })),\n model,\n usage: { prompt_tokens: usage?.prompt_tokens ?? 0, total_tokens: usage?.total_tokens ?? 0 },\n };\n}\n\n/**\n * Build a stable, human-readable identifier for a fixture's match shape, for\n * any log line that has to name WHICH fixture it means. The `Fixture` type\n * carries no `id`/`name`, so the matchers are the only handle a reader has.\n *\n * Used by the relaxed-turnIndex warning (`router.ts`) and by the chaos\n * rejected-value warning (`chaos.ts`); one implementation so the two name the\n * same fixture the same way. The obvious `JSON.stringify(match)` is unfit: it\n * DROPS `predicate` functions (non-serialisable) and serialises any RegExp\n * matcher to `{}`, so a predicate- or regex-gated fixture's warning collapsed to\n * an uninformative \"served fixture {}\" / `{\"userMessage\":{}}` blob.\n *\n * Instead we list the PRESENT matcher keys in declaration order, annotating each\n * by VALUE KIND so predicates and regexes survive: `predicate(fn)`,\n * `userMessage(regex)`, `userMessage(\"hello\")`, `turnIndex=0`, etc. The\n * fixture's array `index` is prefixed as a stable positional identifier when\n * the caller knows it (i.e. `>= 0`); callers holding only the fixture object\n * pass `-1` and get the matcher summary alone. String/number values are shown\n * inline (truncated) so a content match remains recognisable; the whole string\n * is capped to keep the log line bounded.\n */\nconst DESCRIBE_MATCH_MAX = 160;\n\nexport function describeMatch(match: FixtureMatch, index: number): string {\n const parts: string[] = [];\n for (const [key, value] of Object.entries(match)) {\n if (value === undefined) continue;\n if (typeof value === \"function\") {\n parts.push(key === \"predicate\" ? \"[predicate]\" : `${key}[fn]`);\n } else if (value instanceof RegExp) {\n parts.push(`${key}(${value})`);\n } else if (typeof value === \"string\") {\n const v = value.length > 40 ? `${value.slice(0, 40)}…` : value;\n parts.push(`${key}(${JSON.stringify(v)})`);\n } else if (Array.isArray(value)) {\n parts.push(`${key}(${value.length} item${value.length === 1 ? \"\" : \"s\"})`);\n } else if (typeof value === \"object\" && value !== null) {\n // `String(obj)` is \"[object Object]\"; show the (bounded) JSON instead.\n const json = JSON.stringify(value);\n parts.push(`${key}(${json.length > 40 ? `${json.slice(0, 40)}…` : json})`);\n } else {\n parts.push(`${key}=${String(value)}`);\n }\n }\n const keys = parts.length > 0 ? parts.join(\", \") : \"no matchers\";\n const prefix = index >= 0 ? `#${index} ` : \"\";\n const full = `${prefix}{ ${keys} }`;\n if (full.length <= DESCRIBE_MATCH_MAX) return full;\n // Cut at a matcher boundary (never mid-token) and mark the elision; a lone\n // oversized matcher is hard-cut but still marked.\n const head = full.slice(0, DESCRIBE_MATCH_MAX - 4);\n const cut = head.lastIndexOf(\", \");\n return `${cut > 0 ? head.slice(0, cut) : head}, … }`;\n}\n\n// ─── MCP fakes: undeclared-tool override ────────────────────────────────────\n\n/**\n * Parse an `X-AIMock-MCP-Undeclared` header or `?undeclared=` value.\n * Modeled on {@link resolveStrictMode}: case-insensitive, surrounding\n * whitespace trimmed. Returns `null` when unrecognised; unlike the strict\n * header, {@link resolveMcpIdentity} turns that into an error rather than a\n * fall-through, because falling through would turn a mistyped `deny` into\n * the permissive default.\n */\nfunction parseMcpUndeclared(raw: string): McpFakeUndeclaredPolicy | null {\n const val = raw.trim().toLowerCase();\n if (val === \"allow\" || val === \"deny\") return val;\n return null;\n}\n"],"mappings":"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"}
|