@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
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
const require_constants = require('./constants.cjs');
|
|
2
|
+
const require_echo_text = require('./echo-text.cjs');
|
|
3
|
+
|
|
4
|
+
//#region src/message-text.ts
|
|
5
|
+
/**
|
|
6
|
+
* The one message builder of MCP fakes: every error and warning message of
|
|
7
|
+
* mcp-fakes.ts, the `FixtureLoadError` message (fixture-loader.ts) and the MCP
|
|
8
|
+
* identity error messages (helpers.ts) are built here. A message is a list of
|
|
9
|
+
* parts: fixed text written by aimock, kept whole, and user parts (a tool
|
|
10
|
+
* name, an id, a source, a JSON path, a value, a thrown message), each quoted
|
|
11
|
+
* or rendered as JSON when it is made. `build` gives the user parts their
|
|
12
|
+
* shares of the message's length budget and cuts each one once, with
|
|
13
|
+
* `cutText` (echo-text.ts), so the fixed text is never cut and every
|
|
14
|
+
* `… (<n> more chars)` count is the true count of chars left out of that
|
|
15
|
+
* part. A leaf module (it imports only constants.ts and echo-text.ts).
|
|
16
|
+
*/
|
|
17
|
+
/**
|
|
18
|
+
* User data in a message: its whole text, not cut until the message is built.
|
|
19
|
+
* `units` is what a cut of it must not split; `max` is the most chars it may
|
|
20
|
+
* take, whatever the budget: at least 1 (`Infinity` too), so a cut part
|
|
21
|
+
* always keeps at least a `…`. A `max` below 1, or `NaN`, is a `RangeError`.
|
|
22
|
+
*
|
|
23
|
+
* @internal
|
|
24
|
+
*/
|
|
25
|
+
var UserText = class {
|
|
26
|
+
constructor(text, units, max) {
|
|
27
|
+
this.text = text;
|
|
28
|
+
this.units = units;
|
|
29
|
+
this.max = max;
|
|
30
|
+
if (!(max >= 1)) throw new RangeError(`a user part max must be at least 1, got ${max}`);
|
|
31
|
+
Object.freeze(this);
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
/**
|
|
35
|
+
* A message that is not built yet: fixed text and user parts, in order.
|
|
36
|
+
*
|
|
37
|
+
* @internal
|
|
38
|
+
*/
|
|
39
|
+
var Message = class {
|
|
40
|
+
parts;
|
|
41
|
+
constructor(parts) {
|
|
42
|
+
this.parts = Object.freeze([...parts]);
|
|
43
|
+
Object.freeze(this);
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
/**
|
|
47
|
+
* A message from a template: its literal text is fixed text, a number is
|
|
48
|
+
* fixed text, a `Message` is spliced in part by part, and a `UserText` is a
|
|
49
|
+
* user part.
|
|
50
|
+
*
|
|
51
|
+
* @internal
|
|
52
|
+
*/
|
|
53
|
+
function msg(strings, ...values) {
|
|
54
|
+
const parts = [];
|
|
55
|
+
strings.forEach((literal, i) => {
|
|
56
|
+
if (literal !== "") parts.push(literal);
|
|
57
|
+
if (i >= values.length) return;
|
|
58
|
+
const value = values[i];
|
|
59
|
+
if (typeof value === "number") parts.push(String(value));
|
|
60
|
+
else if (value instanceof Message) parts.push(...value.parts);
|
|
61
|
+
else parts.push(value);
|
|
62
|
+
});
|
|
63
|
+
return new Message(parts);
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Aimock's own text as a message: a constant, a fixed list, a built-in tag,
|
|
67
|
+
* or a message that was already built. Never user data.
|
|
68
|
+
*
|
|
69
|
+
* @internal
|
|
70
|
+
*/
|
|
71
|
+
function fixed(text) {
|
|
72
|
+
return new Message([text]);
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Every control char (C0, DEL, C1) and the line and paragraph separators.
|
|
76
|
+
* `JSON.stringify` escapes C0 controls but keeps DEL, C1 controls and the
|
|
77
|
+
* separators raw; text that is not JSON (a text `FixtureLoadError` detail) can
|
|
78
|
+
* hold any of them.
|
|
79
|
+
*/
|
|
80
|
+
const RAW_CONTROLS = /[\u0000-\u001f\u007f-\u009f\u2028\u2029]/g;
|
|
81
|
+
/**
|
|
82
|
+
* `text` JSON-quoted: quotes, backslashes and control chars escaped, DEL, C1
|
|
83
|
+
* controls and U+2028 / U+2029 too (as `\uXXXX`), so the result is one line
|
|
84
|
+
* of printable text that `JSON.parse` reads back as `text`.
|
|
85
|
+
*
|
|
86
|
+
* @internal
|
|
87
|
+
*/
|
|
88
|
+
function quoteJson(text) {
|
|
89
|
+
return escapeRawControls(JSON.stringify(text));
|
|
90
|
+
}
|
|
91
|
+
function escapeRawControls(json) {
|
|
92
|
+
return json.replace(RAW_CONTROLS, (c) => `\\u${c.charCodeAt(0).toString(16).padStart(4, "0")}`);
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* A user string as a message part: JSON-quoted by `quoteJson`, cut as JSON
|
|
96
|
+
* text (never inside an escape) to its share, and never to more than `max`
|
|
97
|
+
* chars.
|
|
98
|
+
*
|
|
99
|
+
* @internal
|
|
100
|
+
*/
|
|
101
|
+
function quote(text, max = require_constants.MCP_FAKES_ECHO_LIMIT) {
|
|
102
|
+
return jsonText(JSON.stringify(text), max);
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* User data already rendered as JSON text (a value, or a JSON path whose keys
|
|
106
|
+
* are JSON-quoted), as a message part: every raw control char (C0, DEL, C1)
|
|
107
|
+
* and line separator in it escaped as `\uXXXX` (in JSON text they can only be
|
|
108
|
+
* inside its strings), cut as JSON text.
|
|
109
|
+
*
|
|
110
|
+
* @internal
|
|
111
|
+
*/
|
|
112
|
+
function jsonText(json, max = require_constants.MCP_FAKES_ECHO_LIMIT) {
|
|
113
|
+
return new UserText(escapeRawControls(json), "json", max);
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Text that is not JSON as a message part, cut as text (never inside a
|
|
117
|
+
* surrogate pair).
|
|
118
|
+
*
|
|
119
|
+
* @internal
|
|
120
|
+
*/
|
|
121
|
+
function plainText(text, max = require_constants.MCP_FAKES_ECHO_LIMIT) {
|
|
122
|
+
return new UserText(text, "text", max);
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* `message` built in a budget of its own (`limit` chars), as fixed text of
|
|
126
|
+
* the message it goes into: a part of a message with its own cap.
|
|
127
|
+
*
|
|
128
|
+
* @internal
|
|
129
|
+
*/
|
|
130
|
+
function within(limit, message) {
|
|
131
|
+
return fixed(build(message, limit));
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* The text of `message`, no longer than `limit` when its fixed text and one
|
|
135
|
+
* char per user part fit in `limit`. The fixed text is kept whole. The user
|
|
136
|
+
* parts share what is left: a part that fits in an equal share keeps all of
|
|
137
|
+
* it, and the others split the rest (water-filling), each at most its `max`.
|
|
138
|
+
* Each user part is then cut once, by `cutText`, to its share. A `limit` of
|
|
139
|
+
* `Infinity` cuts each part only to its `max`. A `NaN` limit is a
|
|
140
|
+
* `RangeError`; a limit too small for the fixed text still gives each
|
|
141
|
+
* non-empty user part 1 char, a bare `…`.
|
|
142
|
+
*
|
|
143
|
+
* @internal
|
|
144
|
+
*/
|
|
145
|
+
function build(message, limit = Infinity) {
|
|
146
|
+
if (Number.isNaN(limit)) throw new RangeError("build limit must be a number, got NaN");
|
|
147
|
+
const users = message.parts.filter((part) => part instanceof UserText);
|
|
148
|
+
let fixedLength = 0;
|
|
149
|
+
for (const part of message.parts) if (typeof part === "string") fixedLength += part.length;
|
|
150
|
+
const shares = allocate(users, limit - fixedLength);
|
|
151
|
+
let i = 0;
|
|
152
|
+
return message.parts.map((part) => typeof part === "string" ? part : require_echo_text.cutText(part.text, shares[i++], part.units)).join("");
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Each user part's share of `room` chars, by water-filling: at least 1 char
|
|
156
|
+
* each (a bare `…` when room runs out), but never more than the part's cap.
|
|
157
|
+
*/
|
|
158
|
+
function allocate(users, room) {
|
|
159
|
+
const caps = users.map((u) => Math.min(u.text.length, Math.floor(u.max)));
|
|
160
|
+
const order = caps.map((_, i) => i).sort((a, b) => caps[a] - caps[b]);
|
|
161
|
+
const shares = new Array(users.length).fill(0);
|
|
162
|
+
let left = Math.max(0, room);
|
|
163
|
+
order.forEach((index, k) => {
|
|
164
|
+
const fair = Math.floor(left / (order.length - k));
|
|
165
|
+
const share = Math.min(caps[index], Math.max(1, fair));
|
|
166
|
+
shares[index] = share;
|
|
167
|
+
left -= share;
|
|
168
|
+
});
|
|
169
|
+
return shares;
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* The text of `message` with no cut at all: for a message that goes, as
|
|
173
|
+
* text, into a user part of another message (a placeholder reason inside a
|
|
174
|
+
* rendered value), which is then cut once there.
|
|
175
|
+
*
|
|
176
|
+
* @internal
|
|
177
|
+
*/
|
|
178
|
+
function fullText(message) {
|
|
179
|
+
return message.parts.map((part) => typeof part === "string" ? part : part.text).join("");
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
//#endregion
|
|
183
|
+
exports.build = build;
|
|
184
|
+
exports.fixed = fixed;
|
|
185
|
+
exports.fullText = fullText;
|
|
186
|
+
exports.jsonText = jsonText;
|
|
187
|
+
exports.msg = msg;
|
|
188
|
+
exports.plainText = plainText;
|
|
189
|
+
exports.quote = quote;
|
|
190
|
+
exports.quoteJson = quoteJson;
|
|
191
|
+
exports.within = within;
|
|
192
|
+
//# sourceMappingURL=message-text.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"message-text.cjs","names":["MCP_FAKES_ECHO_LIMIT","cutText"],"sources":["../src/message-text.ts"],"sourcesContent":["/**\n * The one message builder of MCP fakes: every error and warning message of\n * mcp-fakes.ts, the `FixtureLoadError` message (fixture-loader.ts) and the MCP\n * identity error messages (helpers.ts) are built here. A message is a list of\n * parts: fixed text written by aimock, kept whole, and user parts (a tool\n * name, an id, a source, a JSON path, a value, a thrown message), each quoted\n * or rendered as JSON when it is made. `build` gives the user parts their\n * shares of the message's length budget and cuts each one once, with\n * `cutText` (echo-text.ts), so the fixed text is never cut and every\n * `… (<n> more chars)` count is the true count of chars left out of that\n * part. A leaf module (it imports only constants.ts and echo-text.ts).\n */\nimport { MCP_FAKES_ECHO_LIMIT } from \"./constants.js\";\nimport { cutText, type CutUnits } from \"./echo-text.js\";\n\n/**\n * User data in a message: its whole text, not cut until the message is built.\n * `units` is what a cut of it must not split; `max` is the most chars it may\n * take, whatever the budget: at least 1 (`Infinity` too), so a cut part\n * always keeps at least a `…`. A `max` below 1, or `NaN`, is a `RangeError`.\n *\n * @internal\n */\nexport class UserText {\n constructor(\n readonly text: string,\n readonly units: CutUnits,\n readonly max: number,\n ) {\n if (!(max >= 1)) throw new RangeError(`a user part max must be at least 1, got ${max}`);\n Object.freeze(this);\n }\n}\n\n/**\n * A message that is not built yet: fixed text and user parts, in order.\n *\n * @internal\n */\nexport class Message {\n readonly parts: readonly (string | UserText)[];\n\n constructor(parts: readonly (string | UserText)[]) {\n this.parts = Object.freeze([...parts]);\n Object.freeze(this);\n }\n}\n\n/** What a `msg` template may interpolate: a user part, a message, or a number. */\nexport type MessageValue = UserText | Message | number;\n\n/**\n * A message from a template: its literal text is fixed text, a number is\n * fixed text, a `Message` is spliced in part by part, and a `UserText` is a\n * user part.\n *\n * @internal\n */\nexport function msg(strings: TemplateStringsArray, ...values: readonly MessageValue[]): Message {\n const parts: (string | UserText)[] = [];\n strings.forEach((literal, i) => {\n if (literal !== \"\") parts.push(literal);\n if (i >= values.length) return;\n const value = values[i];\n if (typeof value === \"number\") parts.push(String(value));\n else if (value instanceof Message) parts.push(...value.parts);\n else parts.push(value);\n });\n return new Message(parts);\n}\n\n/**\n * Aimock's own text as a message: a constant, a fixed list, a built-in tag,\n * or a message that was already built. Never user data.\n *\n * @internal\n */\nexport function fixed(text: string): Message {\n return new Message([text]);\n}\n\n/**\n * Every control char (C0, DEL, C1) and the line and paragraph separators.\n * `JSON.stringify` escapes C0 controls but keeps DEL, C1 controls and the\n * separators raw; text that is not JSON (a text `FixtureLoadError` detail) can\n * hold any of them.\n */\n// eslint-disable-next-line no-control-regex -- matching control chars is the point\nconst RAW_CONTROLS = /[\\u0000-\\u001f\\u007f-\\u009f\\u2028\\u2029]/g;\n\n/**\n * `text` JSON-quoted: quotes, backslashes and control chars escaped, DEL, C1\n * controls and U+2028 / U+2029 too (as `\\uXXXX`), so the result is one line\n * of printable text that `JSON.parse` reads back as `text`.\n *\n * @internal\n */\nexport function quoteJson(text: string): string {\n return escapeRawControls(JSON.stringify(text));\n}\n\nfunction escapeRawControls(json: string): string {\n return json.replace(RAW_CONTROLS, (c) => `\\\\u${c.charCodeAt(0).toString(16).padStart(4, \"0\")}`);\n}\n\n/**\n * A user string as a message part: JSON-quoted by `quoteJson`, cut as JSON\n * text (never inside an escape) to its share, and never to more than `max`\n * chars.\n *\n * @internal\n */\nexport function quote(text: string, max: number = MCP_FAKES_ECHO_LIMIT): UserText {\n return jsonText(JSON.stringify(text), max);\n}\n\n/**\n * User data already rendered as JSON text (a value, or a JSON path whose keys\n * are JSON-quoted), as a message part: every raw control char (C0, DEL, C1)\n * and line separator in it escaped as `\\uXXXX` (in JSON text they can only be\n * inside its strings), cut as JSON text.\n *\n * @internal\n */\nexport function jsonText(json: string, max: number = MCP_FAKES_ECHO_LIMIT): UserText {\n return new UserText(escapeRawControls(json), \"json\", max);\n}\n\n/**\n * Text that is not JSON as a message part, cut as text (never inside a\n * surrogate pair).\n *\n * @internal\n */\nexport function plainText(text: string, max: number = MCP_FAKES_ECHO_LIMIT): UserText {\n return new UserText(text, \"text\", max);\n}\n\n/**\n * `message` built in a budget of its own (`limit` chars), as fixed text of\n * the message it goes into: a part of a message with its own cap.\n *\n * @internal\n */\nexport function within(limit: number, message: Message): Message {\n return fixed(build(message, limit));\n}\n\n/**\n * The text of `message`, no longer than `limit` when its fixed text and one\n * char per user part fit in `limit`. The fixed text is kept whole. The user\n * parts share what is left: a part that fits in an equal share keeps all of\n * it, and the others split the rest (water-filling), each at most its `max`.\n * Each user part is then cut once, by `cutText`, to its share. A `limit` of\n * `Infinity` cuts each part only to its `max`. A `NaN` limit is a\n * `RangeError`; a limit too small for the fixed text still gives each\n * non-empty user part 1 char, a bare `…`.\n *\n * @internal\n */\nexport function build(message: Message, limit = Infinity): string {\n if (Number.isNaN(limit)) throw new RangeError(\"build limit must be a number, got NaN\");\n const users = message.parts.filter((part): part is UserText => part instanceof UserText);\n let fixedLength = 0;\n for (const part of message.parts) if (typeof part === \"string\") fixedLength += part.length;\n const shares = allocate(users, limit - fixedLength);\n let i = 0;\n return message.parts\n .map((part) => (typeof part === \"string\" ? part : cutText(part.text, shares[i++], part.units)))\n .join(\"\");\n}\n\n/**\n * Each user part's share of `room` chars, by water-filling: at least 1 char\n * each (a bare `…` when room runs out), but never more than the part's cap.\n */\nfunction allocate(users: readonly UserText[], room: number): number[] {\n // `max` is at least 1 (UserText checks it), so a non-empty part's cap is too.\n const caps = users.map((u) => Math.min(u.text.length, Math.floor(u.max)));\n const order = caps.map((_, i) => i).sort((a, b) => caps[a] - caps[b]);\n const shares: number[] = new Array<number>(users.length).fill(0);\n let left = Math.max(0, room);\n order.forEach((index, k) => {\n const fair = Math.floor(left / (order.length - k));\n const share = Math.min(caps[index], Math.max(1, fair));\n shares[index] = share;\n left -= share;\n });\n return shares;\n}\n\n/**\n * The text of `message` with no cut at all: for a message that goes, as\n * text, into a user part of another message (a placeholder reason inside a\n * rendered value), which is then cut once there.\n *\n * @internal\n */\nexport function fullText(message: Message): string {\n return message.parts.map((part) => (typeof part === \"string\" ? part : part.text)).join(\"\");\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { CutUnits } from "./echo-text.cjs";
|
|
2
|
+
|
|
3
|
+
//#region src/message-text.d.ts
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* User data in a message: its whole text, not cut until the message is built.
|
|
7
|
+
* `units` is what a cut of it must not split; `max` is the most chars it may
|
|
8
|
+
* take, whatever the budget: at least 1 (`Infinity` too), so a cut part
|
|
9
|
+
* always keeps at least a `…`. A `max` below 1, or `NaN`, is a `RangeError`.
|
|
10
|
+
*
|
|
11
|
+
* @internal
|
|
12
|
+
*/
|
|
13
|
+
declare class UserText {
|
|
14
|
+
readonly text: string;
|
|
15
|
+
readonly units: CutUnits;
|
|
16
|
+
readonly max: number;
|
|
17
|
+
constructor(text: string, units: CutUnits, max: number);
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* A message that is not built yet: fixed text and user parts, in order.
|
|
21
|
+
*
|
|
22
|
+
* @internal
|
|
23
|
+
*/
|
|
24
|
+
declare class Message {
|
|
25
|
+
readonly parts: readonly (string | UserText)[];
|
|
26
|
+
constructor(parts: readonly (string | UserText)[]);
|
|
27
|
+
}
|
|
28
|
+
/** What a `msg` template may interpolate: a user part, a message, or a number. */
|
|
29
|
+
//#endregion
|
|
30
|
+
export { Message };
|
|
31
|
+
//# sourceMappingURL=message-text.d.cts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"message-text.d.cts","names":[],"sources":["../src/message-text.ts"],"sourcesContent":[],"mappings":";;;;;;AAuBA;;;;;AAgBA;AAAoB,cAhBP,QAAA,CAgBO;WACiB,IAAA,EAAA,MAAA;WAEG,KAAA,EAhBpB,QAgBoB;EAAQ,SAAA,GAAA,EAAA,MAAA;mCAhB5B;;;;;;;cAaP,OAAA;qCACwB;wCAEG"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { CutUnits } from "./echo-text.js";
|
|
2
|
+
|
|
3
|
+
//#region src/message-text.d.ts
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* User data in a message: its whole text, not cut until the message is built.
|
|
7
|
+
* `units` is what a cut of it must not split; `max` is the most chars it may
|
|
8
|
+
* take, whatever the budget: at least 1 (`Infinity` too), so a cut part
|
|
9
|
+
* always keeps at least a `…`. A `max` below 1, or `NaN`, is a `RangeError`.
|
|
10
|
+
*
|
|
11
|
+
* @internal
|
|
12
|
+
*/
|
|
13
|
+
declare class UserText {
|
|
14
|
+
readonly text: string;
|
|
15
|
+
readonly units: CutUnits;
|
|
16
|
+
readonly max: number;
|
|
17
|
+
constructor(text: string, units: CutUnits, max: number);
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* A message that is not built yet: fixed text and user parts, in order.
|
|
21
|
+
*
|
|
22
|
+
* @internal
|
|
23
|
+
*/
|
|
24
|
+
declare class Message {
|
|
25
|
+
readonly parts: readonly (string | UserText)[];
|
|
26
|
+
constructor(parts: readonly (string | UserText)[]);
|
|
27
|
+
}
|
|
28
|
+
/** What a `msg` template may interpolate: a user part, a message, or a number. */
|
|
29
|
+
//#endregion
|
|
30
|
+
export { Message };
|
|
31
|
+
//# sourceMappingURL=message-text.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"message-text.d.ts","names":[],"sources":["../src/message-text.ts"],"sourcesContent":[],"mappings":";;;;;;AAuBA;;;;;AAgBA;AAAoB,cAhBP,QAAA,CAgBO;WACiB,IAAA,EAAA,MAAA;WAEG,KAAA,EAhBpB,QAgBoB;EAAQ,SAAA,GAAA,EAAA,MAAA;mCAhB5B;;;;;;;cAaP,OAAA;qCACwB;wCAEG"}
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
import { MCP_FAKES_ECHO_LIMIT } from "./constants.js";
|
|
2
|
+
import { cutText } from "./echo-text.js";
|
|
3
|
+
|
|
4
|
+
//#region src/message-text.ts
|
|
5
|
+
/**
|
|
6
|
+
* The one message builder of MCP fakes: every error and warning message of
|
|
7
|
+
* mcp-fakes.ts, the `FixtureLoadError` message (fixture-loader.ts) and the MCP
|
|
8
|
+
* identity error messages (helpers.ts) are built here. A message is a list of
|
|
9
|
+
* parts: fixed text written by aimock, kept whole, and user parts (a tool
|
|
10
|
+
* name, an id, a source, a JSON path, a value, a thrown message), each quoted
|
|
11
|
+
* or rendered as JSON when it is made. `build` gives the user parts their
|
|
12
|
+
* shares of the message's length budget and cuts each one once, with
|
|
13
|
+
* `cutText` (echo-text.ts), so the fixed text is never cut and every
|
|
14
|
+
* `… (<n> more chars)` count is the true count of chars left out of that
|
|
15
|
+
* part. A leaf module (it imports only constants.ts and echo-text.ts).
|
|
16
|
+
*/
|
|
17
|
+
/**
|
|
18
|
+
* User data in a message: its whole text, not cut until the message is built.
|
|
19
|
+
* `units` is what a cut of it must not split; `max` is the most chars it may
|
|
20
|
+
* take, whatever the budget: at least 1 (`Infinity` too), so a cut part
|
|
21
|
+
* always keeps at least a `…`. A `max` below 1, or `NaN`, is a `RangeError`.
|
|
22
|
+
*
|
|
23
|
+
* @internal
|
|
24
|
+
*/
|
|
25
|
+
var UserText = class {
|
|
26
|
+
constructor(text, units, max) {
|
|
27
|
+
this.text = text;
|
|
28
|
+
this.units = units;
|
|
29
|
+
this.max = max;
|
|
30
|
+
if (!(max >= 1)) throw new RangeError(`a user part max must be at least 1, got ${max}`);
|
|
31
|
+
Object.freeze(this);
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
/**
|
|
35
|
+
* A message that is not built yet: fixed text and user parts, in order.
|
|
36
|
+
*
|
|
37
|
+
* @internal
|
|
38
|
+
*/
|
|
39
|
+
var Message = class {
|
|
40
|
+
parts;
|
|
41
|
+
constructor(parts) {
|
|
42
|
+
this.parts = Object.freeze([...parts]);
|
|
43
|
+
Object.freeze(this);
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
/**
|
|
47
|
+
* A message from a template: its literal text is fixed text, a number is
|
|
48
|
+
* fixed text, a `Message` is spliced in part by part, and a `UserText` is a
|
|
49
|
+
* user part.
|
|
50
|
+
*
|
|
51
|
+
* @internal
|
|
52
|
+
*/
|
|
53
|
+
function msg(strings, ...values) {
|
|
54
|
+
const parts = [];
|
|
55
|
+
strings.forEach((literal, i) => {
|
|
56
|
+
if (literal !== "") parts.push(literal);
|
|
57
|
+
if (i >= values.length) return;
|
|
58
|
+
const value = values[i];
|
|
59
|
+
if (typeof value === "number") parts.push(String(value));
|
|
60
|
+
else if (value instanceof Message) parts.push(...value.parts);
|
|
61
|
+
else parts.push(value);
|
|
62
|
+
});
|
|
63
|
+
return new Message(parts);
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Aimock's own text as a message: a constant, a fixed list, a built-in tag,
|
|
67
|
+
* or a message that was already built. Never user data.
|
|
68
|
+
*
|
|
69
|
+
* @internal
|
|
70
|
+
*/
|
|
71
|
+
function fixed(text) {
|
|
72
|
+
return new Message([text]);
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Every control char (C0, DEL, C1) and the line and paragraph separators.
|
|
76
|
+
* `JSON.stringify` escapes C0 controls but keeps DEL, C1 controls and the
|
|
77
|
+
* separators raw; text that is not JSON (a text `FixtureLoadError` detail) can
|
|
78
|
+
* hold any of them.
|
|
79
|
+
*/
|
|
80
|
+
const RAW_CONTROLS = /[\u0000-\u001f\u007f-\u009f\u2028\u2029]/g;
|
|
81
|
+
/**
|
|
82
|
+
* `text` JSON-quoted: quotes, backslashes and control chars escaped, DEL, C1
|
|
83
|
+
* controls and U+2028 / U+2029 too (as `\uXXXX`), so the result is one line
|
|
84
|
+
* of printable text that `JSON.parse` reads back as `text`.
|
|
85
|
+
*
|
|
86
|
+
* @internal
|
|
87
|
+
*/
|
|
88
|
+
function quoteJson(text) {
|
|
89
|
+
return escapeRawControls(JSON.stringify(text));
|
|
90
|
+
}
|
|
91
|
+
function escapeRawControls(json) {
|
|
92
|
+
return json.replace(RAW_CONTROLS, (c) => `\\u${c.charCodeAt(0).toString(16).padStart(4, "0")}`);
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* A user string as a message part: JSON-quoted by `quoteJson`, cut as JSON
|
|
96
|
+
* text (never inside an escape) to its share, and never to more than `max`
|
|
97
|
+
* chars.
|
|
98
|
+
*
|
|
99
|
+
* @internal
|
|
100
|
+
*/
|
|
101
|
+
function quote(text, max = MCP_FAKES_ECHO_LIMIT) {
|
|
102
|
+
return jsonText(JSON.stringify(text), max);
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* User data already rendered as JSON text (a value, or a JSON path whose keys
|
|
106
|
+
* are JSON-quoted), as a message part: every raw control char (C0, DEL, C1)
|
|
107
|
+
* and line separator in it escaped as `\uXXXX` (in JSON text they can only be
|
|
108
|
+
* inside its strings), cut as JSON text.
|
|
109
|
+
*
|
|
110
|
+
* @internal
|
|
111
|
+
*/
|
|
112
|
+
function jsonText(json, max = MCP_FAKES_ECHO_LIMIT) {
|
|
113
|
+
return new UserText(escapeRawControls(json), "json", max);
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Text that is not JSON as a message part, cut as text (never inside a
|
|
117
|
+
* surrogate pair).
|
|
118
|
+
*
|
|
119
|
+
* @internal
|
|
120
|
+
*/
|
|
121
|
+
function plainText(text, max = MCP_FAKES_ECHO_LIMIT) {
|
|
122
|
+
return new UserText(text, "text", max);
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* `message` built in a budget of its own (`limit` chars), as fixed text of
|
|
126
|
+
* the message it goes into: a part of a message with its own cap.
|
|
127
|
+
*
|
|
128
|
+
* @internal
|
|
129
|
+
*/
|
|
130
|
+
function within(limit, message) {
|
|
131
|
+
return fixed(build(message, limit));
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* The text of `message`, no longer than `limit` when its fixed text and one
|
|
135
|
+
* char per user part fit in `limit`. The fixed text is kept whole. The user
|
|
136
|
+
* parts share what is left: a part that fits in an equal share keeps all of
|
|
137
|
+
* it, and the others split the rest (water-filling), each at most its `max`.
|
|
138
|
+
* Each user part is then cut once, by `cutText`, to its share. A `limit` of
|
|
139
|
+
* `Infinity` cuts each part only to its `max`. A `NaN` limit is a
|
|
140
|
+
* `RangeError`; a limit too small for the fixed text still gives each
|
|
141
|
+
* non-empty user part 1 char, a bare `…`.
|
|
142
|
+
*
|
|
143
|
+
* @internal
|
|
144
|
+
*/
|
|
145
|
+
function build(message, limit = Infinity) {
|
|
146
|
+
if (Number.isNaN(limit)) throw new RangeError("build limit must be a number, got NaN");
|
|
147
|
+
const users = message.parts.filter((part) => part instanceof UserText);
|
|
148
|
+
let fixedLength = 0;
|
|
149
|
+
for (const part of message.parts) if (typeof part === "string") fixedLength += part.length;
|
|
150
|
+
const shares = allocate(users, limit - fixedLength);
|
|
151
|
+
let i = 0;
|
|
152
|
+
return message.parts.map((part) => typeof part === "string" ? part : cutText(part.text, shares[i++], part.units)).join("");
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Each user part's share of `room` chars, by water-filling: at least 1 char
|
|
156
|
+
* each (a bare `…` when room runs out), but never more than the part's cap.
|
|
157
|
+
*/
|
|
158
|
+
function allocate(users, room) {
|
|
159
|
+
const caps = users.map((u) => Math.min(u.text.length, Math.floor(u.max)));
|
|
160
|
+
const order = caps.map((_, i) => i).sort((a, b) => caps[a] - caps[b]);
|
|
161
|
+
const shares = new Array(users.length).fill(0);
|
|
162
|
+
let left = Math.max(0, room);
|
|
163
|
+
order.forEach((index, k) => {
|
|
164
|
+
const fair = Math.floor(left / (order.length - k));
|
|
165
|
+
const share = Math.min(caps[index], Math.max(1, fair));
|
|
166
|
+
shares[index] = share;
|
|
167
|
+
left -= share;
|
|
168
|
+
});
|
|
169
|
+
return shares;
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* The text of `message` with no cut at all: for a message that goes, as
|
|
173
|
+
* text, into a user part of another message (a placeholder reason inside a
|
|
174
|
+
* rendered value), which is then cut once there.
|
|
175
|
+
*
|
|
176
|
+
* @internal
|
|
177
|
+
*/
|
|
178
|
+
function fullText(message) {
|
|
179
|
+
return message.parts.map((part) => typeof part === "string" ? part : part.text).join("");
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
//#endregion
|
|
183
|
+
export { build, fixed, fullText, jsonText, msg, plainText, quote, quoteJson, within };
|
|
184
|
+
//# sourceMappingURL=message-text.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"message-text.js","names":[],"sources":["../src/message-text.ts"],"sourcesContent":["/**\n * The one message builder of MCP fakes: every error and warning message of\n * mcp-fakes.ts, the `FixtureLoadError` message (fixture-loader.ts) and the MCP\n * identity error messages (helpers.ts) are built here. A message is a list of\n * parts: fixed text written by aimock, kept whole, and user parts (a tool\n * name, an id, a source, a JSON path, a value, a thrown message), each quoted\n * or rendered as JSON when it is made. `build` gives the user parts their\n * shares of the message's length budget and cuts each one once, with\n * `cutText` (echo-text.ts), so the fixed text is never cut and every\n * `… (<n> more chars)` count is the true count of chars left out of that\n * part. A leaf module (it imports only constants.ts and echo-text.ts).\n */\nimport { MCP_FAKES_ECHO_LIMIT } from \"./constants.js\";\nimport { cutText, type CutUnits } from \"./echo-text.js\";\n\n/**\n * User data in a message: its whole text, not cut until the message is built.\n * `units` is what a cut of it must not split; `max` is the most chars it may\n * take, whatever the budget: at least 1 (`Infinity` too), so a cut part\n * always keeps at least a `…`. A `max` below 1, or `NaN`, is a `RangeError`.\n *\n * @internal\n */\nexport class UserText {\n constructor(\n readonly text: string,\n readonly units: CutUnits,\n readonly max: number,\n ) {\n if (!(max >= 1)) throw new RangeError(`a user part max must be at least 1, got ${max}`);\n Object.freeze(this);\n }\n}\n\n/**\n * A message that is not built yet: fixed text and user parts, in order.\n *\n * @internal\n */\nexport class Message {\n readonly parts: readonly (string | UserText)[];\n\n constructor(parts: readonly (string | UserText)[]) {\n this.parts = Object.freeze([...parts]);\n Object.freeze(this);\n }\n}\n\n/** What a `msg` template may interpolate: a user part, a message, or a number. */\nexport type MessageValue = UserText | Message | number;\n\n/**\n * A message from a template: its literal text is fixed text, a number is\n * fixed text, a `Message` is spliced in part by part, and a `UserText` is a\n * user part.\n *\n * @internal\n */\nexport function msg(strings: TemplateStringsArray, ...values: readonly MessageValue[]): Message {\n const parts: (string | UserText)[] = [];\n strings.forEach((literal, i) => {\n if (literal !== \"\") parts.push(literal);\n if (i >= values.length) return;\n const value = values[i];\n if (typeof value === \"number\") parts.push(String(value));\n else if (value instanceof Message) parts.push(...value.parts);\n else parts.push(value);\n });\n return new Message(parts);\n}\n\n/**\n * Aimock's own text as a message: a constant, a fixed list, a built-in tag,\n * or a message that was already built. Never user data.\n *\n * @internal\n */\nexport function fixed(text: string): Message {\n return new Message([text]);\n}\n\n/**\n * Every control char (C0, DEL, C1) and the line and paragraph separators.\n * `JSON.stringify` escapes C0 controls but keeps DEL, C1 controls and the\n * separators raw; text that is not JSON (a text `FixtureLoadError` detail) can\n * hold any of them.\n */\n// eslint-disable-next-line no-control-regex -- matching control chars is the point\nconst RAW_CONTROLS = /[\\u0000-\\u001f\\u007f-\\u009f\\u2028\\u2029]/g;\n\n/**\n * `text` JSON-quoted: quotes, backslashes and control chars escaped, DEL, C1\n * controls and U+2028 / U+2029 too (as `\\uXXXX`), so the result is one line\n * of printable text that `JSON.parse` reads back as `text`.\n *\n * @internal\n */\nexport function quoteJson(text: string): string {\n return escapeRawControls(JSON.stringify(text));\n}\n\nfunction escapeRawControls(json: string): string {\n return json.replace(RAW_CONTROLS, (c) => `\\\\u${c.charCodeAt(0).toString(16).padStart(4, \"0\")}`);\n}\n\n/**\n * A user string as a message part: JSON-quoted by `quoteJson`, cut as JSON\n * text (never inside an escape) to its share, and never to more than `max`\n * chars.\n *\n * @internal\n */\nexport function quote(text: string, max: number = MCP_FAKES_ECHO_LIMIT): UserText {\n return jsonText(JSON.stringify(text), max);\n}\n\n/**\n * User data already rendered as JSON text (a value, or a JSON path whose keys\n * are JSON-quoted), as a message part: every raw control char (C0, DEL, C1)\n * and line separator in it escaped as `\\uXXXX` (in JSON text they can only be\n * inside its strings), cut as JSON text.\n *\n * @internal\n */\nexport function jsonText(json: string, max: number = MCP_FAKES_ECHO_LIMIT): UserText {\n return new UserText(escapeRawControls(json), \"json\", max);\n}\n\n/**\n * Text that is not JSON as a message part, cut as text (never inside a\n * surrogate pair).\n *\n * @internal\n */\nexport function plainText(text: string, max: number = MCP_FAKES_ECHO_LIMIT): UserText {\n return new UserText(text, \"text\", max);\n}\n\n/**\n * `message` built in a budget of its own (`limit` chars), as fixed text of\n * the message it goes into: a part of a message with its own cap.\n *\n * @internal\n */\nexport function within(limit: number, message: Message): Message {\n return fixed(build(message, limit));\n}\n\n/**\n * The text of `message`, no longer than `limit` when its fixed text and one\n * char per user part fit in `limit`. The fixed text is kept whole. The user\n * parts share what is left: a part that fits in an equal share keeps all of\n * it, and the others split the rest (water-filling), each at most its `max`.\n * Each user part is then cut once, by `cutText`, to its share. A `limit` of\n * `Infinity` cuts each part only to its `max`. A `NaN` limit is a\n * `RangeError`; a limit too small for the fixed text still gives each\n * non-empty user part 1 char, a bare `…`.\n *\n * @internal\n */\nexport function build(message: Message, limit = Infinity): string {\n if (Number.isNaN(limit)) throw new RangeError(\"build limit must be a number, got NaN\");\n const users = message.parts.filter((part): part is UserText => part instanceof UserText);\n let fixedLength = 0;\n for (const part of message.parts) if (typeof part === \"string\") fixedLength += part.length;\n const shares = allocate(users, limit - fixedLength);\n let i = 0;\n return message.parts\n .map((part) => (typeof part === \"string\" ? part : cutText(part.text, shares[i++], part.units)))\n .join(\"\");\n}\n\n/**\n * Each user part's share of `room` chars, by water-filling: at least 1 char\n * each (a bare `…` when room runs out), but never more than the part's cap.\n */\nfunction allocate(users: readonly UserText[], room: number): number[] {\n // `max` is at least 1 (UserText checks it), so a non-empty part's cap is too.\n const caps = users.map((u) => Math.min(u.text.length, Math.floor(u.max)));\n const order = caps.map((_, i) => i).sort((a, b) => caps[a] - caps[b]);\n const shares: number[] = new Array<number>(users.length).fill(0);\n let left = Math.max(0, room);\n order.forEach((index, k) => {\n const fair = Math.floor(left / (order.length - k));\n const share = Math.min(caps[index], Math.max(1, fair));\n shares[index] = share;\n left -= share;\n });\n return shares;\n}\n\n/**\n * The text of `message` with no cut at all: for a message that goes, as\n * text, into a user part of another message (a placeholder reason inside a\n * rendered value), which is then cut once there.\n *\n * @internal\n */\nexport function fullText(message: Message): string {\n return message.parts.map((part) => (typeof part === \"string\" ? part : part.text)).join(\"\");\n}\n"],"mappings":"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"}
|
package/dist/messages.cjs
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
const require_helpers = require('./helpers.cjs');
|
|
2
|
-
const require_router = require('./router.cjs');
|
|
3
2
|
const require_sse_writer = require('./sse-writer.cjs');
|
|
4
|
-
const require_interruption = require('./interruption.cjs');
|
|
5
3
|
const require_chaos = require('./chaos.cjs');
|
|
4
|
+
const require_router = require('./router.cjs');
|
|
6
5
|
const require_recorder = require('./recorder.cjs');
|
|
6
|
+
const require_interruption = require('./interruption.cjs');
|
|
7
7
|
|
|
8
8
|
//#region src/messages.ts
|
|
9
9
|
/**
|
|
@@ -562,7 +562,8 @@ function buildClaudeContentWithToolCallsStreamEvents(content, toolCalls, model,
|
|
|
562
562
|
blockIndex++;
|
|
563
563
|
}
|
|
564
564
|
if (blocks && blocks.length > 0) {
|
|
565
|
-
const
|
|
565
|
+
const outcome = require_helpers.resolveFixtureBlockOutcome(blocks);
|
|
566
|
+
const ordered = outcome.ordered;
|
|
566
567
|
for (const block of ordered) if (block.type === "text") {
|
|
567
568
|
events.push({
|
|
568
569
|
type: "content_block_start",
|
|
@@ -628,7 +629,7 @@ function buildClaudeContentWithToolCallsStreamEvents(content, toolCalls, model,
|
|
|
628
629
|
events.push({
|
|
629
630
|
type: "message_delta",
|
|
630
631
|
delta: {
|
|
631
|
-
stop_reason: claudeStopReason(overrides?.finishReason, "tool_use"),
|
|
632
|
+
stop_reason: claudeStopReason(overrides?.finishReason, outcome.hasToolCalls ? "tool_use" : "end_turn"),
|
|
632
633
|
stop_sequence: null
|
|
633
634
|
},
|
|
634
635
|
usage: { output_tokens: claudeUsage(overrides).output_tokens }
|
|
@@ -731,8 +732,11 @@ function buildClaudeContentWithToolCallsResponse(content, toolCalls, model, logg
|
|
|
731
732
|
input: argsObj
|
|
732
733
|
};
|
|
733
734
|
};
|
|
735
|
+
let blockHasTools;
|
|
734
736
|
if (blocks && blocks.length > 0) {
|
|
735
|
-
const
|
|
737
|
+
const outcome = require_helpers.resolveFixtureBlockOutcome(blocks);
|
|
738
|
+
const ordered = outcome.ordered;
|
|
739
|
+
blockHasTools = outcome.hasToolCalls;
|
|
736
740
|
for (const block of ordered) if (block.type === "text") contentBlocks.push({
|
|
737
741
|
type: "text",
|
|
738
742
|
text: block.text
|
|
@@ -755,7 +759,7 @@ function buildClaudeContentWithToolCallsResponse(content, toolCalls, model, logg
|
|
|
755
759
|
role: overrides?.role ?? "assistant",
|
|
756
760
|
content: contentBlocks,
|
|
757
761
|
model: overrides?.model ?? model,
|
|
758
|
-
stop_reason: claudeStopReason(overrides?.finishReason, "tool_use"),
|
|
762
|
+
stop_reason: claudeStopReason(overrides?.finishReason, blockHasTools === false ? "end_turn" : "tool_use"),
|
|
759
763
|
stop_sequence: null,
|
|
760
764
|
usage: claudeUsage(overrides)
|
|
761
765
|
};
|
|
@@ -826,6 +830,24 @@ async function handleMessages(req, res, raw, fixtures, journal, defaults, setCor
|
|
|
826
830
|
} }));
|
|
827
831
|
return;
|
|
828
832
|
}
|
|
833
|
+
const messagesError = !Array.isArray(claudeReq.messages) ? "messages must be an array" : claudeReq.messages.some((message) => !require_helpers.isJsonObject(message)) ? "messages entries must be JSON objects" : claudeReq.messages.some((message) => (message.role === "user" || message.role === "assistant") && typeof message.content === "number") ? "messages content must be a string or an array of content blocks" : null;
|
|
834
|
+
if (messagesError) {
|
|
835
|
+
journal.add({
|
|
836
|
+
method: req.method ?? "POST",
|
|
837
|
+
path: req.url ?? "/v1/messages",
|
|
838
|
+
headers: require_helpers.flattenHeaders(req.headers),
|
|
839
|
+
body: null,
|
|
840
|
+
response: {
|
|
841
|
+
status: 400,
|
|
842
|
+
fixture: null
|
|
843
|
+
}
|
|
844
|
+
});
|
|
845
|
+
require_sse_writer.writeErrorResponse(res, 400, JSON.stringify({ error: {
|
|
846
|
+
message: messagesError,
|
|
847
|
+
type: "invalid_request_error"
|
|
848
|
+
} }));
|
|
849
|
+
return;
|
|
850
|
+
}
|
|
829
851
|
const thinkingViolation = validateThinkingInvariants(claudeReq);
|
|
830
852
|
if (thinkingViolation) {
|
|
831
853
|
const effectiveStrict = require_helpers.resolveStrictMode(defaults.strict, req.headers);
|
|
@@ -854,6 +876,26 @@ async function handleMessages(req, res, raw, fixtures, journal, defaults, setCor
|
|
|
854
876
|
}
|
|
855
877
|
logger.warn(`THINKING: ${violationMessage} (strict off — replaying anyway)`);
|
|
856
878
|
}
|
|
879
|
+
if (claudeReq.tools && claudeReq.tools.length > 0) {
|
|
880
|
+
const toolsError = !Array.isArray(claudeReq.tools) ? "tools must be an array" : claudeReq.tools.some((tool) => tool === null) ? "tools entries must not be null" : null;
|
|
881
|
+
if (toolsError) {
|
|
882
|
+
journal.add({
|
|
883
|
+
method: req.method ?? "POST",
|
|
884
|
+
path: req.url ?? "/v1/messages",
|
|
885
|
+
headers: require_helpers.flattenHeaders(req.headers),
|
|
886
|
+
body: null,
|
|
887
|
+
response: {
|
|
888
|
+
status: 400,
|
|
889
|
+
fixture: null
|
|
890
|
+
}
|
|
891
|
+
});
|
|
892
|
+
require_sse_writer.writeErrorResponse(res, 400, JSON.stringify({ error: {
|
|
893
|
+
message: toolsError,
|
|
894
|
+
type: "invalid_request_error"
|
|
895
|
+
} }));
|
|
896
|
+
return;
|
|
897
|
+
}
|
|
898
|
+
}
|
|
857
899
|
const completionReq = claudeToCompletionRequest(claudeReq);
|
|
858
900
|
completionReq._context = require_helpers.getContext(req);
|
|
859
901
|
const testId = require_helpers.getTestId(req);
|
|
@@ -866,7 +908,7 @@ async function handleMessages(req, res, raw, fixtures, journal, defaults, setCor
|
|
|
866
908
|
const snippet = typeof lastUserMsg?.content === "string" ? lastUserMsg.content.slice(0, 80) : "";
|
|
867
909
|
logger.debug(`No fixture matched for request (model=${completionReq.model ?? "?"}, msg="${snippet}")`);
|
|
868
910
|
}
|
|
869
|
-
if (require_chaos.
|
|
911
|
+
if (await require_chaos.applyChaosAsync(res, fixture, defaults.chaos, req.headers, req.url, journal, {
|
|
870
912
|
method: req.method ?? "POST",
|
|
871
913
|
path: req.url ?? "/v1/messages",
|
|
872
914
|
headers: require_helpers.flattenHeaders(req.headers),
|