@copilotkit/aimock 1.42.1 → 1.44.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 +65 -0
- package/README.md +108 -16
- package/dist/agui-handler.d.ts.map +1 -1
- package/dist/agui-mock.cjs +1 -1
- 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 +1 -1
- package/dist/agui-mock.js.map +1 -1
- package/dist/agui-recorder.cjs +33 -11
- package/dist/agui-recorder.cjs.map +1 -1
- package/dist/agui-recorder.d.cts +10 -1
- package/dist/agui-recorder.d.cts.map +1 -1
- package/dist/agui-recorder.d.ts +10 -1
- package/dist/agui-recorder.d.ts.map +1 -1
- package/dist/agui-recorder.js +33 -11
- package/dist/agui-recorder.js.map +1 -1
- package/dist/agui-types.d.cts +1 -0
- package/dist/agui-types.d.cts.map +1 -1
- package/dist/agui-types.d.ts +1 -0
- package/dist/agui-types.d.ts.map +1 -1
- package/dist/aimock-cli.cjs +38 -10
- package/dist/aimock-cli.cjs.map +1 -1
- package/dist/aimock-cli.d.cts +5 -0
- package/dist/aimock-cli.d.cts.map +1 -1
- package/dist/aimock-cli.d.ts +5 -0
- package/dist/aimock-cli.d.ts.map +1 -1
- package/dist/aimock-cli.js +38 -10
- package/dist/aimock-cli.js.map +1 -1
- package/dist/batches.cjs +458 -0
- package/dist/batches.cjs.map +1 -0
- package/dist/batches.d.cts +57 -0
- package/dist/batches.d.cts.map +1 -0
- package/dist/batches.d.ts +57 -0
- package/dist/batches.d.ts.map +1 -0
- package/dist/batches.js +453 -0
- package/dist/batches.js.map +1 -0
- package/dist/bedrock-converse.cjs +44 -12
- 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 +46 -14
- package/dist/bedrock-converse.js.map +1 -1
- package/dist/bedrock.cjs +10 -8
- 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 +12 -10
- package/dist/bedrock.js.map +1 -1
- package/dist/byteplus-video.cjs +3 -3
- package/dist/byteplus-video.cjs.map +1 -1
- package/dist/byteplus-video.js +4 -4
- package/dist/byteplus-video.js.map +1 -1
- package/dist/chaos.cjs +625 -72
- package/dist/chaos.cjs.map +1 -1
- package/dist/chaos.d.cts +164 -7
- package/dist/chaos.d.cts.map +1 -1
- package/dist/chaos.d.ts +164 -7
- package/dist/chaos.d.ts.map +1 -1
- package/dist/chaos.js +616 -74
- package/dist/chaos.js.map +1 -1
- package/dist/cli.cjs +131 -30
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +132 -31
- package/dist/cli.js.map +1 -1
- package/dist/cohere.cjs +37 -9
- 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 +39 -11
- package/dist/cohere.js.map +1 -1
- package/dist/config-loader.cjs +1 -1
- package/dist/config-loader.js +1 -1
- package/dist/constants.cjs +8 -0
- package/dist/constants.cjs.map +1 -1
- package/dist/constants.d.cts +6 -2
- package/dist/constants.d.ts +6 -2
- package/dist/constants.js +8 -1
- package/dist/constants.js.map +1 -1
- package/dist/convert.cjs +1 -1
- package/dist/convert.cjs.map +1 -1
- package/dist/convert.js +1 -1
- package/dist/convert.js.map +1 -1
- package/dist/echo-text.cjs +65 -0
- package/dist/echo-text.cjs.map +1 -0
- package/dist/echo-text.d.cts +22 -0
- package/dist/echo-text.d.cts.map +1 -0
- package/dist/echo-text.d.ts +22 -0
- package/dist/echo-text.d.ts.map +1 -0
- package/dist/echo-text.js +65 -0
- package/dist/echo-text.js.map +1 -0
- package/dist/elevenlabs-audio.cjs +3 -3
- package/dist/elevenlabs-audio.cjs.map +1 -1
- package/dist/elevenlabs-audio.js +4 -4
- package/dist/elevenlabs-audio.js.map +1 -1
- package/dist/elevenlabs-voice.cjs +1053 -0
- package/dist/elevenlabs-voice.cjs.map +1 -0
- package/dist/elevenlabs-voice.d.cts +72 -0
- package/dist/elevenlabs-voice.d.cts.map +1 -0
- package/dist/elevenlabs-voice.d.ts +72 -0
- package/dist/elevenlabs-voice.d.ts.map +1 -0
- package/dist/elevenlabs-voice.js +1048 -0
- package/dist/elevenlabs-voice.js.map +1 -0
- package/dist/embeddings.cjs +26 -5
- package/dist/embeddings.cjs.map +1 -1
- package/dist/embeddings.d.cts +1 -1
- package/dist/embeddings.d.cts.map +1 -1
- package/dist/embeddings.d.ts +1 -1
- package/dist/embeddings.d.ts.map +1 -1
- package/dist/embeddings.js +27 -6
- package/dist/embeddings.js.map +1 -1
- package/dist/fal-audio.cjs +3 -3
- package/dist/fal-audio.cjs.map +1 -1
- package/dist/fal-audio.js +4 -4
- package/dist/fal-audio.js.map +1 -1
- package/dist/fal.cjs +28 -4
- package/dist/fal.cjs.map +1 -1
- package/dist/fal.d.cts +16 -1
- package/dist/fal.d.cts.map +1 -1
- package/dist/fal.d.ts +16 -1
- package/dist/fal.d.ts.map +1 -1
- package/dist/fal.js +28 -5
- package/dist/fal.js.map +1 -1
- package/dist/files.cjs +1069 -0
- package/dist/files.cjs.map +1 -0
- package/dist/files.d.cts +68 -0
- package/dist/files.d.cts.map +1 -0
- package/dist/files.d.ts +68 -0
- package/dist/files.d.ts.map +1 -0
- package/dist/files.js +1056 -0
- package/dist/files.js.map +1 -0
- package/dist/fine-tuning.cjs +1033 -0
- package/dist/fine-tuning.cjs.map +1 -0
- package/dist/fine-tuning.d.cts +96 -0
- package/dist/fine-tuning.d.cts.map +1 -0
- package/dist/fine-tuning.d.ts +96 -0
- package/dist/fine-tuning.d.ts.map +1 -0
- package/dist/fine-tuning.js +1026 -0
- package/dist/fine-tuning.js.map +1 -0
- package/dist/fixture-loader-services.cjs +116 -0
- package/dist/fixture-loader-services.cjs.map +1 -0
- package/dist/fixture-loader-services.d.cts +48 -0
- package/dist/fixture-loader-services.d.cts.map +1 -0
- package/dist/fixture-loader-services.d.ts +48 -0
- package/dist/fixture-loader-services.d.ts.map +1 -0
- package/dist/fixture-loader-services.js +114 -0
- package/dist/fixture-loader-services.js.map +1 -0
- package/dist/fixture-loader.cjs +0 -0
- package/dist/fixture-loader.cjs.map +1 -1
- package/dist/fixture-loader.d.cts +131 -5
- package/dist/fixture-loader.d.cts.map +1 -1
- package/dist/fixture-loader.d.ts +131 -5
- package/dist/fixture-loader.d.ts.map +1 -1
- package/dist/fixture-loader.js +0 -0
- package/dist/fixture-loader.js.map +1 -1
- package/dist/gemini-embeddings.cjs +44 -4
- package/dist/gemini-embeddings.cjs.map +1 -1
- package/dist/gemini-embeddings.js +46 -6
- package/dist/gemini-embeddings.js.map +1 -1
- package/dist/gemini-interactions.cjs +51 -7
- package/dist/gemini-interactions.cjs.map +1 -1
- package/dist/gemini-interactions.d.cts.map +1 -1
- package/dist/gemini-interactions.d.ts.map +1 -1
- package/dist/gemini-interactions.js +53 -9
- package/dist/gemini-interactions.js.map +1 -1
- package/dist/gemini.cjs +19 -8
- 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 +21 -10
- package/dist/gemini.js.map +1 -1
- package/dist/grok-video.cjs +14 -14
- package/dist/grok-video.cjs.map +1 -1
- package/dist/grok-video.js +15 -15
- package/dist/grok-video.js.map +1 -1
- package/dist/helpers.cjs +497 -10
- package/dist/helpers.cjs.map +1 -1
- package/dist/helpers.d.cts +38 -2
- package/dist/helpers.d.cts.map +1 -1
- package/dist/helpers.d.ts +38 -2
- package/dist/helpers.d.ts.map +1 -1
- package/dist/helpers.js +487 -12
- package/dist/helpers.js.map +1 -1
- package/dist/images.cjs +57 -125
- 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 +58 -126
- package/dist/images.js.map +1 -1
- package/dist/index.cjs +71 -9
- package/dist/index.d.cts +17 -7
- package/dist/index.d.ts +17 -7
- package/dist/index.js +21 -11
- package/dist/jest.cjs +24 -12
- package/dist/jest.cjs.map +1 -1
- package/dist/jest.d.cts +6 -3
- package/dist/jest.d.cts.map +1 -1
- package/dist/jest.d.ts +6 -3
- package/dist/jest.d.ts.map +1 -1
- package/dist/jest.js +25 -13
- package/dist/jest.js.map +1 -1
- package/dist/journal.cjs +61 -4
- package/dist/journal.cjs.map +1 -1
- package/dist/journal.d.cts +60 -3
- package/dist/journal.d.cts.map +1 -1
- package/dist/journal.d.ts +60 -3
- package/dist/journal.d.ts.map +1 -1
- package/dist/journal.js +61 -5
- package/dist/journal.js.map +1 -1
- package/dist/live-fixture.cjs +1192 -0
- package/dist/live-fixture.cjs.map +1 -0
- package/dist/live-fixture.js +1187 -0
- package/dist/live-fixture.js.map +1 -0
- package/dist/live-recorder.cjs +256 -0
- package/dist/live-recorder.cjs.map +1 -0
- package/dist/live-recorder.js +254 -0
- package/dist/live-recorder.js.map +1 -0
- package/dist/live-replay.cjs +286 -0
- package/dist/live-replay.cjs.map +1 -0
- package/dist/live-replay.js +285 -0
- package/dist/live-replay.js.map +1 -0
- package/dist/live-sanitize.cjs +196 -0
- package/dist/live-sanitize.cjs.map +1 -0
- package/dist/live-sanitize.js +196 -0
- package/dist/live-sanitize.js.map +1 -0
- package/dist/live-types.d.cts +71 -0
- package/dist/live-types.d.cts.map +1 -0
- package/dist/live-types.d.ts +71 -0
- package/dist/live-types.d.ts.map +1 -0
- package/dist/llmock.cjs +132 -49
- package/dist/llmock.cjs.map +1 -1
- package/dist/llmock.d.cts +51 -5
- package/dist/llmock.d.cts.map +1 -1
- package/dist/llmock.d.ts +51 -5
- package/dist/llmock.d.ts.map +1 -1
- package/dist/llmock.js +133 -50
- package/dist/llmock.js.map +1 -1
- package/dist/mcp-fakes-mount.cjs +216 -0
- package/dist/mcp-fakes-mount.cjs.map +1 -0
- package/dist/mcp-fakes-mount.js +210 -0
- package/dist/mcp-fakes-mount.js.map +1 -0
- package/dist/mcp-fakes.cjs +1619 -0
- package/dist/mcp-fakes.cjs.map +1 -0
- package/dist/mcp-fakes.d.cts +101 -0
- package/dist/mcp-fakes.d.cts.map +1 -0
- package/dist/mcp-fakes.d.ts +101 -0
- package/dist/mcp-fakes.d.ts.map +1 -0
- package/dist/mcp-fakes.js +1614 -0
- package/dist/mcp-fakes.js.map +1 -0
- package/dist/mcp-handler.cjs +331 -17
- package/dist/mcp-handler.cjs.map +1 -1
- package/dist/mcp-handler.js +330 -18
- package/dist/mcp-handler.js.map +1 -1
- package/dist/mcp-mock.cjs +217 -49
- package/dist/mcp-mock.cjs.map +1 -1
- package/dist/mcp-mock.d.cts +66 -1
- package/dist/mcp-mock.d.cts.map +1 -1
- package/dist/mcp-mock.d.ts +66 -1
- package/dist/mcp-mock.d.ts.map +1 -1
- package/dist/mcp-mock.js +218 -50
- package/dist/mcp-mock.js.map +1 -1
- package/dist/mcp-stub.cjs +5 -1
- package/dist/mcp-stub.d.cts +3 -1
- package/dist/mcp-stub.d.ts +3 -1
- package/dist/mcp-stub.js +3 -1
- package/dist/mcp-types.d.cts +6 -0
- package/dist/mcp-types.d.ts +6 -0
- package/dist/message-text.cjs +192 -0
- package/dist/message-text.cjs.map +1 -0
- package/dist/message-text.d.cts +31 -0
- package/dist/message-text.d.cts.map +1 -0
- package/dist/message-text.d.ts +31 -0
- package/dist/message-text.d.ts.map +1 -0
- package/dist/message-text.js +184 -0
- package/dist/message-text.js.map +1 -0
- package/dist/messages.cjs +49 -7
- 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 +51 -9
- package/dist/messages.js.map +1 -1
- package/dist/metrics.cjs +278 -45
- package/dist/metrics.cjs.map +1 -1
- package/dist/metrics.d.cts +17 -7
- package/dist/metrics.d.cts.map +1 -1
- package/dist/metrics.d.ts +17 -7
- package/dist/metrics.d.ts.map +1 -1
- package/dist/metrics.js +270 -29
- package/dist/metrics.js.map +1 -1
- package/dist/moderation.cjs +10 -1
- package/dist/moderation.cjs.map +1 -1
- package/dist/moderation.d.cts.map +1 -1
- package/dist/moderation.d.ts.map +1 -1
- package/dist/moderation.js +10 -1
- package/dist/moderation.js.map +1 -1
- package/dist/ollama.cjs +10 -6
- 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 +11 -7
- package/dist/ollama.js.map +1 -1
- package/dist/openapi.cjs +3405 -0
- package/dist/openapi.cjs.map +1 -0
- package/dist/openapi.d.cts +30 -0
- package/dist/openapi.d.cts.map +1 -0
- package/dist/openapi.d.ts +30 -0
- package/dist/openapi.d.ts.map +1 -0
- package/dist/openapi.js +3404 -0
- package/dist/openapi.js.map +1 -0
- package/dist/openrouter-video.cjs +9 -9
- 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 +10 -10
- package/dist/openrouter-video.js.map +1 -1
- package/dist/recorder.cjs +54 -10
- 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 +52 -12
- package/dist/recorder.js.map +1 -1
- package/dist/responses.cjs +47 -7
- 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 +48 -9
- package/dist/responses.js.map +1 -1
- package/dist/route-registry.cjs +951 -0
- package/dist/route-registry.cjs.map +1 -0
- package/dist/route-registry.d.cts +33 -0
- package/dist/route-registry.d.cts.map +1 -0
- package/dist/route-registry.d.ts +33 -0
- package/dist/route-registry.d.ts.map +1 -0
- package/dist/route-registry.js +866 -0
- package/dist/route-registry.js.map +1 -0
- package/dist/router.cjs +21 -34
- package/dist/router.cjs.map +1 -1
- package/dist/router.d.cts +4 -1
- package/dist/router.d.cts.map +1 -1
- package/dist/router.d.ts +4 -1
- package/dist/router.d.ts.map +1 -1
- package/dist/router.js +21 -35
- package/dist/router.js.map +1 -1
- package/dist/server.cjs +1252 -777
- package/dist/server.cjs.map +1 -1
- package/dist/server.d.cts +5 -1
- package/dist/server.d.cts.map +1 -1
- package/dist/server.d.ts +5 -1
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +1185 -710
- package/dist/server.js.map +1 -1
- package/dist/speech.cjs +6 -4
- package/dist/speech.cjs.map +1 -1
- package/dist/speech.js +7 -5
- package/dist/speech.js.map +1 -1
- package/dist/suite.cjs +1 -1
- package/dist/suite.js +1 -1
- package/dist/transcription.cjs +6 -6
- 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 +7 -7
- package/dist/transcription.js.map +1 -1
- package/dist/types.cjs +18 -0
- package/dist/types.cjs.map +1 -0
- package/dist/types.d.cts +320 -17
- package/dist/types.d.cts.map +1 -1
- package/dist/types.d.ts +320 -17
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +17 -0
- package/dist/types.js.map +1 -0
- package/dist/validate-cli.cjs +983 -0
- package/dist/validate-cli.cjs.map +1 -0
- package/dist/validate-cli.js +982 -0
- package/dist/validate-cli.js.map +1 -0
- package/dist/vector-stores.cjs +1180 -0
- package/dist/vector-stores.cjs.map +1 -0
- package/dist/vector-stores.d.cts +106 -0
- package/dist/vector-stores.d.cts.map +1 -0
- package/dist/vector-stores.d.ts +106 -0
- package/dist/vector-stores.d.ts.map +1 -0
- package/dist/vector-stores.js +1164 -0
- package/dist/vector-stores.js.map +1 -0
- package/dist/vector-types.d.cts.map +1 -1
- package/dist/vector-types.d.ts.map +1 -1
- package/dist/veo-video.cjs +13 -13
- package/dist/veo-video.cjs.map +1 -1
- package/dist/veo-video.js +14 -14
- package/dist/veo-video.js.map +1 -1
- package/dist/video.cjs +23 -4
- package/dist/video.cjs.map +1 -1
- package/dist/video.d.cts +1 -1
- package/dist/video.d.cts.map +1 -1
- package/dist/video.d.ts +1 -1
- package/dist/video.d.ts.map +1 -1
- package/dist/video.js +24 -5
- package/dist/video.js.map +1 -1
- package/dist/vitest.cjs +23 -11
- package/dist/vitest.cjs.map +1 -1
- package/dist/vitest.d.cts +5 -2
- package/dist/vitest.d.cts.map +1 -1
- package/dist/vitest.d.ts +5 -2
- package/dist/vitest.d.ts.map +1 -1
- package/dist/vitest.js +25 -13
- package/dist/vitest.js.map +1 -1
- package/dist/watcher.cjs +2 -1
- package/dist/watcher.cjs.map +1 -1
- package/dist/watcher.js +2 -1
- package/dist/watcher.js.map +1 -1
- package/dist/ws-framing.cjs +150 -35
- package/dist/ws-framing.cjs.map +1 -1
- package/dist/ws-framing.d.cts +18 -3
- package/dist/ws-framing.d.cts.map +1 -1
- package/dist/ws-framing.d.ts +18 -3
- package/dist/ws-framing.d.ts.map +1 -1
- package/dist/ws-framing.js +151 -36
- package/dist/ws-framing.js.map +1 -1
- package/dist/ws-gemini-live.cjs +2 -2
- package/dist/ws-gemini-live.js +2 -2
- package/dist/ws-live.cjs +401 -0
- package/dist/ws-live.cjs.map +1 -0
- package/dist/ws-live.js +400 -0
- package/dist/ws-live.js.map +1 -0
- package/dist/ws-realtime.cjs +46 -2
- 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 +46 -2
- package/dist/ws-realtime.js.map +1 -1
- package/dist/ws-responses.cjs +16 -4
- package/dist/ws-responses.cjs.map +1 -1
- package/dist/ws-responses.js +16 -4
- package/dist/ws-responses.js.map +1 -1
- package/dist/ws-upstream.cjs +118 -0
- package/dist/ws-upstream.cjs.map +1 -0
- package/dist/ws-upstream.js +115 -0
- package/dist/ws-upstream.js.map +1 -0
- package/fixtures/openai-live-client.json +390 -0
- package/fixtures/openai-live-managed.json +709 -0
- package/package.json +4 -1
package/dist/helpers.cjs
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
const require_runtime = require('./_virtual/_rolldown/runtime.cjs');
|
|
2
2
|
const require_constants = require('./constants.cjs');
|
|
3
|
+
const require_echo_text = require('./echo-text.cjs');
|
|
4
|
+
const require_message_text = require('./message-text.cjs');
|
|
3
5
|
const require_model_utils = require('./model-utils.cjs');
|
|
4
6
|
const require_api_key_auth = require('./api-key-auth.cjs');
|
|
5
7
|
let node_crypto = require("node:crypto");
|
|
@@ -26,6 +28,37 @@ function resolveStrictMode(serverDefault, rawHeaders) {
|
|
|
26
28
|
}
|
|
27
29
|
return serverDefault ?? false;
|
|
28
30
|
}
|
|
31
|
+
const STRICT_INTEGER_RE = /^\d+$/;
|
|
32
|
+
/**
|
|
33
|
+
* Strict decimal-integer grammar shared by the query/list parsers that grew
|
|
34
|
+
* their own local copy on separate branches (files `limit`, fine-tuning
|
|
35
|
+
* `limit`, chaos integer fields): an unsigned digit run and nothing else — no
|
|
36
|
+
* sign, no exponent, no radix prefix, no surrounding whitespace (callers that
|
|
37
|
+
* accept padded input trim before calling).
|
|
38
|
+
*
|
|
39
|
+
* Deliberately narrower than `Number()`, which reads `0x10` as 16, `1e3` as
|
|
40
|
+
* 1000 and strips whitespace and a leading `+`. Returns the value, or `null`
|
|
41
|
+
* when the text is not a safe integer literal. Range checks and error
|
|
42
|
+
* messages stay with the callers — this is the grammar gate only, so sharing
|
|
43
|
+
* it cannot change any surface's accepted range or its 400 text.
|
|
44
|
+
*/
|
|
45
|
+
function parseStrictIntegerText(text) {
|
|
46
|
+
if (!STRICT_INTEGER_RE.test(text)) return null;
|
|
47
|
+
const n = Number(text);
|
|
48
|
+
return Number.isSafeInteger(n) ? n : null;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Would a fixture miss on this request have been forwarded upstream? This is
|
|
52
|
+
* the ONE rule behind the no-fixture chaos journal `source` label: "proxy"
|
|
53
|
+
* only when record mode has an upstream for THIS provider and strict mode is
|
|
54
|
+
* not refusing the miss first. `effectiveStrict` is the header-resolved value
|
|
55
|
+
* from {@link resolveStrictMode}. `record.providers` is read guarded — a JS
|
|
56
|
+
* caller can hand over `record: {}`.
|
|
57
|
+
*/
|
|
58
|
+
function wouldProxyMiss(effectiveStrict, record, providerKey) {
|
|
59
|
+
if (effectiveStrict || providerKey === void 0) return false;
|
|
60
|
+
return Boolean(record?.providers?.[providerKey]);
|
|
61
|
+
}
|
|
29
62
|
/**
|
|
30
63
|
* Returns `true` or `false` when the X-AIMock-Strict header overrides the
|
|
31
64
|
* server default, or `undefined` when it doesn't. Designed to be spread
|
|
@@ -143,6 +176,10 @@ function flattenHeaders(headers) {
|
|
|
143
176
|
}
|
|
144
177
|
return flat;
|
|
145
178
|
}
|
|
179
|
+
/** Structural discriminator; registration performs full transcript validation. */
|
|
180
|
+
function isLiveResponse(value) {
|
|
181
|
+
return value !== null && typeof value === "object" && !Array.isArray(value) && "live" in value;
|
|
182
|
+
}
|
|
146
183
|
function isResponseFactory(r) {
|
|
147
184
|
return typeof r === "function";
|
|
148
185
|
}
|
|
@@ -177,6 +214,57 @@ function normalizeFactoryResponse(raw) {
|
|
|
177
214
|
function generateId(prefix = "chatcmpl") {
|
|
178
215
|
return `${prefix}-${(0, node_crypto.randomBytes)(12).toString("base64url")}`;
|
|
179
216
|
}
|
|
217
|
+
/**
|
|
218
|
+
* Resolve the request id for this HTTP request.
|
|
219
|
+
*
|
|
220
|
+
* - When the caller sends a well-formed `X-Request-Id` (1-128 chars of
|
|
221
|
+
* `A-Za-z0-9-_.:`), it is echoed verbatim so distributed traces correlate.
|
|
222
|
+
* - Otherwise (absent, empty, too long, or illegal characters) a fresh
|
|
223
|
+
* `req-…` id is generated — never trust an attacker-controlled correlation
|
|
224
|
+
* value to be a valid log key.
|
|
225
|
+
*
|
|
226
|
+
* Returns `{ id, generated }`. The server normalizes
|
|
227
|
+
* `req.headers["x-request-id"]` to the resolved value, so every downstream
|
|
228
|
+
* `flattenHeaders` journal snapshot carries it with zero per-handler edits —
|
|
229
|
+
* which also means a MINTED id would otherwise ride the egress header set to
|
|
230
|
+
* a real provider. The server therefore passes `generated` to
|
|
231
|
+
* `markMintedRequestId` so `buildForwardHeaders` can drop it; a caller's own
|
|
232
|
+
* id still forwards verbatim.
|
|
233
|
+
*/
|
|
234
|
+
function resolveRequestId(rawHeaders) {
|
|
235
|
+
const raw = rawHeaders["x-request-id"];
|
|
236
|
+
const first = Array.isArray(raw) ? raw[0] : raw;
|
|
237
|
+
if (typeof first === "string") {
|
|
238
|
+
const trimmed = first.trim();
|
|
239
|
+
if (trimmed.length >= 1 && trimmed.length <= 128 && /^[A-Za-z0-9\-_.:]+$/.test(trimmed)) return {
|
|
240
|
+
id: trimmed,
|
|
241
|
+
generated: false
|
|
242
|
+
};
|
|
243
|
+
}
|
|
244
|
+
return {
|
|
245
|
+
id: generateId("req"),
|
|
246
|
+
generated: true
|
|
247
|
+
};
|
|
248
|
+
}
|
|
249
|
+
/**
|
|
250
|
+
* Requests whose `x-request-id` aimock MINTED (the caller sent none, or sent
|
|
251
|
+
* an unusable one). The normalized header is indistinguishable from a
|
|
252
|
+
* caller-supplied one by inspection, so the provenance is tracked out-of-band
|
|
253
|
+
* and keyed on the request object, exactly like the egress auth marker.
|
|
254
|
+
*/
|
|
255
|
+
const mintedRequestIds = /* @__PURE__ */ new WeakSet();
|
|
256
|
+
/** @internal Record that this request's `x-request-id` is aimock's own. */
|
|
257
|
+
function markMintedRequestId(req, generated) {
|
|
258
|
+
if (generated) mintedRequestIds.add(req);
|
|
259
|
+
}
|
|
260
|
+
/**
|
|
261
|
+
* @internal True when `x-request-id` on this request was minted by aimock.
|
|
262
|
+
* Egress paths use it to avoid transmitting an invented correlation id to a
|
|
263
|
+
* real provider.
|
|
264
|
+
*/
|
|
265
|
+
function hasMintedRequestId(req) {
|
|
266
|
+
return mintedRequestIds.has(req);
|
|
267
|
+
}
|
|
180
268
|
function generateToolCallId() {
|
|
181
269
|
return `call_${(0, node_crypto.randomBytes)(12).toString("base64url")}`;
|
|
182
270
|
}
|
|
@@ -255,6 +343,24 @@ function resolveFixtureBlocks(blocks) {
|
|
|
255
343
|
} else throw new Error(`Invalid fixture block at index ${i}: unknown type ${JSON.stringify(b.type)} (expected "text" or "toolCall")`);
|
|
256
344
|
});
|
|
257
345
|
}
|
|
346
|
+
/** Project normalized ordered blocks without changing the fixture or generating ids. */
|
|
347
|
+
function resolveFixtureBlockOutcome(blocks) {
|
|
348
|
+
const ordered = resolveFixtureBlocks(blocks);
|
|
349
|
+
let content = "";
|
|
350
|
+
const toolCalls = [];
|
|
351
|
+
for (const block of ordered) if (block.type === "text") content += block.text;
|
|
352
|
+
else toolCalls.push({
|
|
353
|
+
name: block.name,
|
|
354
|
+
arguments: block.arguments,
|
|
355
|
+
...block.id !== void 0 ? { id: block.id } : {}
|
|
356
|
+
});
|
|
357
|
+
return {
|
|
358
|
+
ordered,
|
|
359
|
+
content,
|
|
360
|
+
toolCalls,
|
|
361
|
+
hasToolCalls: toolCalls.length > 0
|
|
362
|
+
};
|
|
363
|
+
}
|
|
258
364
|
function isErrorResponse(r) {
|
|
259
365
|
return "error" in r && r.error !== null && typeof r.error === "object" && "message" in r.error && typeof r.error.message === "string";
|
|
260
366
|
}
|
|
@@ -604,7 +710,8 @@ function buildContentWithToolCallsChunks(content, toolCalls, model, chunkSize, r
|
|
|
604
710
|
const chunks = [];
|
|
605
711
|
const fingerprint = overrides?.systemFingerprint;
|
|
606
712
|
if (blocks && blocks.length > 0) {
|
|
607
|
-
const
|
|
713
|
+
const outcome = resolveFixtureBlockOutcome(blocks);
|
|
714
|
+
const ordered = outcome.ordered;
|
|
608
715
|
if (reasoning) for (let i = 0; i < reasoning.length; i += chunkSize) {
|
|
609
716
|
const slice = reasoning.slice(i, i + chunkSize);
|
|
610
717
|
chunks.push({
|
|
@@ -711,7 +818,7 @@ function buildContentWithToolCallsChunks(content, toolCalls, model, chunkSize, r
|
|
|
711
818
|
index: 0,
|
|
712
819
|
delta: {},
|
|
713
820
|
logprobs: null,
|
|
714
|
-
finish_reason: overrides?.finishReason ?? "tool_calls"
|
|
821
|
+
finish_reason: overrides?.finishReason ?? (outcome.hasToolCalls ? "tool_calls" : "stop")
|
|
715
822
|
}],
|
|
716
823
|
...fingerprint !== void 0 && { system_fingerprint: fingerprint }
|
|
717
824
|
});
|
|
@@ -860,7 +967,31 @@ function buildContentWithToolCallsCompletion(content, toolCalls, model, reasonin
|
|
|
860
967
|
};
|
|
861
968
|
}
|
|
862
969
|
const DEFAULT_MAX_BODY_BYTES = 10 * 1024 * 1024;
|
|
863
|
-
|
|
970
|
+
/**
|
|
971
|
+
* A body read that stopped because the CLIENT sent more bytes than the route
|
|
972
|
+
* allows. The socket is already destroyed when this rejects, so no status line
|
|
973
|
+
* ever reaches the caller — but the fault is the caller's, and error arms that
|
|
974
|
+
* journal and log the failure need to say so. Carrying the classification on
|
|
975
|
+
* the error type (rather than re-matching the message text) is what lets them:
|
|
976
|
+
* the message is unchanged from the plain `Error` this replaced.
|
|
977
|
+
*/
|
|
978
|
+
var RequestBodyTooLargeError = class extends Error {
|
|
979
|
+
constructor(maxBytes) {
|
|
980
|
+
super(`Request body exceeded size limit of ${maxBytes} bytes`);
|
|
981
|
+
this.maxBytes = maxBytes;
|
|
982
|
+
this.name = "RequestBodyTooLargeError";
|
|
983
|
+
}
|
|
984
|
+
};
|
|
985
|
+
/**
|
|
986
|
+
* Read a request body as raw bytes, preserving every octet.
|
|
987
|
+
*
|
|
988
|
+
* This is the byte-level primitive {@link readBody} is built on: routes that
|
|
989
|
+
* must not lose bytes (binary file uploads) take the Buffer, everything else
|
|
990
|
+
* keeps taking the decoded string. Splitting it this way leaves the text path
|
|
991
|
+
* byte-identical — `readBody` performs exactly the same `Buffer.concat(...)`
|
|
992
|
+
* + default (utf8) `toString()` it always did, just one call later.
|
|
993
|
+
*/
|
|
994
|
+
function readBodyBuffer(req, maxBytes = DEFAULT_MAX_BODY_BYTES) {
|
|
864
995
|
return new Promise((resolve, reject) => {
|
|
865
996
|
const chunks = [];
|
|
866
997
|
let totalBytes = 0;
|
|
@@ -871,7 +1002,7 @@ function readBody(req, maxBytes = DEFAULT_MAX_BODY_BYTES) {
|
|
|
871
1002
|
if (totalBytes > maxBytes) {
|
|
872
1003
|
settled = true;
|
|
873
1004
|
req.destroy();
|
|
874
|
-
reject(
|
|
1005
|
+
reject(new RequestBodyTooLargeError(maxBytes));
|
|
875
1006
|
return;
|
|
876
1007
|
}
|
|
877
1008
|
chunks.push(chunk);
|
|
@@ -879,8 +1010,58 @@ function readBody(req, maxBytes = DEFAULT_MAX_BODY_BYTES) {
|
|
|
879
1010
|
req.on("end", () => {
|
|
880
1011
|
if (!settled) {
|
|
881
1012
|
settled = true;
|
|
882
|
-
resolve(Buffer.concat(chunks)
|
|
1013
|
+
resolve(Buffer.concat(chunks));
|
|
1014
|
+
}
|
|
1015
|
+
});
|
|
1016
|
+
req.on("error", (err) => {
|
|
1017
|
+
if (!settled) {
|
|
1018
|
+
settled = true;
|
|
1019
|
+
reject(err);
|
|
1020
|
+
}
|
|
1021
|
+
});
|
|
1022
|
+
});
|
|
1023
|
+
}
|
|
1024
|
+
/**
|
|
1025
|
+
* Read a request body that may legitimately be over-size, without either
|
|
1026
|
+
* buffering it all or dropping the socket.
|
|
1027
|
+
*
|
|
1028
|
+
* {@link readBodyBuffer} answers an over-size body with `req.destroy()`: no
|
|
1029
|
+
* status line, no body, no CORS headers. That is the right default for routes
|
|
1030
|
+
* whose limit is a pure DoS bound, but it is the wrong answer for a route that
|
|
1031
|
+
* owes the caller a `400` describing what was wrong — the caller cannot read an
|
|
1032
|
+
* error it never receives. So this variant splits the two concerns:
|
|
1033
|
+
*
|
|
1034
|
+
* - `maxBytes` bounds MEMORY. Past it the buffered chunks are dropped and the
|
|
1035
|
+
* body is only counted, so peak retention never exceeds `maxBytes` no matter
|
|
1036
|
+
* how large the body is. The caller gets `buffer: null` and answers itself.
|
|
1037
|
+
* - `drainMaxBytes` bounds WORK, and is the only socket drop here: a body that
|
|
1038
|
+
* keeps coming past it is not worth draining and rejects as `readBodyBuffer`
|
|
1039
|
+
* would.
|
|
1040
|
+
*/
|
|
1041
|
+
function readBodyBufferBounded(req, maxBytes, drainMaxBytes) {
|
|
1042
|
+
return new Promise((resolve, reject) => {
|
|
1043
|
+
let chunks = [];
|
|
1044
|
+
let totalBytes = 0;
|
|
1045
|
+
let settled = false;
|
|
1046
|
+
req.on("data", (chunk) => {
|
|
1047
|
+
if (settled) return;
|
|
1048
|
+
totalBytes += chunk.length;
|
|
1049
|
+
if (chunks !== null && totalBytes > maxBytes) chunks = null;
|
|
1050
|
+
if (totalBytes > drainMaxBytes) {
|
|
1051
|
+
settled = true;
|
|
1052
|
+
req.destroy();
|
|
1053
|
+
reject(new RequestBodyTooLargeError(drainMaxBytes));
|
|
1054
|
+
return;
|
|
883
1055
|
}
|
|
1056
|
+
if (chunks !== null) chunks.push(chunk);
|
|
1057
|
+
});
|
|
1058
|
+
req.on("end", () => {
|
|
1059
|
+
if (settled) return;
|
|
1060
|
+
settled = true;
|
|
1061
|
+
resolve({
|
|
1062
|
+
buffer: chunks === null ? null : Buffer.concat(chunks),
|
|
1063
|
+
bytesRead: totalBytes
|
|
1064
|
+
});
|
|
884
1065
|
});
|
|
885
1066
|
req.on("error", (err) => {
|
|
886
1067
|
if (!settled) {
|
|
@@ -890,6 +1071,9 @@ function readBody(req, maxBytes = DEFAULT_MAX_BODY_BYTES) {
|
|
|
890
1071
|
});
|
|
891
1072
|
});
|
|
892
1073
|
}
|
|
1074
|
+
function readBody(req, maxBytes = DEFAULT_MAX_BODY_BYTES) {
|
|
1075
|
+
return readBodyBuffer(req, maxBytes).then((buf) => buf.toString());
|
|
1076
|
+
}
|
|
893
1077
|
/**
|
|
894
1078
|
* Case-insensitive substring/regex match used for search, rerank, and
|
|
895
1079
|
* moderation endpoints where exact casing rarely matters. String patterns
|
|
@@ -908,11 +1092,14 @@ function matchesPattern(text, pattern) {
|
|
|
908
1092
|
return result;
|
|
909
1093
|
}
|
|
910
1094
|
/**
|
|
911
|
-
*
|
|
912
|
-
*
|
|
913
|
-
* `
|
|
914
|
-
*
|
|
915
|
-
*
|
|
1095
|
+
* Which test LLM and other HTTP-route traffic belongs to: the `X-Test-Id`
|
|
1096
|
+
* header (raw, not percent-decoded) wins, then `?testId=` in the query string
|
|
1097
|
+
* (read through `URLSearchParams`, so percent-decoded and with `+` as a
|
|
1098
|
+
* space), then `DEFAULT_TEST_ID`. Every per-test axis of those routes
|
|
1099
|
+
* (fixture match-counts, the journal filter, chaos scoping) resolves through
|
|
1100
|
+
* this. MCP mounts resolve the test id with {@link resolveMcpIdentity}
|
|
1101
|
+
* instead, which percent-decodes the header, so `X-Test-Id: a%20b` is `a%20b`
|
|
1102
|
+
* here and `a b` there. Both read a `+` in the query as a space.
|
|
916
1103
|
*/
|
|
917
1104
|
function resolveTestId(headers, url) {
|
|
918
1105
|
const headerValue = headers["x-test-id"];
|
|
@@ -936,6 +1123,237 @@ function getContext(req) {
|
|
|
936
1123
|
} else if (typeof headerValue === "string" && headerValue) return headerValue;
|
|
937
1124
|
}
|
|
938
1125
|
/**
|
|
1126
|
+
* Decode one MCP identity value: an `X-Test-Id` / `X-AIMock-Context` header
|
|
1127
|
+
* value, and also each query name and (through {@link decodeMcpQueryValue})
|
|
1128
|
+
* each `?testId=` / `?context=` / `?undeclared=` query value. Clients send `encodeURIComponent(id)` because
|
|
1129
|
+
* `fetch` rejects characters such as `›` in a header. A value that is not
|
|
1130
|
+
* valid percent-encoding (a raw `applies 50% discount`) is used as-is, with
|
|
1131
|
+
* `fellBack: true` so the caller can log the fallback. Only that `URIError`
|
|
1132
|
+
* is caught; any other throw propagates. MCP requests only: LLM routes read
|
|
1133
|
+
* the header raw and the query through `URLSearchParams` (see
|
|
1134
|
+
* {@link resolveTestId}).
|
|
1135
|
+
*/
|
|
1136
|
+
function decodeMcpHeaderValue(raw) {
|
|
1137
|
+
try {
|
|
1138
|
+
return {
|
|
1139
|
+
value: decodeURIComponent(raw),
|
|
1140
|
+
fellBack: false
|
|
1141
|
+
};
|
|
1142
|
+
} catch (err) {
|
|
1143
|
+
if (!(err instanceof URIError)) throw err;
|
|
1144
|
+
return {
|
|
1145
|
+
value: raw,
|
|
1146
|
+
fellBack: true
|
|
1147
|
+
};
|
|
1148
|
+
}
|
|
1149
|
+
}
|
|
1150
|
+
/**
|
|
1151
|
+
* Decode one MCP query value (`?testId=`, `?context=`, `?undeclared=`) the way
|
|
1152
|
+
* `URLSearchParams` reads it (spec I1): a `+` is a space, then the value is
|
|
1153
|
+
* percent-decoded by the {@link decodeMcpHeaderValue} rule. So a query built
|
|
1154
|
+
* with `URL.searchParams.set(...)` (a space sent as `+`) and one built with
|
|
1155
|
+
* `encodeURIComponent` both resolve to the id itself. A value that is not
|
|
1156
|
+
* valid percent-encoding is used as-is (its `+` still a space), with
|
|
1157
|
+
* `fellBack: true`.
|
|
1158
|
+
*/
|
|
1159
|
+
function decodeMcpQueryValue(raw) {
|
|
1160
|
+
return decodeMcpHeaderValue(raw.replace(/\+/g, " "));
|
|
1161
|
+
}
|
|
1162
|
+
/**
|
|
1163
|
+
* Every value of the header `name`, one per header line: `[]` when absent.
|
|
1164
|
+
* Node joins a repeated header into one comma-joined `req.headers` string, so
|
|
1165
|
+
* the values come from `req.headersDistinct` (one element per line) when the
|
|
1166
|
+
* request has it. A caller without it (a hand-built request) is read from
|
|
1167
|
+
* `req.headers`: one element for a string, each element for an array.
|
|
1168
|
+
*/
|
|
1169
|
+
function headerValues(req, name) {
|
|
1170
|
+
const distinct = req.headersDistinct?.[name];
|
|
1171
|
+
if (distinct !== void 0) return distinct;
|
|
1172
|
+
const value = req.headers[name];
|
|
1173
|
+
if (value === void 0) return [];
|
|
1174
|
+
return Array.isArray(value) ? value : [value];
|
|
1175
|
+
}
|
|
1176
|
+
/**
|
|
1177
|
+
* Every pair of a query string, in order, each name decoded by the
|
|
1178
|
+
* `decodeMcpHeaderValue` rule. A bare `name` with no `=` counts as an
|
|
1179
|
+
* occurrence with an empty value, so `?testId&testId=t1` has two. Each value
|
|
1180
|
+
* is left raw for the caller to decode with {@link decodeMcpQueryValue}.
|
|
1181
|
+
* A name that is not valid percent-encoding (`test%Id`) gets `name: null`: it
|
|
1182
|
+
* matches no field, and the caller counts it.
|
|
1183
|
+
*/
|
|
1184
|
+
function queryPairs(query) {
|
|
1185
|
+
if (query === "") return [];
|
|
1186
|
+
return query.split("&").map((pair) => {
|
|
1187
|
+
const eq = pair.indexOf("=");
|
|
1188
|
+
const key = decodeMcpHeaderValue(eq === -1 ? pair : pair.slice(0, eq));
|
|
1189
|
+
return {
|
|
1190
|
+
name: key.fellBack ? null : key.value,
|
|
1191
|
+
value: eq === -1 ? "" : pair.slice(eq + 1)
|
|
1192
|
+
};
|
|
1193
|
+
});
|
|
1194
|
+
}
|
|
1195
|
+
/**
|
|
1196
|
+
* A request value as a message part, the way the message builder quotes every
|
|
1197
|
+
* user string: JSON-quoted (quotes and control characters escaped), then cut
|
|
1198
|
+
* once as JSON text, so the whole echo, quotes, escapes and the
|
|
1199
|
+
* `… (<n> more chars)` count included, fits in `MCP_FAKES_ECHO_LIMIT` and the
|
|
1200
|
+
* cut never splits a JSON escape or a surrogate pair.
|
|
1201
|
+
*/
|
|
1202
|
+
function echoIdentityValue(value) {
|
|
1203
|
+
return require_message_text.quote(value, require_constants.MCP_FAKES_ECHO_LIMIT);
|
|
1204
|
+
}
|
|
1205
|
+
const MCP_IDENTITY_INPUTS = {
|
|
1206
|
+
testId: {
|
|
1207
|
+
header: "x-test-id",
|
|
1208
|
+
query: "testId",
|
|
1209
|
+
label: "X-Test-Id"
|
|
1210
|
+
},
|
|
1211
|
+
context: {
|
|
1212
|
+
header: "x-aimock-context",
|
|
1213
|
+
query: "context",
|
|
1214
|
+
label: "X-AIMock-Context"
|
|
1215
|
+
},
|
|
1216
|
+
undeclared: {
|
|
1217
|
+
header: "x-aimock-mcp-undeclared",
|
|
1218
|
+
query: "undeclared",
|
|
1219
|
+
label: "X-AIMock-MCP-Undeclared"
|
|
1220
|
+
}
|
|
1221
|
+
};
|
|
1222
|
+
/**
|
|
1223
|
+
* Resolve the identity (test id, context, undeclared override) of one MCP request.
|
|
1224
|
+
* Each field resolves on its own, first of: the header, the query parameter,
|
|
1225
|
+
* the session value bound at `initialize`. A field sent more than once on
|
|
1226
|
+
* either path (more than one header line, read from `headersDistinct` because
|
|
1227
|
+
* Node joins repeated lines into one `headers` string; a header array from a
|
|
1228
|
+
* hand-built request; a repeated query name) is an error
|
|
1229
|
+
* (`MCP_DUPLICATE_IDENTITY`), even when the other path also carries it. For
|
|
1230
|
+
* the test id and the context, a header value is decoded with
|
|
1231
|
+
* `decodeURIComponent` (raw value on failure, with `fellBack` set) and a query
|
|
1232
|
+
* value the way `URLSearchParams` reads it (spec I1): a `+` is a space, then
|
|
1233
|
+
* the same `decodeURIComponent` rule. So `encodeURIComponent` output (what
|
|
1234
|
+
* clients send) and a query built with `URL.searchParams` both decode to the
|
|
1235
|
+
* id itself, while a raw `+` is a `+` in the header and a space in the query.
|
|
1236
|
+
* When both paths carry a test id or a context, the header is used and the
|
|
1237
|
+
* query value is ignored, even if the two differ, but an ignored query value
|
|
1238
|
+
* that does not decode still sets `fellBack`. The undeclared header is not
|
|
1239
|
+
* percent-decoded; its query value is, by the same query rule. Both are
|
|
1240
|
+
* trimmed and compared case-insensitively. `supplied` says whether the
|
|
1241
|
+
* request itself carried a non-empty header or query value for the field,
|
|
1242
|
+
* never `resolveTestId`'s result (which returns `"__default__"` when nothing
|
|
1243
|
+
* is sent). Blank values: an empty value is not supplied and falls through;
|
|
1244
|
+
* it is never rejected. A whitespace-only value is not supplied for the
|
|
1245
|
+
* undeclared override (it is trimmed), but is a real value for the test id
|
|
1246
|
+
* and the context, which match exactly and are never trimmed (Node trims a
|
|
1247
|
+
* header value, so only the query or a hand-built request can carry one). A
|
|
1248
|
+
* lone query name with no `=` carries no value. An empty session value counts
|
|
1249
|
+
* as none, and a missing `req.url` as `/`. A field with no value anywhere is
|
|
1250
|
+
* `null`. An unrecognized undeclared override on the header or the query is
|
|
1251
|
+
* an error (`ok: false`), not a fall-through: both are checked before the
|
|
1252
|
+
* header one is used, so a valid header does not hide a mistyped query value.
|
|
1253
|
+
* Bad percent-encoding is never silent: a test id or context value that does
|
|
1254
|
+
* not decode sets `fellBack`, on either path, used or ignored; an undeclared
|
|
1255
|
+
* query value that does not decode is that error, with the message saying
|
|
1256
|
+
* so; and a query name that does not decode matches no field and is counted
|
|
1257
|
+
* in `undecodedQueryNames`. `resolveTestId` / `getContext` stay unchanged for
|
|
1258
|
+
* LLM routes.
|
|
1259
|
+
*/
|
|
1260
|
+
function resolveMcpIdentity(req, session) {
|
|
1261
|
+
const url = req.url ?? "/";
|
|
1262
|
+
const qIdx = url.indexOf("?");
|
|
1263
|
+
const pairs = queryPairs(qIdx === -1 ? "" : url.slice(qIdx + 1));
|
|
1264
|
+
const raw = {};
|
|
1265
|
+
for (const field of [
|
|
1266
|
+
"testId",
|
|
1267
|
+
"context",
|
|
1268
|
+
"undeclared"
|
|
1269
|
+
]) {
|
|
1270
|
+
const input = MCP_IDENTITY_INPUTS[field];
|
|
1271
|
+
const sent = [["header", headerValues(req, input.header)], ["query", pairs.filter((p) => p.name === input.query).map((p) => p.value)]];
|
|
1272
|
+
for (const [source, values] of sent) if (values.length > 1) {
|
|
1273
|
+
const label = source === "header" ? require_message_text.msg`${require_message_text.fixed(input.label)} header` : require_message_text.msg`?${require_message_text.fixed(input.query)}= query parameter`;
|
|
1274
|
+
return {
|
|
1275
|
+
ok: false,
|
|
1276
|
+
error: {
|
|
1277
|
+
code: "MCP_DUPLICATE_IDENTITY",
|
|
1278
|
+
status: 400,
|
|
1279
|
+
field,
|
|
1280
|
+
source,
|
|
1281
|
+
count: values.length,
|
|
1282
|
+
message: require_message_text.build(require_message_text.msg`Duplicate ${label}: ${values.length} values sent, expected one`)
|
|
1283
|
+
}
|
|
1284
|
+
};
|
|
1285
|
+
}
|
|
1286
|
+
raw[field] = {
|
|
1287
|
+
header: sent[0][1][0],
|
|
1288
|
+
query: sent[1][1][0]
|
|
1289
|
+
};
|
|
1290
|
+
}
|
|
1291
|
+
const fromRequest = (field) => {
|
|
1292
|
+
const { header, query: q } = raw[field];
|
|
1293
|
+
const fromQuery = q ? decodeMcpQueryValue(q) : null;
|
|
1294
|
+
if (header) {
|
|
1295
|
+
const used = decodeMcpHeaderValue(header);
|
|
1296
|
+
return {
|
|
1297
|
+
value: used.value,
|
|
1298
|
+
fellBack: used.fellBack || (fromQuery?.fellBack ?? false)
|
|
1299
|
+
};
|
|
1300
|
+
}
|
|
1301
|
+
return fromQuery ?? {
|
|
1302
|
+
value: null,
|
|
1303
|
+
fellBack: false
|
|
1304
|
+
};
|
|
1305
|
+
};
|
|
1306
|
+
const testId = fromRequest("testId");
|
|
1307
|
+
const context = fromRequest("context");
|
|
1308
|
+
let undeclared = null;
|
|
1309
|
+
for (const source of ["header", "query"]) {
|
|
1310
|
+
const sent = raw.undeclared[source];
|
|
1311
|
+
if (sent === void 0) continue;
|
|
1312
|
+
const decoded = source === "header" ? {
|
|
1313
|
+
value: sent,
|
|
1314
|
+
fellBack: false
|
|
1315
|
+
} : decodeMcpQueryValue(sent);
|
|
1316
|
+
if (decoded.value.trim() === "") continue;
|
|
1317
|
+
const parsed = parseMcpUndeclared(decoded.value);
|
|
1318
|
+
if (parsed === null) {
|
|
1319
|
+
const echoed = echoIdentityValue(sent);
|
|
1320
|
+
const label = source === "header" ? require_message_text.msg`X-AIMock-MCP-Undeclared header` : require_message_text.msg`?undeclared= query parameter`;
|
|
1321
|
+
const note = decoded.fellBack ? require_message_text.msg` (not valid percent-encoding)` : require_message_text.msg``;
|
|
1322
|
+
return {
|
|
1323
|
+
ok: false,
|
|
1324
|
+
error: {
|
|
1325
|
+
code: "MCP_INVALID_UNDECLARED",
|
|
1326
|
+
status: 400,
|
|
1327
|
+
field: "undeclared",
|
|
1328
|
+
source,
|
|
1329
|
+
raw: require_echo_text.cutText(sent),
|
|
1330
|
+
message: require_message_text.build(require_message_text.msg`Invalid ${label} value ${echoed}${note}: expected allow or deny`)
|
|
1331
|
+
}
|
|
1332
|
+
};
|
|
1333
|
+
}
|
|
1334
|
+
undeclared ??= parsed;
|
|
1335
|
+
}
|
|
1336
|
+
const undecodedQueryNames = pairs.filter((p) => p.name === null).length;
|
|
1337
|
+
return {
|
|
1338
|
+
ok: true,
|
|
1339
|
+
identity: {
|
|
1340
|
+
testId: testId.value ?? (session?.testId || null),
|
|
1341
|
+
context: context.value ?? (session?.context || null),
|
|
1342
|
+
undeclared: undeclared ?? session?.undeclared ?? null
|
|
1343
|
+
},
|
|
1344
|
+
supplied: {
|
|
1345
|
+
testId: testId.value !== null,
|
|
1346
|
+
context: context.value !== null,
|
|
1347
|
+
undeclared: undeclared !== null
|
|
1348
|
+
},
|
|
1349
|
+
fellBack: {
|
|
1350
|
+
testId: testId.fellBack,
|
|
1351
|
+
context: context.fellBack
|
|
1352
|
+
},
|
|
1353
|
+
...undecodedQueryNames > 0 ? { undecodedQueryNames } : {}
|
|
1354
|
+
};
|
|
1355
|
+
}
|
|
1356
|
+
/**
|
|
939
1357
|
* Convert a test ID (e.g. Playwright titlePath) into a filesystem-safe slug
|
|
940
1358
|
* suitable for use as a directory name in snapshot-style recording.
|
|
941
1359
|
*/
|
|
@@ -1122,10 +1540,68 @@ function buildEmbeddingResponse(embeddings, model, usage) {
|
|
|
1122
1540
|
}
|
|
1123
1541
|
};
|
|
1124
1542
|
}
|
|
1543
|
+
/**
|
|
1544
|
+
* Build a stable, human-readable identifier for a fixture's match shape, for
|
|
1545
|
+
* any log line that has to name WHICH fixture it means. The `Fixture` type
|
|
1546
|
+
* carries no `id`/`name`, so the matchers are the only handle a reader has.
|
|
1547
|
+
*
|
|
1548
|
+
* Used by the relaxed-turnIndex warning (`router.ts`) and by the chaos
|
|
1549
|
+
* rejected-value warning (`chaos.ts`); one implementation so the two name the
|
|
1550
|
+
* same fixture the same way. The obvious `JSON.stringify(match)` is unfit: it
|
|
1551
|
+
* DROPS `predicate` functions (non-serialisable) and serialises any RegExp
|
|
1552
|
+
* matcher to `{}`, so a predicate- or regex-gated fixture's warning collapsed to
|
|
1553
|
+
* an uninformative "served fixture {}" / `{"userMessage":{}}` blob.
|
|
1554
|
+
*
|
|
1555
|
+
* Instead we list the PRESENT matcher keys in declaration order, annotating each
|
|
1556
|
+
* by VALUE KIND so predicates and regexes survive: `predicate(fn)`,
|
|
1557
|
+
* `userMessage(regex)`, `userMessage("hello")`, `turnIndex=0`, etc. The
|
|
1558
|
+
* fixture's array `index` is prefixed as a stable positional identifier when
|
|
1559
|
+
* the caller knows it (i.e. `>= 0`); callers holding only the fixture object
|
|
1560
|
+
* pass `-1` and get the matcher summary alone. String/number values are shown
|
|
1561
|
+
* inline (truncated) so a content match remains recognisable; the whole string
|
|
1562
|
+
* is capped to keep the log line bounded.
|
|
1563
|
+
*/
|
|
1564
|
+
const DESCRIBE_MATCH_MAX = 160;
|
|
1565
|
+
function describeMatch(match, index) {
|
|
1566
|
+
const parts = [];
|
|
1567
|
+
for (const [key, value] of Object.entries(match)) {
|
|
1568
|
+
if (value === void 0) continue;
|
|
1569
|
+
if (typeof value === "function") parts.push(key === "predicate" ? "[predicate]" : `${key}[fn]`);
|
|
1570
|
+
else if (value instanceof RegExp) parts.push(`${key}(${value})`);
|
|
1571
|
+
else if (typeof value === "string") {
|
|
1572
|
+
const v = value.length > 40 ? `${value.slice(0, 40)}…` : value;
|
|
1573
|
+
parts.push(`${key}(${JSON.stringify(v)})`);
|
|
1574
|
+
} else if (Array.isArray(value)) parts.push(`${key}(${value.length} item${value.length === 1 ? "" : "s"})`);
|
|
1575
|
+
else if (typeof value === "object" && value !== null) {
|
|
1576
|
+
const json = JSON.stringify(value);
|
|
1577
|
+
parts.push(`${key}(${json.length > 40 ? `${json.slice(0, 40)}…` : json})`);
|
|
1578
|
+
} else parts.push(`${key}=${String(value)}`);
|
|
1579
|
+
}
|
|
1580
|
+
const keys = parts.length > 0 ? parts.join(", ") : "no matchers";
|
|
1581
|
+
const full = `${index >= 0 ? `#${index} ` : ""}{ ${keys} }`;
|
|
1582
|
+
if (full.length <= DESCRIBE_MATCH_MAX) return full;
|
|
1583
|
+
const head = full.slice(0, DESCRIBE_MATCH_MAX - 4);
|
|
1584
|
+
const cut = head.lastIndexOf(", ");
|
|
1585
|
+
return `${cut > 0 ? head.slice(0, cut) : head}, … }`;
|
|
1586
|
+
}
|
|
1587
|
+
/**
|
|
1588
|
+
* Parse an `X-AIMock-MCP-Undeclared` header or `?undeclared=` value.
|
|
1589
|
+
* Modeled on {@link resolveStrictMode}: case-insensitive, surrounding
|
|
1590
|
+
* whitespace trimmed. Returns `null` when unrecognised; unlike the strict
|
|
1591
|
+
* header, {@link resolveMcpIdentity} turns that into an error rather than a
|
|
1592
|
+
* fall-through, because falling through would turn a mistyped `deny` into
|
|
1593
|
+
* the permissive default.
|
|
1594
|
+
*/
|
|
1595
|
+
function parseMcpUndeclared(raw) {
|
|
1596
|
+
const val = raw.trim().toLowerCase();
|
|
1597
|
+
if (val === "allow" || val === "deny") return val;
|
|
1598
|
+
return null;
|
|
1599
|
+
}
|
|
1125
1600
|
|
|
1126
1601
|
//#endregion
|
|
1127
1602
|
exports.FORMAT_TO_CONTENT_TYPE = FORMAT_TO_CONTENT_TYPE;
|
|
1128
1603
|
exports.MAX_EMBEDDING_DIMENSIONS = MAX_EMBEDDING_DIMENSIONS;
|
|
1604
|
+
exports.RequestBodyTooLargeError = RequestBodyTooLargeError;
|
|
1129
1605
|
exports.buildContentWithToolCallsChunks = buildContentWithToolCallsChunks;
|
|
1130
1606
|
exports.buildContentWithToolCallsCompletion = buildContentWithToolCallsCompletion;
|
|
1131
1607
|
exports.buildEmbeddingResponse = buildEmbeddingResponse;
|
|
@@ -1134,6 +1610,8 @@ exports.buildTextCompletion = buildTextCompletion;
|
|
|
1134
1610
|
exports.buildToolCallChunks = buildToolCallChunks;
|
|
1135
1611
|
exports.buildToolCallCompletion = buildToolCallCompletion;
|
|
1136
1612
|
exports.buildUsageChunk = buildUsageChunk;
|
|
1613
|
+
exports.decodeMcpHeaderValue = decodeMcpHeaderValue;
|
|
1614
|
+
exports.describeMatch = describeMatch;
|
|
1137
1615
|
exports.extractOverrides = extractOverrides;
|
|
1138
1616
|
exports.flattenHeaders = flattenHeaders;
|
|
1139
1617
|
exports.formatToMime = formatToMime;
|
|
@@ -1144,6 +1622,7 @@ exports.generateToolCallId = generateToolCallId;
|
|
|
1144
1622
|
exports.generateToolUseId = generateToolUseId;
|
|
1145
1623
|
exports.getContext = getContext;
|
|
1146
1624
|
exports.getTestId = getTestId;
|
|
1625
|
+
exports.hasMintedRequestId = hasMintedRequestId;
|
|
1147
1626
|
exports.isAudioResponse = isAudioResponse;
|
|
1148
1627
|
exports.isContentWithToolCallsResponse = isContentWithToolCallsResponse;
|
|
1149
1628
|
exports.isEmbeddingResponse = isEmbeddingResponse;
|
|
@@ -1151,18 +1630,25 @@ exports.isErrorResponse = isErrorResponse;
|
|
|
1151
1630
|
exports.isImageResponse = isImageResponse;
|
|
1152
1631
|
exports.isJSONResponse = isJSONResponse;
|
|
1153
1632
|
exports.isJsonObject = isJsonObject;
|
|
1633
|
+
exports.isLiveResponse = isLiveResponse;
|
|
1154
1634
|
exports.isResponseFactory = isResponseFactory;
|
|
1155
1635
|
exports.isTextResponse = isTextResponse;
|
|
1156
1636
|
exports.isToolCallResponse = isToolCallResponse;
|
|
1157
1637
|
exports.isTranscriptionResponse = isTranscriptionResponse;
|
|
1158
1638
|
exports.isVideoResponse = isVideoResponse;
|
|
1639
|
+
exports.markMintedRequestId = markMintedRequestId;
|
|
1159
1640
|
exports.matchesPattern = matchesPattern;
|
|
1160
1641
|
exports.normalizeEmbeddingInput = normalizeEmbeddingInput;
|
|
1161
1642
|
exports.normalizeTextInput = normalizeTextInput;
|
|
1643
|
+
exports.parseStrictIntegerText = parseStrictIntegerText;
|
|
1162
1644
|
exports.readBody = readBody;
|
|
1645
|
+
exports.readBodyBufferBounded = readBodyBufferBounded;
|
|
1646
|
+
exports.resolveFixtureBlockOutcome = resolveFixtureBlockOutcome;
|
|
1163
1647
|
exports.resolveFixtureBlocks = resolveFixtureBlocks;
|
|
1648
|
+
exports.resolveMcpIdentity = resolveMcpIdentity;
|
|
1164
1649
|
exports.resolveReasoningArtifactsForModel = resolveReasoningArtifactsForModel;
|
|
1165
1650
|
exports.resolveReasoningForModel = resolveReasoningForModel;
|
|
1651
|
+
exports.resolveRequestId = resolveRequestId;
|
|
1166
1652
|
exports.resolveResponse = resolveResponse;
|
|
1167
1653
|
exports.resolveStrictMode = resolveStrictMode;
|
|
1168
1654
|
exports.resolveTestId = resolveTestId;
|
|
@@ -1176,4 +1662,5 @@ exports.strictOverrideField = strictOverrideField;
|
|
|
1176
1662
|
exports.validateChatMessages = validateChatMessages;
|
|
1177
1663
|
exports.validateEmbeddingDimensions = validateEmbeddingDimensions;
|
|
1178
1664
|
exports.validateToolsField = validateToolsField;
|
|
1665
|
+
exports.wouldProxyMiss = wouldProxyMiss;
|
|
1179
1666
|
//# sourceMappingURL=helpers.cjs.map
|