@copilotkit/aimock 1.43.0 → 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 +15 -0
- package/README.md +8 -2
- package/dist/agui-handler.d.ts.map +1 -1
- package/dist/agui-types.d.ts.map +1 -1
- package/dist/bedrock-converse.cjs +32 -4
- 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 +32 -4
- package/dist/bedrock-converse.js.map +1 -1
- package/dist/chaos.cjs +8 -5
- package/dist/chaos.cjs.map +1 -1
- package/dist/chaos.d.cts.map +1 -1
- package/dist/chaos.d.ts.map +1 -1
- package/dist/chaos.js +9 -6
- package/dist/chaos.js.map +1 -1
- package/dist/cli.cjs +89 -7
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +91 -9
- package/dist/cli.js.map +1 -1
- package/dist/cohere.cjs +27 -1
- package/dist/cohere.cjs.map +1 -1
- package/dist/cohere.js +27 -1
- package/dist/cohere.js.map +1 -1
- package/dist/config-loader.cjs +1 -1
- package/dist/config-loader.d.ts.map +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/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/embeddings.cjs +18 -1
- 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 +18 -1
- package/dist/embeddings.js.map +1 -1
- package/dist/files.cjs +9 -11
- package/dist/files.cjs.map +1 -1
- package/dist/files.d.cts.map +1 -1
- package/dist/files.d.ts.map +1 -1
- package/dist/files.js +10 -13
- package/dist/files.js.map +1 -1
- package/dist/fine-tuning.cjs +1 -19
- package/dist/fine-tuning.cjs.map +1 -1
- package/dist/fine-tuning.d.cts.map +1 -1
- package/dist/fine-tuning.d.ts.map +1 -1
- package/dist/fine-tuning.js +2 -20
- package/dist/fine-tuning.js.map +1 -1
- 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 +172 -11
- package/dist/fixture-loader.cjs.map +1 -1
- package/dist/fixture-loader.d.cts +95 -1
- package/dist/fixture-loader.d.cts.map +1 -1
- package/dist/fixture-loader.d.ts +95 -1
- package/dist/fixture-loader.d.ts.map +1 -1
- package/dist/fixture-loader.js +168 -12
- package/dist/fixture-loader.js.map +1 -1
- package/dist/gemini-embeddings.cjs +42 -2
- package/dist/gemini-embeddings.cjs.map +1 -1
- package/dist/gemini-embeddings.js +43 -3
- package/dist/gemini-embeddings.js.map +1 -1
- package/dist/gemini-interactions.cjs +38 -0
- 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 +38 -0
- package/dist/gemini-interactions.js.map +1 -1
- package/dist/gemini.cjs +8 -1
- 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 +8 -1
- package/dist/gemini.js.map +1 -1
- package/dist/helpers.cjs +276 -5
- package/dist/helpers.cjs.map +1 -1
- package/dist/helpers.d.cts.map +1 -1
- package/dist/helpers.d.ts.map +1 -1
- package/dist/helpers.js +275 -7
- package/dist/helpers.js.map +1 -1
- package/dist/images.cjs +3 -2
- package/dist/images.cjs.map +1 -1
- package/dist/images.js +3 -2
- package/dist/images.js.map +1 -1
- package/dist/index.cjs +30 -1
- package/dist/index.d.cts +8 -3
- package/dist/index.d.ts +8 -3
- package/dist/index.js +9 -3
- package/dist/jest.cjs +15 -10
- package/dist/jest.cjs.map +1 -1
- package/dist/jest.d.cts +4 -2
- package/dist/jest.d.cts.map +1 -1
- package/dist/jest.d.ts +4 -2
- package/dist/jest.d.ts.map +1 -1
- package/dist/jest.js +16 -11
- package/dist/jest.js.map +1 -1
- package/dist/journal.cjs +20 -0
- package/dist/journal.cjs.map +1 -1
- package/dist/journal.d.cts +16 -0
- package/dist/journal.d.cts.map +1 -1
- package/dist/journal.d.ts +16 -0
- package/dist/journal.d.ts.map +1 -1
- package/dist/journal.js +20 -0
- package/dist/journal.js.map +1 -1
- package/dist/llmock.cjs +82 -10
- package/dist/llmock.cjs.map +1 -1
- package/dist/llmock.d.cts +35 -0
- package/dist/llmock.d.cts.map +1 -1
- package/dist/llmock.d.ts +35 -0
- package/dist/llmock.d.ts.map +1 -1
- package/dist/llmock.js +83 -11
- 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 +38 -0
- package/dist/messages.cjs.map +1 -1
- package/dist/messages.js +38 -0
- package/dist/messages.js.map +1 -1
- package/dist/metrics.cjs +46 -66
- package/dist/metrics.cjs.map +1 -1
- package/dist/metrics.d.cts +14 -6
- package/dist/metrics.d.cts.map +1 -1
- package/dist/metrics.d.ts +14 -6
- package/dist/metrics.d.ts.map +1 -1
- package/dist/metrics.js +32 -40
- package/dist/metrics.js.map +1 -1
- package/dist/ollama.cjs +5 -1
- 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 +5 -1
- 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 +4 -4
- 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 +4 -4
- package/dist/openrouter-video.js.map +1 -1
- package/dist/recorder.cjs +9 -1
- 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 +9 -1
- package/dist/recorder.js.map +1 -1
- package/dist/responses.cjs +43 -4
- 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 +43 -4
- 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 +1 -1
- package/dist/router.cjs.map +1 -1
- package/dist/router.js +1 -1
- package/dist/router.js.map +1 -1
- package/dist/server.cjs +590 -191
- package/dist/server.cjs.map +1 -1
- package/dist/server.d.cts +4 -1
- package/dist/server.d.cts.map +1 -1
- package/dist/server.d.ts +4 -1
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +498 -99
- package/dist/server.js.map +1 -1
- package/dist/speech.cjs +4 -2
- package/dist/speech.cjs.map +1 -1
- package/dist/speech.js +4 -2
- package/dist/speech.js.map +1 -1
- package/dist/suite.cjs +1 -1
- package/dist/suite.js +1 -1
- package/dist/transcription.cjs +2 -2
- 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 +2 -2
- 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 +202 -2
- package/dist/types.d.cts.map +1 -1
- package/dist/types.d.ts +202 -2
- 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 +89 -31
- package/dist/validate-cli.cjs.map +1 -1
- package/dist/validate-cli.js +91 -33
- package/dist/validate-cli.js.map +1 -1
- 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/video.cjs +19 -0
- package/dist/video.cjs.map +1 -1
- package/dist/video.d.cts.map +1 -1
- package/dist/video.d.ts.map +1 -1
- package/dist/video.js +19 -0
- package/dist/video.js.map +1 -1
- package/dist/vitest.cjs +15 -10
- package/dist/vitest.cjs.map +1 -1
- package/dist/vitest.d.cts +4 -2
- package/dist/vitest.d.cts.map +1 -1
- package/dist/vitest.d.ts +4 -2
- package/dist/vitest.d.ts.map +1 -1
- package/dist/vitest.js +16 -11
- package/dist/vitest.js.map +1 -1
- package/dist/ws-gemini-live.d.ts +2 -2
- package/dist/ws-realtime.d.ts +2 -2
- package/dist/ws-responses.cjs +14 -2
- package/dist/ws-responses.cjs.map +1 -1
- package/dist/ws-responses.js +14 -2
- package/dist/ws-responses.js.map +1 -1
- 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,25 @@ 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
|
+
}
|
|
29
50
|
/**
|
|
30
51
|
* Would a fixture miss on this request have been forwarded upstream? This is
|
|
31
52
|
* the ONE rule behind the no-fixture chaos journal `source` label: "proxy"
|
|
@@ -1071,11 +1092,14 @@ function matchesPattern(text, pattern) {
|
|
|
1071
1092
|
return result;
|
|
1072
1093
|
}
|
|
1073
1094
|
/**
|
|
1074
|
-
*
|
|
1075
|
-
*
|
|
1076
|
-
* `
|
|
1077
|
-
*
|
|
1078
|
-
*
|
|
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.
|
|
1079
1103
|
*/
|
|
1080
1104
|
function resolveTestId(headers, url) {
|
|
1081
1105
|
const headerValue = headers["x-test-id"];
|
|
@@ -1099,6 +1123,237 @@ function getContext(req) {
|
|
|
1099
1123
|
} else if (typeof headerValue === "string" && headerValue) return headerValue;
|
|
1100
1124
|
}
|
|
1101
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
|
+
/**
|
|
1102
1357
|
* Convert a test ID (e.g. Playwright titlePath) into a filesystem-safe slug
|
|
1103
1358
|
* suitable for use as a directory name in snapshot-style recording.
|
|
1104
1359
|
*/
|
|
@@ -1329,6 +1584,19 @@ function describeMatch(match, index) {
|
|
|
1329
1584
|
const cut = head.lastIndexOf(", ");
|
|
1330
1585
|
return `${cut > 0 ? head.slice(0, cut) : head}, … }`;
|
|
1331
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
|
+
}
|
|
1332
1600
|
|
|
1333
1601
|
//#endregion
|
|
1334
1602
|
exports.FORMAT_TO_CONTENT_TYPE = FORMAT_TO_CONTENT_TYPE;
|
|
@@ -1342,6 +1610,7 @@ exports.buildTextCompletion = buildTextCompletion;
|
|
|
1342
1610
|
exports.buildToolCallChunks = buildToolCallChunks;
|
|
1343
1611
|
exports.buildToolCallCompletion = buildToolCallCompletion;
|
|
1344
1612
|
exports.buildUsageChunk = buildUsageChunk;
|
|
1613
|
+
exports.decodeMcpHeaderValue = decodeMcpHeaderValue;
|
|
1345
1614
|
exports.describeMatch = describeMatch;
|
|
1346
1615
|
exports.extractOverrides = extractOverrides;
|
|
1347
1616
|
exports.flattenHeaders = flattenHeaders;
|
|
@@ -1371,10 +1640,12 @@ exports.markMintedRequestId = markMintedRequestId;
|
|
|
1371
1640
|
exports.matchesPattern = matchesPattern;
|
|
1372
1641
|
exports.normalizeEmbeddingInput = normalizeEmbeddingInput;
|
|
1373
1642
|
exports.normalizeTextInput = normalizeTextInput;
|
|
1643
|
+
exports.parseStrictIntegerText = parseStrictIntegerText;
|
|
1374
1644
|
exports.readBody = readBody;
|
|
1375
1645
|
exports.readBodyBufferBounded = readBodyBufferBounded;
|
|
1376
1646
|
exports.resolveFixtureBlockOutcome = resolveFixtureBlockOutcome;
|
|
1377
1647
|
exports.resolveFixtureBlocks = resolveFixtureBlocks;
|
|
1648
|
+
exports.resolveMcpIdentity = resolveMcpIdentity;
|
|
1378
1649
|
exports.resolveReasoningArtifactsForModel = resolveReasoningArtifactsForModel;
|
|
1379
1650
|
exports.resolveReasoningForModel = resolveReasoningForModel;
|
|
1380
1651
|
exports.resolveRequestId = resolveRequestId;
|