@copilotkit/aimock 1.42.0 → 1.43.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 +56 -0
- package/README.md +102 -16
- 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 +12 -8
- 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 +14 -10
- 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 +622 -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 +613 -74
- package/dist/chaos.js.map +1 -1
- package/dist/cli.cjs +42 -23
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +42 -23
- package/dist/cli.js.map +1 -1
- package/dist/cohere.cjs +10 -8
- 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 +12 -10
- package/dist/cohere.js.map +1 -1
- package/dist/config-loader.d.cts.map +1 -1
- package/dist/config-loader.d.ts.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/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 +8 -4
- package/dist/embeddings.cjs.map +1 -1
- package/dist/embeddings.d.cts.map +1 -1
- package/dist/embeddings.d.ts.map +1 -1
- package/dist/embeddings.js +9 -5
- 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 +1071 -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 +1059 -0
- package/dist/files.js.map +1 -0
- package/dist/fine-tuning.cjs +1051 -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 +1044 -0
- package/dist/fine-tuning.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 +37 -5
- package/dist/fixture-loader.d.cts.map +1 -1
- package/dist/fixture-loader.d.ts +37 -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 +2 -2
- package/dist/gemini-embeddings.cjs.map +1 -1
- package/dist/gemini-embeddings.js +3 -3
- package/dist/gemini-embeddings.js.map +1 -1
- package/dist/gemini-interactions.cjs +13 -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 +15 -9
- package/dist/gemini-interactions.js.map +1 -1
- package/dist/gemini.cjs +11 -7
- 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 +13 -9
- 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 +229 -7
- 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 +221 -8
- package/dist/helpers.js.map +1 -1
- package/dist/images.cjs +54 -123
- 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 +55 -124
- package/dist/images.js.map +1 -1
- package/dist/index.cjs +41 -8
- package/dist/index.d.cts +12 -7
- package/dist/index.d.ts +12 -7
- package/dist/index.js +14 -10
- package/dist/jest.cjs +14 -7
- package/dist/jest.cjs.map +1 -1
- package/dist/jest.d.cts +3 -2
- package/dist/jest.d.cts.map +1 -1
- package/dist/jest.d.ts +3 -2
- package/dist/jest.d.ts.map +1 -1
- package/dist/jest.js +14 -7
- package/dist/jest.js.map +1 -1
- package/dist/journal.cjs +41 -4
- package/dist/journal.cjs.map +1 -1
- package/dist/journal.d.cts +44 -3
- package/dist/journal.d.cts.map +1 -1
- package/dist/journal.d.ts +44 -3
- package/dist/journal.d.ts.map +1 -1
- package/dist/journal.js +41 -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 +52 -41
- package/dist/llmock.cjs.map +1 -1
- package/dist/llmock.d.cts +16 -5
- package/dist/llmock.d.cts.map +1 -1
- package/dist/llmock.d.ts +16 -5
- package/dist/llmock.d.ts.map +1 -1
- package/dist/llmock.js +53 -42
- package/dist/llmock.js.map +1 -1
- package/dist/messages.cjs +11 -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 +13 -9
- package/dist/messages.js.map +1 -1
- package/dist/metrics.cjs +258 -5
- package/dist/metrics.cjs.map +1 -1
- package/dist/metrics.d.cts +3 -1
- package/dist/metrics.d.cts.map +1 -1
- package/dist/metrics.d.ts +3 -1
- package/dist/metrics.d.ts.map +1 -1
- package/dist/metrics.js +255 -6
- 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 +5 -5
- package/dist/ollama.cjs.map +1 -1
- package/dist/ollama.js +6 -6
- package/dist/ollama.js.map +1 -1
- package/dist/openrouter-video.cjs +5 -5
- package/dist/openrouter-video.cjs.map +1 -1
- package/dist/openrouter-video.js +6 -6
- package/dist/openrouter-video.js.map +1 -1
- package/dist/recorder.cjs +45 -9
- 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 +43 -11
- package/dist/recorder.js.map +1 -1
- package/dist/responses.cjs +4 -3
- package/dist/responses.cjs.map +1 -1
- package/dist/responses.js +5 -5
- package/dist/responses.js.map +1 -1
- package/dist/router.cjs +20 -33
- 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 +20 -34
- package/dist/router.js.map +1 -1
- package/dist/server.cjs +701 -625
- package/dist/server.cjs.map +1 -1
- package/dist/server.d.cts +1 -0
- package/dist/server.d.cts.map +1 -1
- package/dist/server.d.ts +1 -0
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +704 -628
- package/dist/server.js.map +1 -1
- package/dist/speech.cjs +2 -2
- package/dist/speech.cjs.map +1 -1
- package/dist/speech.js +3 -3
- package/dist/speech.js.map +1 -1
- package/dist/transcription.cjs +4 -4
- package/dist/transcription.cjs.map +1 -1
- package/dist/transcription.js +5 -5
- package/dist/transcription.js.map +1 -1
- package/dist/types.d.cts +119 -16
- package/dist/types.d.cts.map +1 -1
- package/dist/types.d.ts +119 -16
- package/dist/types.d.ts.map +1 -1
- package/dist/validate-cli.cjs +925 -0
- package/dist/validate-cli.cjs.map +1 -0
- package/dist/validate-cli.js +924 -0
- package/dist/validate-cli.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 +4 -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 +5 -5
- package/dist/video.js.map +1 -1
- package/dist/vitest.cjs +13 -6
- package/dist/vitest.cjs.map +1 -1
- package/dist/vitest.d.cts +2 -1
- package/dist/vitest.d.cts.map +1 -1
- package/dist/vitest.d.ts +2 -1
- package/dist/vitest.d.ts.map +1 -1
- package/dist/vitest.js +14 -7
- 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.d.ts +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 +2 -2
- 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 +2 -2
- package/dist/ws-responses.js +2 -2
- 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 +1 -1
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
import { validateLiveTranscript } from "./live-fixture.js";
|
|
2
|
+
|
|
3
|
+
//#region src/live-sanitize.ts
|
|
4
|
+
var LiveUnsafeExportError = class extends Error {
|
|
5
|
+
category = "unsafe-export";
|
|
6
|
+
constructor() {
|
|
7
|
+
super("unsafe-export: Live transcript cannot be retained safely");
|
|
8
|
+
this.name = "LiveUnsafeExportError";
|
|
9
|
+
}
|
|
10
|
+
};
|
|
11
|
+
const credentialKey = /^(?:authorization|proxy-authorization|cookie|set-cookie|x-api-key|api[_-]?key|access[_-]?token|refresh[_-]?token|client[_-]?secret|password|secret|token|sec-websocket-protocol)$/i;
|
|
12
|
+
const semanticKey = new Set([
|
|
13
|
+
"instructions",
|
|
14
|
+
"content",
|
|
15
|
+
"delta",
|
|
16
|
+
"audio",
|
|
17
|
+
"arguments",
|
|
18
|
+
"output",
|
|
19
|
+
"input",
|
|
20
|
+
"tools",
|
|
21
|
+
"parameters",
|
|
22
|
+
"client",
|
|
23
|
+
"text",
|
|
24
|
+
"thinking"
|
|
25
|
+
]);
|
|
26
|
+
const metadataKey = new Set([
|
|
27
|
+
"metadata",
|
|
28
|
+
"headers",
|
|
29
|
+
"diagnostics"
|
|
30
|
+
]);
|
|
31
|
+
const unsafeKey = new Set([
|
|
32
|
+
"__proto__",
|
|
33
|
+
"constructor",
|
|
34
|
+
"prototype"
|
|
35
|
+
]);
|
|
36
|
+
const redacted = "[REDACTED]";
|
|
37
|
+
function fail() {
|
|
38
|
+
throw new LiveUnsafeExportError();
|
|
39
|
+
}
|
|
40
|
+
function decodeUrlComponent(value) {
|
|
41
|
+
return new URLSearchParams(`value=${value.replace(/\+/g, "%2B").replace(/&/g, "%26")}`).get("value");
|
|
42
|
+
}
|
|
43
|
+
function escapePointer(key) {
|
|
44
|
+
return key.replace(/~/g, "~0").replace(/\//g, "~1");
|
|
45
|
+
}
|
|
46
|
+
function parts(pointer) {
|
|
47
|
+
if (!pointer.startsWith("/") || /~(?![01])/u.test(pointer)) fail();
|
|
48
|
+
const keys = pointer.slice(1).split("/").map((key) => key.replace(/~1/g, "/").replace(/~0/g, "~"));
|
|
49
|
+
if (keys.some((key) => unsafeKey.has(key))) fail();
|
|
50
|
+
return keys;
|
|
51
|
+
}
|
|
52
|
+
function child(value, key) {
|
|
53
|
+
if (!value || typeof value !== "object" || !Object.hasOwn(value, key)) fail();
|
|
54
|
+
if (Array.isArray(value)) {
|
|
55
|
+
if (!/^(0|[1-9]\d*)$/.test(key)) fail();
|
|
56
|
+
return value[Number(key)];
|
|
57
|
+
}
|
|
58
|
+
return value[key];
|
|
59
|
+
}
|
|
60
|
+
function setIdentifier(event, pointer, value) {
|
|
61
|
+
const keys = parts(pointer);
|
|
62
|
+
const last = keys.pop();
|
|
63
|
+
if (last === void 0) fail();
|
|
64
|
+
let parent = event;
|
|
65
|
+
for (const key of keys) parent = child(parent, key);
|
|
66
|
+
if (typeof child(parent, last) !== "string" || !parent || typeof parent !== "object") fail();
|
|
67
|
+
if (Array.isArray(parent)) parent[Number(last)] = value;
|
|
68
|
+
else parent[last] = value;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Sanitize a separately retained successful capture; never mutate the relay.
|
|
72
|
+
* Validation supplies the reviewed identifier descriptors and causal checks.
|
|
73
|
+
* Credential descriptors supplement the built-in policy, not permission to
|
|
74
|
+
* delete arbitrary protocol operands. Ambiguous secret-bearing data fails closed.
|
|
75
|
+
*/
|
|
76
|
+
function sanitizeLiveTranscript(transcript, secretValues = [], credentialFields = [], validationOptions = {}) {
|
|
77
|
+
try {
|
|
78
|
+
const result = validateLiveTranscript(transcript, validationOptions);
|
|
79
|
+
if (!Array.isArray(secretValues) || secretValues.some((s) => typeof s !== "string")) fail();
|
|
80
|
+
const secrets = [...new Set(secretValues.filter((s) => s.length))].sort((a, b) => b.length - a.length);
|
|
81
|
+
const hasSecret = (value) => secrets.some((secret) => value.includes(secret));
|
|
82
|
+
const replaceSecrets = (value) => {
|
|
83
|
+
for (const secret of secrets) value = value.split(secret).join(redacted);
|
|
84
|
+
return value;
|
|
85
|
+
};
|
|
86
|
+
const descriptors = new Set(credentialFields.map((field) => {
|
|
87
|
+
parts(field.pointer);
|
|
88
|
+
return field.pointer;
|
|
89
|
+
}));
|
|
90
|
+
const handled = /* @__PURE__ */ new Set();
|
|
91
|
+
const originalIds = /* @__PURE__ */ new Set();
|
|
92
|
+
for (const binding of result.bindings) {
|
|
93
|
+
let value = result.entries[binding.entry].event;
|
|
94
|
+
for (const key of parts(binding.pointer)) value = child(value, key);
|
|
95
|
+
if (typeof value !== "string") fail();
|
|
96
|
+
originalIds.add(value);
|
|
97
|
+
}
|
|
98
|
+
let nextId = 1;
|
|
99
|
+
const identities = /* @__PURE__ */ new Map();
|
|
100
|
+
for (const binding of result.bindings) {
|
|
101
|
+
let identity = identities.get(binding.name);
|
|
102
|
+
if (!identity) {
|
|
103
|
+
const index = identities.size + 1;
|
|
104
|
+
while (originalIds.has(`live-id-${nextId}`)) nextId++;
|
|
105
|
+
identity = {
|
|
106
|
+
name: `binding-${index}`,
|
|
107
|
+
value: `live-id-${nextId++}`
|
|
108
|
+
};
|
|
109
|
+
identities.set(binding.name, identity);
|
|
110
|
+
}
|
|
111
|
+
setIdentifier(result.entries[binding.entry].event, binding.pointer, identity.value);
|
|
112
|
+
binding.name = identity.name;
|
|
113
|
+
}
|
|
114
|
+
function sanitizeUrl(value) {
|
|
115
|
+
if (!/^[a-z][a-z0-9+.-]*:\/\//i.test(value)) return value;
|
|
116
|
+
let url;
|
|
117
|
+
try {
|
|
118
|
+
url = new URL(value);
|
|
119
|
+
} catch {
|
|
120
|
+
return value;
|
|
121
|
+
}
|
|
122
|
+
if (hasSecret(decodeUrlComponent(url.pathname))) fail();
|
|
123
|
+
let changed = false;
|
|
124
|
+
if (url.username || url.password) {
|
|
125
|
+
url.username = "";
|
|
126
|
+
url.password = "";
|
|
127
|
+
changed = true;
|
|
128
|
+
}
|
|
129
|
+
for (const [key, val] of [...url.searchParams]) if (credentialKey.test(key) || hasSecret(key) || hasSecret(val)) {
|
|
130
|
+
url.searchParams.delete(key);
|
|
131
|
+
changed = true;
|
|
132
|
+
}
|
|
133
|
+
const decodedHash = decodeUrlComponent(url.hash);
|
|
134
|
+
if (url.hash && (hasSecret(decodedHash) || /(?:token|secret|password|api[_-]?key)=/i.test(decodedHash))) {
|
|
135
|
+
url.hash = "";
|
|
136
|
+
changed = true;
|
|
137
|
+
}
|
|
138
|
+
return changed ? url.href : value;
|
|
139
|
+
}
|
|
140
|
+
function providerMetadata(pointer) {
|
|
141
|
+
const match = /^\/entries\/(\d+)\/event\/event\/(?:response\/(?:user|prompt_cache_key|safety_identifier)|item\/encrypted_content)$/.exec(pointer);
|
|
142
|
+
if (!match) return false;
|
|
143
|
+
const event = result.entries[Number(match[1])].event;
|
|
144
|
+
const nested = event.event;
|
|
145
|
+
if (event.type !== "response.event" || !nested || typeof nested !== "object" || Array.isArray(nested)) return false;
|
|
146
|
+
if (!pointer.endsWith("/item/encrypted_content")) return [
|
|
147
|
+
"response.created",
|
|
148
|
+
"response.in_progress",
|
|
149
|
+
"response.completed"
|
|
150
|
+
].includes(String(nested.type));
|
|
151
|
+
const item = nested.item;
|
|
152
|
+
return ["response.output_item.added", "response.output_item.done"].includes(String(nested.type)) && !!item && typeof item === "object" && !Array.isArray(item) && item.type === "reasoning";
|
|
153
|
+
}
|
|
154
|
+
function visit(value, path, metadata = false, semantic = false) {
|
|
155
|
+
if (typeof value === "string") {
|
|
156
|
+
const url = sanitizeUrl(value);
|
|
157
|
+
if ((hasSecret(value) || url !== value) && (!metadata || semantic)) fail();
|
|
158
|
+
return metadata && !semantic ? replaceSecrets(url) : value;
|
|
159
|
+
}
|
|
160
|
+
if (Array.isArray(value)) return value.map((item, i) => visit(item, `${path}/${i}`, metadata, semantic));
|
|
161
|
+
if (!value || typeof value !== "object") return value;
|
|
162
|
+
const output = {};
|
|
163
|
+
for (const [key, item] of Object.entries(value)) {
|
|
164
|
+
const pointer = `${path}/${escapePointer(key)}`;
|
|
165
|
+
if (providerMetadata(pointer) && (typeof item === "string" || item === null)) {
|
|
166
|
+
if (descriptors.has(pointer)) handled.add(pointer);
|
|
167
|
+
else output[key] = typeof item === "string" ? redacted : item;
|
|
168
|
+
continue;
|
|
169
|
+
}
|
|
170
|
+
const protectedBranch = semantic || semanticKey.has(key);
|
|
171
|
+
const removable = !protectedBranch && (metadata || credentialKey.test(key));
|
|
172
|
+
if (descriptors.has(pointer)) {
|
|
173
|
+
if (!removable) fail();
|
|
174
|
+
handled.add(pointer);
|
|
175
|
+
continue;
|
|
176
|
+
}
|
|
177
|
+
if (!semantic && credentialKey.test(key)) continue;
|
|
178
|
+
if (hasSecret(key)) {
|
|
179
|
+
if (!metadata || protectedBranch) fail();
|
|
180
|
+
continue;
|
|
181
|
+
}
|
|
182
|
+
output[key] = visit(item, pointer, metadata || metadataKey.has(key), protectedBranch);
|
|
183
|
+
}
|
|
184
|
+
return output;
|
|
185
|
+
}
|
|
186
|
+
const cleaned = visit(result, "");
|
|
187
|
+
if (handled.size !== descriptors.size || hasSecret(JSON.stringify(cleaned))) fail();
|
|
188
|
+
return validateLiveTranscript(cleaned, validationOptions);
|
|
189
|
+
} catch {
|
|
190
|
+
fail();
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
//#endregion
|
|
195
|
+
export { sanitizeLiveTranscript };
|
|
196
|
+
//# sourceMappingURL=live-sanitize.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"live-sanitize.js","names":[],"sources":["../src/live-sanitize.ts"],"sourcesContent":["import { validateLiveTranscript } from \"./live-fixture.js\";\nimport type { LiveJson, LiveObject, LiveOptions, LiveTranscript } from \"./live-types.js\";\n\n/** Exact transcript-rooted pointer to an observed removable credential metadata field. */\nexport interface LiveCredentialField {\n readonly pointer: string;\n}\n\nexport class LiveUnsafeExportError extends Error {\n readonly category = \"unsafe-export\";\n constructor() {\n // Never reflect untrusted values, keys, pointers, or validation errors.\n super(\"unsafe-export: Live transcript cannot be retained safely\");\n this.name = \"LiveUnsafeExportError\";\n }\n}\n\nconst credentialKey =\n /^(?:authorization|proxy-authorization|cookie|set-cookie|x-api-key|api[_-]?key|access[_-]?token|refresh[_-]?token|client[_-]?secret|password|secret|token|sec-websocket-protocol)$/i;\n// These branches carry conversation, tool schema, or PCM semantics. A key named\n// \"authorization\" inside tool parameters is a schema property, not a header.\nconst semanticKey = new Set([\n \"instructions\",\n \"content\",\n \"delta\",\n \"audio\",\n \"arguments\",\n \"output\",\n \"input\",\n \"tools\",\n \"parameters\",\n \"client\",\n \"text\",\n \"thinking\",\n]);\nconst metadataKey = new Set([\"metadata\", \"headers\", \"diagnostics\"]);\nconst unsafeKey = new Set([\"__proto__\", \"constructor\", \"prototype\"]);\nconst redacted = \"[REDACTED]\";\nfunction fail(): never {\n throw new LiveUnsafeExportError();\n}\nfunction decodeUrlComponent(value: string): string {\n // Tolerate malformed escapes while exposing encoded secrets. Protect literal\n // form separators so the entire component remains one value.\n return new URLSearchParams(`value=${value.replace(/\\+/g, \"%2B\").replace(/&/g, \"%26\")}`).get(\n \"value\",\n )!;\n}\nfunction escapePointer(key: string): string {\n return key.replace(/~/g, \"~0\").replace(/\\//g, \"~1\");\n}\nfunction parts(pointer: string): string[] {\n if (!pointer.startsWith(\"/\") || /~(?![01])/u.test(pointer)) fail();\n const keys = pointer\n .slice(1)\n .split(\"/\")\n .map((key) => key.replace(/~1/g, \"/\").replace(/~0/g, \"~\"));\n if (keys.some((key) => unsafeKey.has(key))) fail();\n return keys;\n}\nfunction child(value: LiveJson, key: string): LiveJson {\n if (!value || typeof value !== \"object\" || !Object.hasOwn(value, key)) fail();\n if (Array.isArray(value)) {\n if (!/^(0|[1-9]\\d*)$/.test(key)) fail();\n return value[Number(key)];\n }\n return value[key];\n}\nfunction setIdentifier(event: LiveObject, pointer: string, value: string): void {\n const keys = parts(pointer);\n const last = keys.pop();\n if (last === undefined) fail();\n let parent: LiveJson = event;\n for (const key of keys) parent = child(parent, key);\n if (typeof child(parent, last) !== \"string\" || !parent || typeof parent !== \"object\") fail();\n if (Array.isArray(parent)) parent[Number(last)] = value;\n else parent[last] = value;\n}\n\n/**\n * Sanitize a separately retained successful capture; never mutate the relay.\n * Validation supplies the reviewed identifier descriptors and causal checks.\n * Credential descriptors supplement the built-in policy, not permission to\n * delete arbitrary protocol operands. Ambiguous secret-bearing data fails closed.\n */\nexport function sanitizeLiveTranscript(\n transcript: LiveTranscript,\n secretValues: readonly string[] = [],\n credentialFields: readonly LiveCredentialField[] = [],\n validationOptions: Omit<LiveOptions, \"secretValues\"> = {},\n): LiveTranscript {\n try {\n const result = validateLiveTranscript(transcript, validationOptions);\n if (!Array.isArray(secretValues) || secretValues.some((s) => typeof s !== \"string\")) fail();\n const secrets = [...new Set(secretValues.filter((s) => s.length))].sort(\n (a, b) => b.length - a.length,\n );\n const hasSecret = (value: string) => secrets.some((secret) => value.includes(secret));\n const replaceSecrets = (value: string) => {\n for (const secret of secrets) value = value.split(secret).join(redacted);\n return value;\n };\n const descriptors = new Set(\n credentialFields.map((field) => {\n parts(field.pointer);\n return field.pointer;\n }),\n );\n const handled = new Set<string>();\n // Names themselves may have been derived from provider IDs. Replace both\n // the annotation names and only their explicitly annotated operand values.\n const originalIds = new Set<string>();\n for (const binding of result.bindings) {\n let value: LiveJson = result.entries[binding.entry].event;\n for (const key of parts(binding.pointer)) value = child(value, key);\n if (typeof value !== \"string\") fail();\n originalIds.add(value);\n }\n let nextId = 1;\n const identities = new Map<string, { name: string; value: string }>();\n for (const binding of result.bindings) {\n let identity = identities.get(binding.name);\n if (!identity) {\n const index = identities.size + 1;\n while (originalIds.has(`live-id-${nextId}`)) nextId++;\n identity = { name: `binding-${index}`, value: `live-id-${nextId++}` };\n identities.set(binding.name, identity);\n }\n setIdentifier(result.entries[binding.entry].event, binding.pointer, identity.value);\n binding.name = identity.name;\n }\n function sanitizeUrl(value: string): string {\n // Leave non-URLs and clean URLs byte-for-byte intact.\n if (!/^[a-z][a-z0-9+.-]*:\\/\\//i.test(value)) return value;\n let url: URL;\n try {\n url = new URL(value);\n } catch {\n return value;\n }\n // A path identifies the resource; changing it could change replay semantics.\n if (hasSecret(decodeUrlComponent(url.pathname))) fail();\n let changed = false;\n if (url.username || url.password) {\n url.username = \"\";\n url.password = \"\";\n changed = true;\n }\n for (const [key, val] of [...url.searchParams]) {\n if (credentialKey.test(key) || hasSecret(key) || hasSecret(val)) {\n url.searchParams.delete(key);\n changed = true;\n }\n }\n const decodedHash = decodeUrlComponent(url.hash);\n if (\n url.hash &&\n (hasSecret(decodedHash) || /(?:token|secret|password|api[_-]?key)=/i.test(decodedHash))\n ) {\n url.hash = \"\";\n changed = true;\n }\n return changed ? url.href : value;\n }\n function providerMetadata(pointer: string): boolean {\n const match =\n /^\\/entries\\/(\\d+)\\/event\\/event\\/(?:response\\/(?:user|prompt_cache_key|safety_identifier)|item\\/encrypted_content)$/.exec(\n pointer,\n );\n if (!match) return false;\n const event = result.entries[Number(match[1])].event;\n const nested = event.event;\n if (\n event.type !== \"response.event\" ||\n !nested ||\n typeof nested !== \"object\" ||\n Array.isArray(nested)\n )\n return false;\n if (!pointer.endsWith(\"/item/encrypted_content\")) {\n return [\"response.created\", \"response.in_progress\", \"response.completed\"].includes(\n String(nested.type),\n );\n }\n const item = nested.item;\n return (\n [\"response.output_item.added\", \"response.output_item.done\"].includes(String(nested.type)) &&\n !!item &&\n typeof item === \"object\" &&\n !Array.isArray(item) &&\n item.type === \"reasoning\"\n );\n }\n function visit(value: LiveJson, path: string, metadata = false, semantic = false): LiveJson {\n if (typeof value === \"string\") {\n const url = sanitizeUrl(value);\n if ((hasSecret(value) || url !== value) && (!metadata || semantic)) fail();\n return metadata && !semantic ? replaceSecrets(url) : value;\n }\n if (Array.isArray(value))\n return value.map((item, i) => visit(item, `${path}/${i}`, metadata, semantic));\n if (!value || typeof value !== \"object\") return value;\n const output: LiveObject = {};\n for (const [key, item] of Object.entries(value)) {\n const pointer = `${path}/${escapePointer(key)}`;\n // Exact retained metadata sites from the reviewed v3 sanitation\n // evidence. Never infer these by equal values or similar field names.\n if (providerMetadata(pointer) && (typeof item === \"string\" || item === null)) {\n if (descriptors.has(pointer)) handled.add(pointer);\n else output[key] = typeof item === \"string\" ? redacted : item;\n continue;\n }\n const protectedBranch = semantic || semanticKey.has(key);\n const removable = !protectedBranch && (metadata || credentialKey.test(key));\n if (descriptors.has(pointer)) {\n if (!removable) fail();\n handled.add(pointer);\n continue;\n }\n if (!semantic && credentialKey.test(key)) continue;\n if (hasSecret(key)) {\n if (!metadata || protectedBranch) fail();\n continue;\n }\n output[key] = visit(item, pointer, metadata || metadataKey.has(key), protectedBranch);\n }\n return output;\n }\n // Validation clones all bounded JSON, including unknown metadata. Preserve\n // those fields, then validate again to catch any semantic deletion.\n const cleaned = visit(result as LiveTranscript & LiveObject, \"\");\n if (handled.size !== descriptors.size || hasSecret(JSON.stringify(cleaned))) fail();\n return validateLiveTranscript(cleaned, validationOptions);\n } catch {\n fail();\n }\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
//#region src/live-types.d.ts
|
|
2
|
+
type LiveJson = null | boolean | number | string | LiveJson[] | {
|
|
3
|
+
[key: string]: LiveJson;
|
|
4
|
+
};
|
|
5
|
+
type LiveObject = {
|
|
6
|
+
[key: string]: LiveJson;
|
|
7
|
+
};
|
|
8
|
+
type LiveMode = "client" | "managed";
|
|
9
|
+
interface LivePosition {
|
|
10
|
+
command: number;
|
|
11
|
+
audioBytes: number;
|
|
12
|
+
}
|
|
13
|
+
interface LiveBinding {
|
|
14
|
+
entry: number;
|
|
15
|
+
pointer: string;
|
|
16
|
+
name: string;
|
|
17
|
+
action: "define" | "reference";
|
|
18
|
+
owner: "client" | "server";
|
|
19
|
+
origin?: "observed" | "imported-context";
|
|
20
|
+
}
|
|
21
|
+
interface LiveEntry {
|
|
22
|
+
direction: "client" | "server";
|
|
23
|
+
atMs: number;
|
|
24
|
+
event: LiveObject;
|
|
25
|
+
barrier?: LivePosition;
|
|
26
|
+
}
|
|
27
|
+
interface LiveTranscript {
|
|
28
|
+
version: 1;
|
|
29
|
+
model: "gpt-live-1";
|
|
30
|
+
mode: LiveMode;
|
|
31
|
+
audio: {
|
|
32
|
+
encoding: "pcm16le";
|
|
33
|
+
sampleRateHz: 24000;
|
|
34
|
+
channels: 1;
|
|
35
|
+
};
|
|
36
|
+
configuration: LiveObject;
|
|
37
|
+
entries: LiveEntry[];
|
|
38
|
+
bindings: LiveBinding[];
|
|
39
|
+
capture: {
|
|
40
|
+
source: "authored" | "provider";
|
|
41
|
+
complete: true;
|
|
42
|
+
terminalEntry: number;
|
|
43
|
+
provenance?: {
|
|
44
|
+
host: string;
|
|
45
|
+
path: "/v1/live/sessions";
|
|
46
|
+
observedAt: string;
|
|
47
|
+
referenceVersion: string;
|
|
48
|
+
sanitationVersion: 1;
|
|
49
|
+
};
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
interface LiveFixtureResponse {
|
|
53
|
+
live: LiveTranscript;
|
|
54
|
+
liveTiming?: "recorded" | "immediate";
|
|
55
|
+
}
|
|
56
|
+
interface LiveOptions {
|
|
57
|
+
maxSessions?: number;
|
|
58
|
+
maxMessageBytes?: number;
|
|
59
|
+
maxBufferedBytes?: number;
|
|
60
|
+
maxWriteBytes?: number;
|
|
61
|
+
maxDecodedAudioBytes?: number;
|
|
62
|
+
maxDurationMs?: number;
|
|
63
|
+
idleTimeoutMs?: number;
|
|
64
|
+
mismatchTimeoutMs?: number;
|
|
65
|
+
secretValues?: string[];
|
|
66
|
+
}
|
|
67
|
+
type LiveFailureCategory = "invalid-client" | "fixture-mismatch" | "protocol-divergence" | "upstream-auth" | "upstream-access" | "upstream-connect" | "timeout" | "resource-limit" | "incomplete-capture" | "unsafe-export";
|
|
68
|
+
//# sourceMappingURL=live-types.d.ts.map
|
|
69
|
+
//#endregion
|
|
70
|
+
export { LiveBinding, LiveEntry, LiveFailureCategory, LiveFixtureResponse, LiveJson, LiveMode, LiveObject, LiveOptions, LivePosition, LiveTranscript };
|
|
71
|
+
//# sourceMappingURL=live-types.d.cts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"live-types.d.cts","names":[],"sources":["../src/live-types.ts"],"sourcesContent":[],"mappings":";KAAY,QAAA,sCAA8C;EAA9C,CAAA,GAAA,EAAA,MAAQ,CAAA,EAAoE,QAApE;CAAA;AAAsC,KAC9C,UAAA,GAD8C;MAA8B,EAAA,MAAA,CAAA,EAC9C,QAD8C;CAAQ;AACpF,KACA,QAAA,GADU,QAAoB,GAAA,SAAQ;AACtC,UACK,YAAA,CADG;EACH,OAAA,EAAA,MAAY;EAIZ,UAAA,EAAA,MAAW;AAQ5B;AAA0B,UART,WAAA,CAQS;OAGjB,EAAA,MAAA;SACG,EAAA,MAAA;EAAY,IAAA,EAAA,MAAA;EAEP,MAAA,EAAA,QAAA,GAAc,WAAA;EAAA,KAAA,EAAA,QAAA,GAAA,QAAA;QAGvB,CAAA,EAAA,UAAA,GAAA,kBAAA;;AAGG,UAZM,SAAA,CAYN;WACC,EAAA,QAAA,GAAA,QAAA;EAAW,IAAA,EAAA,MAAA;EAcN,KAAA,EAxBR,UAwBQ;EAIA,OAAA,CAAA,EA3BL,YA2BgB;AAW5B;UApCiB,cAAA;;;QAGT;;;;;;iBAES;WACN;YACC;;;;;;;;;;;;;;UAcK,mBAAA;QACT;;;UAGS,WAAA;;;;;;;;;;;KAWL,mBAAA"}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
//#region src/live-types.d.ts
|
|
2
|
+
type LiveJson = null | boolean | number | string | LiveJson[] | {
|
|
3
|
+
[key: string]: LiveJson;
|
|
4
|
+
};
|
|
5
|
+
type LiveObject = {
|
|
6
|
+
[key: string]: LiveJson;
|
|
7
|
+
};
|
|
8
|
+
type LiveMode = "client" | "managed";
|
|
9
|
+
interface LivePosition {
|
|
10
|
+
command: number;
|
|
11
|
+
audioBytes: number;
|
|
12
|
+
}
|
|
13
|
+
interface LiveBinding {
|
|
14
|
+
entry: number;
|
|
15
|
+
pointer: string;
|
|
16
|
+
name: string;
|
|
17
|
+
action: "define" | "reference";
|
|
18
|
+
owner: "client" | "server";
|
|
19
|
+
origin?: "observed" | "imported-context";
|
|
20
|
+
}
|
|
21
|
+
interface LiveEntry {
|
|
22
|
+
direction: "client" | "server";
|
|
23
|
+
atMs: number;
|
|
24
|
+
event: LiveObject;
|
|
25
|
+
barrier?: LivePosition;
|
|
26
|
+
}
|
|
27
|
+
interface LiveTranscript {
|
|
28
|
+
version: 1;
|
|
29
|
+
model: "gpt-live-1";
|
|
30
|
+
mode: LiveMode;
|
|
31
|
+
audio: {
|
|
32
|
+
encoding: "pcm16le";
|
|
33
|
+
sampleRateHz: 24000;
|
|
34
|
+
channels: 1;
|
|
35
|
+
};
|
|
36
|
+
configuration: LiveObject;
|
|
37
|
+
entries: LiveEntry[];
|
|
38
|
+
bindings: LiveBinding[];
|
|
39
|
+
capture: {
|
|
40
|
+
source: "authored" | "provider";
|
|
41
|
+
complete: true;
|
|
42
|
+
terminalEntry: number;
|
|
43
|
+
provenance?: {
|
|
44
|
+
host: string;
|
|
45
|
+
path: "/v1/live/sessions";
|
|
46
|
+
observedAt: string;
|
|
47
|
+
referenceVersion: string;
|
|
48
|
+
sanitationVersion: 1;
|
|
49
|
+
};
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
interface LiveFixtureResponse {
|
|
53
|
+
live: LiveTranscript;
|
|
54
|
+
liveTiming?: "recorded" | "immediate";
|
|
55
|
+
}
|
|
56
|
+
interface LiveOptions {
|
|
57
|
+
maxSessions?: number;
|
|
58
|
+
maxMessageBytes?: number;
|
|
59
|
+
maxBufferedBytes?: number;
|
|
60
|
+
maxWriteBytes?: number;
|
|
61
|
+
maxDecodedAudioBytes?: number;
|
|
62
|
+
maxDurationMs?: number;
|
|
63
|
+
idleTimeoutMs?: number;
|
|
64
|
+
mismatchTimeoutMs?: number;
|
|
65
|
+
secretValues?: string[];
|
|
66
|
+
}
|
|
67
|
+
type LiveFailureCategory = "invalid-client" | "fixture-mismatch" | "protocol-divergence" | "upstream-auth" | "upstream-access" | "upstream-connect" | "timeout" | "resource-limit" | "incomplete-capture" | "unsafe-export";
|
|
68
|
+
//# sourceMappingURL=live-types.d.ts.map
|
|
69
|
+
//#endregion
|
|
70
|
+
export { LiveBinding, LiveEntry, LiveFailureCategory, LiveFixtureResponse, LiveJson, LiveMode, LiveObject, LiveOptions, LivePosition, LiveTranscript };
|
|
71
|
+
//# sourceMappingURL=live-types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"live-types.d.ts","names":[],"sources":["../src/live-types.ts"],"sourcesContent":[],"mappings":";KAAY,QAAA,sCAA8C;EAA9C,CAAA,GAAA,EAAA,MAAQ,CAAA,EAAoE,QAApE;CAAA;AAAsC,KAC9C,UAAA,GAD8C;MAA8B,EAAA,MAAA,CAAA,EAC9C,QAD8C;CAAQ;AACpF,KACA,QAAA,GADU,QAAoB,GAAA,SAAQ;AACtC,UACK,YAAA,CADG;EACH,OAAA,EAAA,MAAY;EAIZ,UAAA,EAAA,MAAW;AAQ5B;AAA0B,UART,WAAA,CAQS;OAGjB,EAAA,MAAA;SACG,EAAA,MAAA;EAAY,IAAA,EAAA,MAAA;EAEP,MAAA,EAAA,QAAA,GAAc,WAAA;EAAA,KAAA,EAAA,QAAA,GAAA,QAAA;QAGvB,CAAA,EAAA,UAAA,GAAA,kBAAA;;AAGG,UAZM,SAAA,CAYN;WACC,EAAA,QAAA,GAAA,QAAA;EAAW,IAAA,EAAA,MAAA;EAcN,KAAA,EAxBR,UAwBQ;EAIA,OAAA,CAAA,EA3BL,YA2BgB;AAW5B;UApCiB,cAAA;;;QAGT;;;;;;iBAES;WACN;YACC;;;;;;;;;;;;;;UAcK,mBAAA;QACT;;;UAGS,WAAA;;;;;;;;;;;KAWL,mBAAA"}
|
package/dist/llmock.cjs
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
|
+
const require_helpers = require('./helpers.cjs');
|
|
2
|
+
const require_live_fixture = require('./live-fixture.cjs');
|
|
1
3
|
const require_fixture_loader = require('./fixture-loader.cjs');
|
|
2
4
|
const require_fal = require('./fal.cjs');
|
|
5
|
+
const require_elevenlabs_voice = require('./elevenlabs-voice.cjs');
|
|
3
6
|
const require_server = require('./server.cjs');
|
|
4
7
|
|
|
5
8
|
//#region src/llmock.ts
|
|
@@ -14,29 +17,38 @@ var LLMock = class LLMock {
|
|
|
14
17
|
resolvedInboundAuth;
|
|
15
18
|
constructor(options, resolvedInboundAuth) {
|
|
16
19
|
this.options = options ?? {};
|
|
20
|
+
if (this.options.live !== void 0) require_live_fixture.normalizeLiveOptions(this.options.live);
|
|
17
21
|
this.resolvedInboundAuth = resolvedInboundAuth;
|
|
18
22
|
}
|
|
23
|
+
normalizeFixture(fixture) {
|
|
24
|
+
if (require_helpers.isLiveResponse(fixture.response) || fixture.match.endpoint === "openai-live" && typeof fixture.response !== "function") return {
|
|
25
|
+
...fixture,
|
|
26
|
+
match: { ...fixture.match },
|
|
27
|
+
response: require_live_fixture.normalizeLiveFixture(fixture.response, this.options.live)
|
|
28
|
+
};
|
|
29
|
+
return fixture;
|
|
30
|
+
}
|
|
19
31
|
addFixture(fixture) {
|
|
20
|
-
this.fixtures.push(fixture);
|
|
32
|
+
this.fixtures.push(this.normalizeFixture(fixture));
|
|
21
33
|
return this;
|
|
22
34
|
}
|
|
23
35
|
addFixtures(fixtures) {
|
|
24
|
-
this.fixtures.push(...fixtures);
|
|
36
|
+
this.fixtures.push(...fixtures.map((fixture) => this.normalizeFixture(fixture)));
|
|
25
37
|
return this;
|
|
26
38
|
}
|
|
27
39
|
prependFixture(fixture) {
|
|
28
|
-
this.fixtures.unshift(fixture);
|
|
40
|
+
this.fixtures.unshift(this.normalizeFixture(fixture));
|
|
29
41
|
return this;
|
|
30
42
|
}
|
|
31
43
|
getFixtures() {
|
|
32
44
|
return this.fixtures;
|
|
33
45
|
}
|
|
34
46
|
loadFixtureFile(filePath) {
|
|
35
|
-
this.fixtures.push(...require_fixture_loader.loadFixtureFile(filePath));
|
|
47
|
+
this.fixtures.push(...require_fixture_loader.loadFixtureFile(filePath, void 0, this.options.live));
|
|
36
48
|
return this;
|
|
37
49
|
}
|
|
38
50
|
loadFixtureDir(dirPath) {
|
|
39
|
-
this.fixtures.push(...require_fixture_loader.loadFixturesFromDir(dirPath));
|
|
51
|
+
this.fixtures.push(...require_fixture_loader.loadFixturesFromDir(dirPath, void 0, this.options.live));
|
|
40
52
|
return this;
|
|
41
53
|
}
|
|
42
54
|
/**
|
|
@@ -51,23 +63,29 @@ var LLMock = class LLMock {
|
|
|
51
63
|
throw new Error(`addFixturesFromJSON: invalid JSON — ${err instanceof Error ? err.message : String(err)}`);
|
|
52
64
|
}
|
|
53
65
|
else entries = input;
|
|
54
|
-
const converted = entries.map((e) => require_fixture_loader.entryToFixture(e));
|
|
55
|
-
const errors = require_fixture_loader.validateFixtures(converted).filter((i) => i.severity === "error");
|
|
66
|
+
const converted = entries.map((e) => require_fixture_loader.entryToFixture(e, void 0, this.options.live));
|
|
67
|
+
const errors = require_fixture_loader.validateFixtures(converted, this.options.live).filter((i) => i.severity === "error");
|
|
56
68
|
if (errors.length > 0) throw new Error(`Fixture validation failed: ${JSON.stringify(errors)}`);
|
|
57
69
|
this.fixtures.push(...converted);
|
|
58
70
|
return this;
|
|
59
71
|
}
|
|
60
72
|
clearFixtures() {
|
|
61
|
-
this.fixtures
|
|
73
|
+
require_fixture_loader.clearFixtureQueue(this.fixtures);
|
|
62
74
|
return this;
|
|
63
75
|
}
|
|
64
76
|
on(match, response, opts) {
|
|
65
77
|
return this.addFixture({
|
|
66
78
|
match,
|
|
67
|
-
response: typeof response === "function" ? response : require_fixture_loader.normalizeResponse(response),
|
|
79
|
+
response: typeof response === "function" ? response : require_fixture_loader.normalizeResponse(response, this.options.live),
|
|
68
80
|
...opts
|
|
69
81
|
});
|
|
70
82
|
}
|
|
83
|
+
onLive(match, transcript, opts) {
|
|
84
|
+
return this.on({
|
|
85
|
+
...match,
|
|
86
|
+
endpoint: "openai-live"
|
|
87
|
+
}, { live: transcript }, opts);
|
|
88
|
+
}
|
|
71
89
|
onMessage(pattern, response, opts) {
|
|
72
90
|
return this.on({ userMessage: pattern }, response, opts);
|
|
73
91
|
}
|
|
@@ -165,6 +183,15 @@ var LLMock = class LLMock {
|
|
|
165
183
|
response
|
|
166
184
|
});
|
|
167
185
|
}
|
|
186
|
+
onElevenLabsVoiceDesign(description, response) {
|
|
187
|
+
return this.addFixture({
|
|
188
|
+
match: {
|
|
189
|
+
userMessage: description,
|
|
190
|
+
endpoint: "elevenlabs-voice-design"
|
|
191
|
+
},
|
|
192
|
+
response: require_elevenlabs_voice.voiceDesignToJson(response)
|
|
193
|
+
});
|
|
194
|
+
}
|
|
168
195
|
onFalAudio(prompt, response, model) {
|
|
169
196
|
return this.addFixture({
|
|
170
197
|
match: {
|
|
@@ -233,38 +260,12 @@ var LLMock = class LLMock {
|
|
|
233
260
|
/**
|
|
234
261
|
* Queue a one-shot error that will be returned for the next matching
|
|
235
262
|
* request, then automatically removed. Implemented as an internal fixture
|
|
236
|
-
*
|
|
237
|
-
*
|
|
263
|
+
* inserted at the front of the fixture list; see `queueOneShotError` for
|
|
264
|
+
* the endpoint gate and the served-not-evaluated consumption rule.
|
|
238
265
|
*/
|
|
239
266
|
nextRequestError(status, errorBody) {
|
|
240
267
|
if (!require_fixture_loader.isInjectableStatus(status)) throw new RangeError(`nextRequestError: invalid status ${String(status)} — must be ${require_fixture_loader.INJECTED_STATUS_RANGE}`);
|
|
241
|
-
|
|
242
|
-
error: {
|
|
243
|
-
message: errorBody?.message ?? "Injected error",
|
|
244
|
-
type: errorBody?.type ?? "server_error",
|
|
245
|
-
code: errorBody?.code
|
|
246
|
-
},
|
|
247
|
-
status
|
|
248
|
-
};
|
|
249
|
-
const errorEndpointCompatible = (req) => {
|
|
250
|
-
const reqEndpoint = req._endpointType;
|
|
251
|
-
if (reqEndpoint === void 0 || reqEndpoint === "chat" || reqEndpoint === "embedding" || reqEndpoint.startsWith("realtime") || reqEndpoint === "fal") return true;
|
|
252
|
-
return false;
|
|
253
|
-
};
|
|
254
|
-
const fixture = {
|
|
255
|
-
match: { predicate: errorEndpointCompatible },
|
|
256
|
-
response: errorResponse
|
|
257
|
-
};
|
|
258
|
-
this.fixtures.unshift(fixture);
|
|
259
|
-
const original = fixture.match.predicate;
|
|
260
|
-
fixture.match.predicate = (req) => {
|
|
261
|
-
const result = original(req);
|
|
262
|
-
if (result) {
|
|
263
|
-
const idx = this.fixtures.indexOf(fixture);
|
|
264
|
-
if (idx !== -1) this.fixtures.splice(idx, 1);
|
|
265
|
-
}
|
|
266
|
-
return result;
|
|
267
|
-
};
|
|
268
|
+
require_fixture_loader.queueOneShotError(this.fixtures, status, errorBody);
|
|
268
269
|
return this;
|
|
269
270
|
}
|
|
270
271
|
mount(path, handler) {
|
|
@@ -286,8 +287,12 @@ var LLMock = class LLMock {
|
|
|
286
287
|
getLastRequest() {
|
|
287
288
|
return this.journal.getLast();
|
|
288
289
|
}
|
|
290
|
+
/**
|
|
291
|
+
* Clear the request journal. Fixture match-counts (sequencing state) are
|
|
292
|
+
* left intact — use `resetMatchCounts()` for those.
|
|
293
|
+
*/
|
|
289
294
|
clearRequests() {
|
|
290
|
-
this.journal.
|
|
295
|
+
this.journal.clearEntries();
|
|
291
296
|
}
|
|
292
297
|
resetMatchCounts(testId) {
|
|
293
298
|
if (this.serverInstance) this.serverInstance.journal.clearMatchCounts(testId);
|
|
@@ -335,8 +340,10 @@ var LLMock = class LLMock {
|
|
|
335
340
|
* NOT ALL OF THIS IS PER-INSTANCE. `performFullReset` clears module-global
|
|
336
341
|
* state as well: the Gemini interaction and event-id counters
|
|
337
342
|
* (`resetInteractionCounter` / `resetEventIdCounter` in
|
|
338
|
-
* `./gemini-interactions.js`)
|
|
339
|
-
* `falQueueStates`)
|
|
343
|
+
* `./gemini-interactions.js`), the fal.ai job/queue maps (`falJobs`,
|
|
344
|
+
* `falQueueStates`), the fine-tuning job store (`clearFineTuningStore`),
|
|
345
|
+
* and the ElevenLabs Voice Design store (`clearElevenLabsVoices`). With two
|
|
346
|
+
* `LLMock` instances live in one process,
|
|
340
347
|
* `a.reset()` rewinds the Gemini id sequence that `b` is mid-way through —
|
|
341
348
|
* `b` then re-emits `aimock-int-0` / `evt_1`, ids it has already handed
|
|
342
349
|
* out — and drops `b`'s in-flight fal jobs. Give each instance its own
|
|
@@ -365,6 +372,10 @@ var LLMock = class LLMock {
|
|
|
365
372
|
}));
|
|
366
373
|
return this.serverInstance.url;
|
|
367
374
|
}
|
|
375
|
+
closeLiveSessions(testId) {
|
|
376
|
+
this.serverInstance?.closeLiveSessions(testId);
|
|
377
|
+
return this;
|
|
378
|
+
}
|
|
368
379
|
async stop() {
|
|
369
380
|
if (!this.serverInstance) throw new Error("Server not started");
|
|
370
381
|
const { server } = this.serverInstance;
|