@copilotkit/aimock 1.46.0 → 1.47.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 +12 -1
- package/README.md +4 -1
- package/dist/aws-event-stream.cjs +20 -23
- package/dist/aws-event-stream.cjs.map +1 -1
- package/dist/aws-event-stream.d.cts +0 -1
- package/dist/aws-event-stream.d.ts +0 -1
- package/dist/aws-event-stream.js +20 -24
- package/dist/aws-event-stream.js.map +1 -1
- package/dist/bedrock-converse.cjs +79 -82
- 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 +80 -83
- package/dist/bedrock-converse.js.map +1 -1
- package/dist/bedrock.cjs +112 -121
- 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 +113 -122
- package/dist/bedrock.js.map +1 -1
- package/dist/cohere.cjs +58 -106
- 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 +60 -108
- 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/fixture-loader.cjs +103 -2
- package/dist/fixture-loader.cjs.map +1 -1
- package/dist/fixture-loader.d.cts.map +1 -1
- package/dist/fixture-loader.d.ts.map +1 -1
- package/dist/fixture-loader.js +104 -4
- package/dist/fixture-loader.js.map +1 -1
- package/dist/gemini-interactions.cjs +63 -121
- 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 +65 -123
- package/dist/gemini-interactions.js.map +1 -1
- package/dist/gemini.cjs +242 -155
- 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 +244 -157
- package/dist/gemini.js.map +1 -1
- package/dist/helpers.cjs +400 -7
- package/dist/helpers.cjs.map +1 -1
- package/dist/helpers.d.cts +51 -1
- package/dist/helpers.d.cts.map +1 -1
- package/dist/helpers.d.ts +51 -1
- package/dist/helpers.d.ts.map +1 -1
- package/dist/helpers.js +391 -8
- package/dist/helpers.js.map +1 -1
- package/dist/index.cjs +4 -2
- package/dist/index.d.cts +4 -4
- package/dist/index.d.ts +4 -4
- package/dist/index.js +4 -4
- package/dist/mcp-fakes.d.cts.map +1 -1
- package/dist/messages.cjs +159 -141
- 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 +161 -143
- package/dist/messages.js.map +1 -1
- package/dist/misbehavior.cjs +62 -3
- package/dist/misbehavior.cjs.map +1 -1
- package/dist/misbehavior.d.cts.map +1 -1
- package/dist/misbehavior.d.ts.map +1 -1
- package/dist/misbehavior.js +63 -4
- package/dist/misbehavior.js.map +1 -1
- package/dist/ndjson-writer.cjs +21 -23
- package/dist/ndjson-writer.cjs.map +1 -1
- package/dist/ndjson-writer.d.cts +0 -1
- package/dist/ndjson-writer.d.cts.map +1 -1
- package/dist/ndjson-writer.d.ts +0 -1
- package/dist/ndjson-writer.d.ts.map +1 -1
- package/dist/ndjson-writer.js +21 -24
- package/dist/ndjson-writer.js.map +1 -1
- package/dist/ollama.cjs +153 -147
- 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 +155 -149
- package/dist/ollama.js.map +1 -1
- package/dist/openrouter-chat.cjs +1 -1
- package/dist/openrouter-chat.js +1 -1
- package/dist/raw-validation.cjs +177 -0
- package/dist/raw-validation.cjs.map +1 -0
- package/dist/raw-validation.js +176 -0
- package/dist/raw-validation.js.map +1 -0
- package/dist/response/deliver.cjs +144 -0
- package/dist/response/deliver.cjs.map +1 -0
- package/dist/response/deliver.js +143 -0
- package/dist/response/deliver.js.map +1 -0
- package/dist/response/frame-loop.cjs +102 -0
- package/dist/response/frame-loop.cjs.map +1 -0
- package/dist/response/frame-loop.js +100 -0
- package/dist/response/frame-loop.js.map +1 -0
- package/dist/response/lifecycle.cjs +223 -0
- package/dist/response/lifecycle.cjs.map +1 -0
- package/dist/response/lifecycle.js +211 -0
- package/dist/response/lifecycle.js.map +1 -0
- package/dist/response/proxy.cjs +71 -0
- package/dist/response/proxy.cjs.map +1 -0
- package/dist/response/proxy.js +70 -0
- package/dist/response/proxy.js.map +1 -0
- package/dist/response/raw.cjs +228 -0
- package/dist/response/raw.cjs.map +1 -0
- package/dist/response/raw.js +227 -0
- package/dist/response/raw.js.map +1 -0
- package/dist/response/timing.cjs +157 -0
- package/dist/response/timing.cjs.map +1 -0
- package/dist/response/timing.js +150 -0
- package/dist/response/timing.js.map +1 -0
- package/dist/responses.cjs +269 -148
- 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 +270 -149
- package/dist/responses.js.map +1 -1
- package/dist/router.cjs +20 -4
- package/dist/router.cjs.map +1 -1
- package/dist/router.d.cts +7 -1
- package/dist/router.d.cts.map +1 -1
- package/dist/router.d.ts +7 -1
- package/dist/router.d.ts.map +1 -1
- package/dist/router.js +21 -5
- package/dist/router.js.map +1 -1
- package/dist/server.cjs +89 -120
- package/dist/server.cjs.map +1 -1
- package/dist/server.d.cts.map +1 -1
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +91 -122
- package/dist/server.js.map +1 -1
- package/dist/sse-writer.cjs +49 -30
- package/dist/sse-writer.cjs.map +1 -1
- package/dist/sse-writer.d.cts +0 -1
- package/dist/sse-writer.d.cts.map +1 -1
- package/dist/sse-writer.d.ts +0 -1
- package/dist/sse-writer.d.ts.map +1 -1
- package/dist/sse-writer.js +49 -31
- package/dist/sse-writer.js.map +1 -1
- package/dist/transcription.cjs +19 -33
- 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 +20 -34
- package/dist/transcription.js.map +1 -1
- package/dist/types.cjs.map +1 -1
- package/dist/types.d.cts +173 -10
- package/dist/types.d.cts.map +1 -1
- package/dist/types.d.ts +173 -10
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/dist/ws-framing.cjs +12 -6
- package/dist/ws-framing.cjs.map +1 -1
- package/dist/ws-framing.d.cts +2 -0
- package/dist/ws-framing.d.cts.map +1 -1
- package/dist/ws-framing.d.ts +2 -0
- package/dist/ws-framing.d.ts.map +1 -1
- package/dist/ws-framing.js +12 -6
- package/dist/ws-framing.js.map +1 -1
- package/dist/ws-gemini-live.cjs +120 -93
- package/dist/ws-gemini-live.cjs.map +1 -1
- package/dist/ws-gemini-live.js +120 -93
- package/dist/ws-gemini-live.js.map +1 -1
- package/dist/ws-realtime.cjs +80 -70
- 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 +80 -70
- package/dist/ws-realtime.js.map +1 -1
- package/dist/ws-responses.cjs +72 -53
- package/dist/ws-responses.cjs.map +1 -1
- package/dist/ws-responses.d.cts.map +1 -1
- package/dist/ws-responses.d.ts.map +1 -1
- package/dist/ws-responses.js +73 -54
- package/dist/ws-responses.js.map +1 -1
- package/package.json +1 -1
- package/skills/write-fixtures/SKILL.md +34 -2
package/dist/messages.cjs
CHANGED
|
@@ -8,7 +8,10 @@ const require_misbehavior = require('./misbehavior.cjs');
|
|
|
8
8
|
const require_router = require('./router.cjs');
|
|
9
9
|
const require_anthropic = require('./response/usage/codecs/anthropic.cjs');
|
|
10
10
|
const require_recorder = require('./recorder.cjs');
|
|
11
|
-
const
|
|
11
|
+
const require_timing = require('./response/timing.cjs');
|
|
12
|
+
const require_deliver = require('./response/deliver.cjs');
|
|
13
|
+
const require_proxy = require('./response/proxy.cjs');
|
|
14
|
+
const require_raw = require('./response/raw.cjs');
|
|
12
15
|
const require_finish = require('./response/finish.cjs');
|
|
13
16
|
|
|
14
17
|
//#region src/messages.ts
|
|
@@ -232,6 +235,76 @@ function pushRedactedThinkingStreamEvents(events, startIndex, redactedThinking)
|
|
|
232
235
|
}
|
|
233
236
|
return blockIndex;
|
|
234
237
|
}
|
|
238
|
+
/**
|
|
239
|
+
* Fixture `citations` as Anthropic `web_search_result_location` citations, the
|
|
240
|
+
* URL-sourced citation type (see `CitationsWebSearchResultLocation` in
|
|
241
|
+
* `@anthropic-ai/sdk`), grouped per text block. `texts` are the turn's text
|
|
242
|
+
* blocks in order; citation offsets index their joined text, the same
|
|
243
|
+
* convention as Chat Completions and Gemini. Each citation goes only to the
|
|
244
|
+
* last text block whose start offset in the joined text is at or before the
|
|
245
|
+
* citation's `startIndex`, with `cited_text` sliced from that block's own text
|
|
246
|
+
* (cut off at the block's end).
|
|
247
|
+
* `web_search_result_location` carries no offsets, so nothing is rebased.
|
|
248
|
+
* `encrypted_index` is opaque on the real wire, so aimock fills a stable
|
|
249
|
+
* base64 placeholder. File citations are skipped (see {@link resolveCitations}):
|
|
250
|
+
* Anthropic has no citation type for an OpenAI file ID.
|
|
251
|
+
*/
|
|
252
|
+
function claudeBlockCitations(texts, overrides) {
|
|
253
|
+
const starts = [];
|
|
254
|
+
let offset = 0;
|
|
255
|
+
for (const text of texts) {
|
|
256
|
+
starts.push(offset);
|
|
257
|
+
offset += text.length;
|
|
258
|
+
}
|
|
259
|
+
const perBlock = texts.map(() => []);
|
|
260
|
+
if (texts.length === 0) {
|
|
261
|
+
require_helpers.resolveCitations("", overrides?.citations);
|
|
262
|
+
return perBlock;
|
|
263
|
+
}
|
|
264
|
+
require_helpers.resolveCitations(texts.join(""), overrides?.citations).forEach((c, i) => {
|
|
265
|
+
let owner = 0;
|
|
266
|
+
for (let k = 1; k < starts.length; k++) if (starts[k] <= c.startIndex) owner = k;
|
|
267
|
+
const start = starts[owner];
|
|
268
|
+
perBlock[owner].push({
|
|
269
|
+
type: "web_search_result_location",
|
|
270
|
+
cited_text: texts[owner].slice(c.startIndex - start, c.endIndex - start),
|
|
271
|
+
url: c.url,
|
|
272
|
+
title: c.title,
|
|
273
|
+
encrypted_index: Buffer.from(`aimock-citation:${i}:${c.url}`).toString("base64")
|
|
274
|
+
});
|
|
275
|
+
});
|
|
276
|
+
return perBlock;
|
|
277
|
+
}
|
|
278
|
+
/** {@link claudeBlockCitations} for a turn with a single text block. */
|
|
279
|
+
function claudeCitations(text, overrides) {
|
|
280
|
+
return claudeBlockCitations([text], overrides)[0];
|
|
281
|
+
}
|
|
282
|
+
/** A text content block; `citations` is added only when it has some. */
|
|
283
|
+
function claudeTextBlock(text, citations) {
|
|
284
|
+
return citations.length > 0 ? {
|
|
285
|
+
type: "text",
|
|
286
|
+
text,
|
|
287
|
+
citations
|
|
288
|
+
} : {
|
|
289
|
+
type: "text",
|
|
290
|
+
text
|
|
291
|
+
};
|
|
292
|
+
}
|
|
293
|
+
/**
|
|
294
|
+
* Streamed form of a text block's citations: one `citations_delta` per
|
|
295
|
+
* citation, after the block's text deltas and before its `content_block_stop`,
|
|
296
|
+
* as in the Anthropic citations streaming example. Nothing when it has none.
|
|
297
|
+
*/
|
|
298
|
+
function claudeCitationDeltas(citations, index) {
|
|
299
|
+
return citations.map((citation) => ({
|
|
300
|
+
type: "content_block_delta",
|
|
301
|
+
index,
|
|
302
|
+
delta: {
|
|
303
|
+
type: "citations_delta",
|
|
304
|
+
citation
|
|
305
|
+
}
|
|
306
|
+
}));
|
|
307
|
+
}
|
|
235
308
|
function buildClaudeTextStreamEvents(content, model, chunkSize, reasoning, overrides, reasoningSignature, redactedThinking) {
|
|
236
309
|
const msgId = overrides?.id ?? require_helpers.generateMessageId();
|
|
237
310
|
const effectiveModel = overrides?.model ?? model;
|
|
@@ -306,6 +379,7 @@ function buildClaudeTextStreamEvents(content, model, chunkSize, reasoning, overr
|
|
|
306
379
|
}
|
|
307
380
|
});
|
|
308
381
|
}
|
|
382
|
+
events.push(...claudeCitationDeltas(claudeCitations(content, overrides), blockIndex));
|
|
309
383
|
events.push({
|
|
310
384
|
type: "content_block_stop",
|
|
311
385
|
index: blockIndex
|
|
@@ -322,6 +396,7 @@ function buildClaudeTextStreamEvents(content, model, chunkSize, reasoning, overr
|
|
|
322
396
|
return events;
|
|
323
397
|
}
|
|
324
398
|
function buildClaudeToolCallStreamEvents(toolCalls, model, chunkSize, logger, reasoning, overrides, reasoningSignature, redactedThinking) {
|
|
399
|
+
require_helpers.resolveCitations("", overrides?.citations);
|
|
325
400
|
const msgId = overrides?.id ?? require_helpers.generateMessageId();
|
|
326
401
|
const effectiveModel = overrides?.model ?? model;
|
|
327
402
|
const signature = reasoningSignature ?? PLACEHOLDER_SIGNATURE;
|
|
@@ -443,10 +518,7 @@ function buildClaudeTextResponse(content, model, reasoning, overrides, reasoning
|
|
|
443
518
|
thinking: reasoning,
|
|
444
519
|
signature: reasoningSignature ?? PLACEHOLDER_SIGNATURE
|
|
445
520
|
});
|
|
446
|
-
contentBlocks.push(
|
|
447
|
-
type: "text",
|
|
448
|
-
text: content
|
|
449
|
-
});
|
|
521
|
+
contentBlocks.push(claudeTextBlock(content, claudeCitations(content, overrides)));
|
|
450
522
|
return {
|
|
451
523
|
id: overrides?.id ?? require_helpers.generateMessageId(),
|
|
452
524
|
type: "message",
|
|
@@ -459,6 +531,7 @@ function buildClaudeTextResponse(content, model, reasoning, overrides, reasoning
|
|
|
459
531
|
};
|
|
460
532
|
}
|
|
461
533
|
function buildClaudeToolCallResponse(toolCalls, model, logger, reasoning, overrides, reasoningSignature, redactedThinking) {
|
|
534
|
+
require_helpers.resolveCitations("", overrides?.citations);
|
|
462
535
|
const contentBlocks = [];
|
|
463
536
|
pushRedactedThinkingBlocks(contentBlocks, redactedThinking);
|
|
464
537
|
if (reasoning) contentBlocks.push({
|
|
@@ -544,6 +617,8 @@ function buildClaudeContentWithToolCallsStreamEvents(content, toolCalls, model,
|
|
|
544
617
|
if (blocks && blocks.length > 0) {
|
|
545
618
|
const outcome = require_helpers.resolveFixtureBlockOutcome(blocks);
|
|
546
619
|
const ordered = outcome.ordered;
|
|
620
|
+
const textCitations = claudeBlockCitations(ordered.flatMap((b) => b.type === "text" ? [b.text] : []), overrides);
|
|
621
|
+
let textPos = 0;
|
|
547
622
|
for (const block of ordered) if (block.type === "text") {
|
|
548
623
|
events.push({
|
|
549
624
|
type: "content_block_start",
|
|
@@ -564,6 +639,7 @@ function buildClaudeContentWithToolCallsStreamEvents(content, toolCalls, model,
|
|
|
564
639
|
}
|
|
565
640
|
});
|
|
566
641
|
}
|
|
642
|
+
events.push(...claudeCitationDeltas(textCitations[textPos++] ?? [], blockIndex));
|
|
567
643
|
events.push({
|
|
568
644
|
type: "content_block_stop",
|
|
569
645
|
index: blockIndex
|
|
@@ -629,6 +705,7 @@ function buildClaudeContentWithToolCallsStreamEvents(content, toolCalls, model,
|
|
|
629
705
|
}
|
|
630
706
|
});
|
|
631
707
|
}
|
|
708
|
+
events.push(...claudeCitationDeltas(claudeCitations(content, overrides), blockIndex));
|
|
632
709
|
events.push({
|
|
633
710
|
type: "content_block_stop",
|
|
634
711
|
index: blockIndex
|
|
@@ -696,21 +773,17 @@ function buildClaudeContentWithToolCallsResponse(content, toolCalls, model, logg
|
|
|
696
773
|
if (blocks && blocks.length > 0) {
|
|
697
774
|
const outcome = require_helpers.resolveFixtureBlockOutcome(blocks);
|
|
698
775
|
const ordered = outcome.ordered;
|
|
776
|
+
const textCitations = claudeBlockCitations(ordered.flatMap((b) => b.type === "text" ? [b.text] : []), overrides);
|
|
777
|
+
let textPos = 0;
|
|
699
778
|
blockHasTools = outcome.hasToolCalls;
|
|
700
|
-
for (const block of ordered) if (block.type === "text") contentBlocks.push(
|
|
701
|
-
type: "text",
|
|
702
|
-
text: block.text
|
|
703
|
-
});
|
|
779
|
+
for (const block of ordered) if (block.type === "text") contentBlocks.push(claudeTextBlock(block.text, textCitations[textPos++] ?? []));
|
|
704
780
|
else contentBlocks.push(toolUseBlock({
|
|
705
781
|
name: block.name,
|
|
706
782
|
arguments: block.arguments,
|
|
707
783
|
id: block.id
|
|
708
784
|
}));
|
|
709
785
|
} else {
|
|
710
|
-
contentBlocks.push(
|
|
711
|
-
type: "text",
|
|
712
|
-
text: content
|
|
713
|
-
});
|
|
786
|
+
contentBlocks.push(claudeTextBlock(content, claudeCitations(content, overrides)));
|
|
714
787
|
for (const tc of toolCalls) contentBlocks.push(toolUseBlock(tc));
|
|
715
788
|
}
|
|
716
789
|
return {
|
|
@@ -780,8 +853,10 @@ function prepareClaudeMisbehavior(plan, context) {
|
|
|
780
853
|
outputTokens: require_helpers.estimateTokens(content + servedToolCalls.map((call) => call.name + call.arguments).join("") + (reasoning ?? "") + (plan.refusal ?? ""))
|
|
781
854
|
});
|
|
782
855
|
const usage = require_anthropic.render(resolvedUsage, "messages-final", "applied-misbehavior");
|
|
856
|
+
const { citations, ...rest } = require_helpers.extractOverrides(response);
|
|
783
857
|
const overrides = {
|
|
784
|
-
...
|
|
858
|
+
...rest,
|
|
859
|
+
...context.citations ? { citations } : {},
|
|
785
860
|
...plan.stop === "length" ? { finishReason: "length" } : {},
|
|
786
861
|
usage
|
|
787
862
|
};
|
|
@@ -804,6 +879,7 @@ function prepareClaudeMisbehavior(plan, context) {
|
|
|
804
879
|
usage
|
|
805
880
|
};
|
|
806
881
|
if (!context.stream) return {
|
|
882
|
+
reasoning,
|
|
807
883
|
stream: false,
|
|
808
884
|
body,
|
|
809
885
|
plan: preparedPlan,
|
|
@@ -853,6 +929,7 @@ function prepareClaudeMisbehavior(plan, context) {
|
|
|
853
929
|
usage: require_anthropic.render(resolvedUsage, "messages-delta", "applied-misbehavior")
|
|
854
930
|
}, { type: "message_stop" });
|
|
855
931
|
return {
|
|
932
|
+
reasoning,
|
|
856
933
|
stream: true,
|
|
857
934
|
events,
|
|
858
935
|
plan: preparedPlan,
|
|
@@ -878,12 +955,14 @@ function prepareClaudeMisbehavior(plan, context) {
|
|
|
878
955
|
usage
|
|
879
956
|
};
|
|
880
957
|
if (!context.stream) return {
|
|
958
|
+
reasoning,
|
|
881
959
|
stream: false,
|
|
882
960
|
body,
|
|
883
961
|
plan: preparedPlan,
|
|
884
962
|
usage
|
|
885
963
|
};
|
|
886
964
|
return {
|
|
965
|
+
reasoning,
|
|
887
966
|
stream: true,
|
|
888
967
|
events: [
|
|
889
968
|
{
|
|
@@ -920,6 +999,7 @@ function prepareClaudeMisbehavior(plan, context) {
|
|
|
920
999
|
if (plan.summary.fault === "stop-length-mid-tool") {
|
|
921
1000
|
const cutIndex = events.filter((event) => event.type === "content_block_start" && require_helpers.isJsonObject(event.content_block) && event.content_block.type === "tool_use")[plan.target?.index ?? 0]?.index;
|
|
922
1001
|
return {
|
|
1002
|
+
reasoning,
|
|
923
1003
|
stream: true,
|
|
924
1004
|
events: events.filter((event) => !(event.type === "content_block_stop" && event.index === cutIndex)),
|
|
925
1005
|
plan: preparedPlan,
|
|
@@ -927,6 +1007,7 @@ function prepareClaudeMisbehavior(plan, context) {
|
|
|
927
1007
|
};
|
|
928
1008
|
}
|
|
929
1009
|
return {
|
|
1010
|
+
reasoning,
|
|
930
1011
|
stream: true,
|
|
931
1012
|
events,
|
|
932
1013
|
plan: preparedPlan,
|
|
@@ -940,51 +1021,13 @@ function prepareClaudeMisbehavior(plan, context) {
|
|
|
940
1021
|
artifacts.redactedThinking
|
|
941
1022
|
];
|
|
942
1023
|
return {
|
|
1024
|
+
reasoning,
|
|
943
1025
|
stream: false,
|
|
944
1026
|
body: require_helpers.isCombinedFixtureResponse(preparedResponse) ? buildClaudeContentWithToolCallsResponse(preparedResponse.content ?? "", toolCalls, model, context.logger, ...args, preparedResponse.blocks) : require_helpers.isToolCallResponse(preparedResponse) ? buildClaudeToolCallResponse(toolCalls, model, context.logger, ...args) : buildClaudeTextResponse(content, model, ...args),
|
|
945
1027
|
plan: preparedPlan,
|
|
946
1028
|
usage
|
|
947
1029
|
};
|
|
948
1030
|
}
|
|
949
|
-
async function writeClaudeSSEStream(res, events, optionsOrLatency) {
|
|
950
|
-
const opts = typeof optionsOrLatency === "number" ? { latency: optionsOrLatency } : optionsOrLatency ?? {};
|
|
951
|
-
const latency = opts.latency ?? 0;
|
|
952
|
-
const profile = opts.streamingProfile;
|
|
953
|
-
const { recordedTimings, replaySpeed } = opts;
|
|
954
|
-
const onChunkSent = opts.onChunkSent;
|
|
955
|
-
if (res.writableEnded) return true;
|
|
956
|
-
if (res.destroyed) return false;
|
|
957
|
-
const controller = new AbortController();
|
|
958
|
-
const signal = controller.signal;
|
|
959
|
-
const abort = () => controller.abort();
|
|
960
|
-
const onClose = () => {
|
|
961
|
-
if (!res.writableFinished) abort();
|
|
962
|
-
};
|
|
963
|
-
res.once("close", onClose);
|
|
964
|
-
opts.signal?.addEventListener("abort", abort, { once: true });
|
|
965
|
-
if (opts.signal?.aborted) abort();
|
|
966
|
-
try {
|
|
967
|
-
res.setHeader("Content-Type", "text/event-stream");
|
|
968
|
-
res.setHeader("Cache-Control", "no-cache");
|
|
969
|
-
res.setHeader("Connection", "keep-alive");
|
|
970
|
-
let chunkIndex = 0;
|
|
971
|
-
for (const event of events) {
|
|
972
|
-
const chunkDelay = require_sse_writer.calculateDelay(chunkIndex, profile, latency, recordedTimings, replaySpeed);
|
|
973
|
-
if (chunkDelay > 0) await require_sse_writer.delay(chunkDelay, signal);
|
|
974
|
-
if (signal.aborted || res.destroyed) return false;
|
|
975
|
-
if (res.writableEnded) return true;
|
|
976
|
-
res.write(`event: ${event.type}\ndata: ${JSON.stringify(event)}\n\n`);
|
|
977
|
-
onChunkSent?.();
|
|
978
|
-
if (signal.aborted || res.destroyed) return false;
|
|
979
|
-
chunkIndex++;
|
|
980
|
-
}
|
|
981
|
-
if (!res.writableEnded) res.end();
|
|
982
|
-
return true;
|
|
983
|
-
} finally {
|
|
984
|
-
res.off("close", onClose);
|
|
985
|
-
opts.signal?.removeEventListener("abort", abort);
|
|
986
|
-
}
|
|
987
|
-
}
|
|
988
1031
|
async function handleMessages(req, res, raw, fixtures, journal, defaults, setCorsHeaders) {
|
|
989
1032
|
const { logger } = defaults;
|
|
990
1033
|
setCorsHeaders(res);
|
|
@@ -1112,7 +1155,10 @@ async function handleMessages(req, res, raw, fixtures, journal, defaults, setCor
|
|
|
1112
1155
|
const completionReq = claudeToCompletionRequest(claudeReq);
|
|
1113
1156
|
completionReq._context = require_helpers.getContext(req);
|
|
1114
1157
|
const testId = require_helpers.getTestId(req);
|
|
1115
|
-
const { fixture, skippedBySequenceOrTurn } = require_router.matchFixtureDiagnostic(fixtures, completionReq, journal.getFixtureMatchCountsForTest(testId), defaults.requestTransform,
|
|
1158
|
+
const { fixture, skippedBySequenceOrTurn } = require_router.matchFixtureDiagnostic(fixtures, completionReq, journal.getFixtureMatchCountsForTest(testId), defaults.requestTransform, {
|
|
1159
|
+
...require_router.recordMatchOptions(!!defaults.record, defaults.logger),
|
|
1160
|
+
wire: "anthropic"
|
|
1161
|
+
});
|
|
1116
1162
|
if (fixture) {
|
|
1117
1163
|
journal.incrementFixtureMatchCount(fixture, fixtures, testId);
|
|
1118
1164
|
logger.debug(`Fixture matched: ${JSON.stringify(fixture.match).slice(0, 120)}`);
|
|
@@ -1150,7 +1196,7 @@ async function handleMessages(req, res, raw, fixtures, journal, defaults, setCor
|
|
|
1150
1196
|
return;
|
|
1151
1197
|
}
|
|
1152
1198
|
if (defaults.record) {
|
|
1153
|
-
const outcome = await require_recorder.proxyAndRecord(req, res, completionReq, "anthropic", req.url ?? "/v1/messages", fixtures, defaults, raw);
|
|
1199
|
+
const { outcome, clientHungUp } = await require_proxy.withHangUpDetection(req, res, () => require_recorder.proxyAndRecord(req, res, completionReq, "anthropic", req.url ?? "/v1/messages", fixtures, defaults, raw));
|
|
1154
1200
|
if (outcome === "handled_by_hook") return;
|
|
1155
1201
|
if (outcome !== "not_configured") {
|
|
1156
1202
|
journal.add({
|
|
@@ -1161,7 +1207,8 @@ async function handleMessages(req, res, raw, fixtures, journal, defaults, setCor
|
|
|
1161
1207
|
response: {
|
|
1162
1208
|
status: res.statusCode ?? 200,
|
|
1163
1209
|
fixture: null,
|
|
1164
|
-
source: "proxy"
|
|
1210
|
+
source: "proxy",
|
|
1211
|
+
...require_proxy.proxyInterruption(res, clientHungUp)
|
|
1165
1212
|
}
|
|
1166
1213
|
});
|
|
1167
1214
|
return;
|
|
@@ -1222,14 +1269,22 @@ async function handleMessages(req, res, raw, fixtures, journal, defaults, setCor
|
|
|
1222
1269
|
}
|
|
1223
1270
|
const latency = fixture.latency ?? defaults.latency;
|
|
1224
1271
|
const chunkSize = Math.max(1, fixture.chunkSize ?? defaults.chunkSize);
|
|
1272
|
+
const replaySpeed = fixture.replaySpeed ?? defaults.replaySpeed;
|
|
1273
|
+
const streamTiming = {
|
|
1274
|
+
latency,
|
|
1275
|
+
streamingProfile: fixture.streamingProfile,
|
|
1276
|
+
recordedTimings: fixture.recordedTimings,
|
|
1277
|
+
replaySpeed
|
|
1278
|
+
};
|
|
1279
|
+
const jsonOpts = (payloadLength, duplicateId = false) => ({
|
|
1280
|
+
surface: "other",
|
|
1281
|
+
payloadLength,
|
|
1282
|
+
chunkSize,
|
|
1283
|
+
recordedTimings: fixture.recordedTimings,
|
|
1284
|
+
duplicateId,
|
|
1285
|
+
replaySpeed
|
|
1286
|
+
});
|
|
1225
1287
|
if (misbehavior.kind === "applied") {
|
|
1226
|
-
const prepared = prepareClaudeMisbehavior(misbehavior, {
|
|
1227
|
-
request: completionReq,
|
|
1228
|
-
stream: claudeReq.stream === true,
|
|
1229
|
-
chunkSize,
|
|
1230
|
-
logger,
|
|
1231
|
-
strict: require_policy.resolveRequestDecisions(defaults.strict, req.headers).reasoningSuppression
|
|
1232
|
-
});
|
|
1233
1288
|
const entry = journal.add({
|
|
1234
1289
|
method: req.method ?? "POST",
|
|
1235
1290
|
path: req.url ?? "/v1/messages",
|
|
@@ -1240,6 +1295,15 @@ async function handleMessages(req, res, raw, fixtures, journal, defaults, setCor
|
|
|
1240
1295
|
fixture
|
|
1241
1296
|
}
|
|
1242
1297
|
});
|
|
1298
|
+
require_helpers.rejectGeneratedFiles(misbehavior.response, "Anthropic Messages");
|
|
1299
|
+
const prepared = prepareClaudeMisbehavior(misbehavior, {
|
|
1300
|
+
request: completionReq,
|
|
1301
|
+
stream: claudeReq.stream === true,
|
|
1302
|
+
chunkSize,
|
|
1303
|
+
logger,
|
|
1304
|
+
strict: require_policy.resolveRequestDecisions(defaults.strict, req.headers).reasoningSuppression,
|
|
1305
|
+
citations: true
|
|
1306
|
+
});
|
|
1243
1307
|
require_misbehavior.recordMisbehaviorOutcome({
|
|
1244
1308
|
entry,
|
|
1245
1309
|
summary: prepared.plan.summary,
|
|
@@ -1247,27 +1311,26 @@ async function handleMessages(req, res, raw, fixtures, journal, defaults, setCor
|
|
|
1247
1311
|
testId
|
|
1248
1312
|
});
|
|
1249
1313
|
if (!prepared.stream) {
|
|
1250
|
-
|
|
1251
|
-
res.
|
|
1252
|
-
} else
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
}
|
|
1314
|
+
const preparedBody = prepared.body;
|
|
1315
|
+
await require_deliver.deliverJson(res, entry, fixture, () => preparedBody, jsonOpts(require_timing.misbehaviorPayloadLength(misbehavior, misbehavior.reasoning === void 0 ? prepared.reasoning : void 0), !!misbehavior.duplicateId));
|
|
1316
|
+
} else await require_deliver.deliverStream(res, entry, fixture, require_sse_writer.typedSseCodec(), prepared.events, streamTiming);
|
|
1317
|
+
return;
|
|
1318
|
+
}
|
|
1319
|
+
if (require_helpers.isRawResponse(response)) {
|
|
1320
|
+
await require_raw.serveRawResponse({
|
|
1321
|
+
wire: "anthropic",
|
|
1322
|
+
stream: claudeReq.stream === true,
|
|
1323
|
+
req,
|
|
1324
|
+
res,
|
|
1325
|
+
fixture,
|
|
1326
|
+
response,
|
|
1327
|
+
body: completionReq,
|
|
1328
|
+
journal,
|
|
1329
|
+
defaults,
|
|
1330
|
+
testId,
|
|
1331
|
+
misbehaviorSummary: misbehavior.summary,
|
|
1332
|
+
errorWire: "messages"
|
|
1333
|
+
});
|
|
1271
1334
|
return;
|
|
1272
1335
|
}
|
|
1273
1336
|
if (require_helpers.isErrorResponse(response)) {
|
|
@@ -1312,27 +1375,12 @@ async function handleMessages(req, res, raw, fixtures, journal, defaults, setCor
|
|
|
1312
1375
|
defaults,
|
|
1313
1376
|
testId
|
|
1314
1377
|
});
|
|
1378
|
+
require_helpers.rejectGeneratedFiles(response, "Anthropic Messages");
|
|
1315
1379
|
const functionToolCalls = require_helpers.requireEmittedFunctionToolCalls(response, "Anthropic Messages");
|
|
1316
|
-
if (claudeReq.stream !== true)
|
|
1317
|
-
|
|
1318
|
-
res.writeHead(200, { "Content-Type": "application/json" });
|
|
1319
|
-
res.end(JSON.stringify(body));
|
|
1320
|
-
} else {
|
|
1380
|
+
if (claudeReq.stream !== true) await require_deliver.deliverJson(res, journalEntry, fixture, () => buildClaudeContentWithToolCallsResponse(response.content ?? "", functionToolCalls, completionReq.model, logger, effReasoning, overrides, effReasoningSignature, effRedactedThinking, response.blocks), jsonOpts(require_timing.servedPayloadLength(response, effReasoning)));
|
|
1381
|
+
else {
|
|
1321
1382
|
const events = buildClaudeContentWithToolCallsStreamEvents(response.content ?? "", functionToolCalls, completionReq.model, chunkSize, logger, effReasoning, overrides, effReasoningSignature, effRedactedThinking, response.blocks);
|
|
1322
|
-
|
|
1323
|
-
if (!await writeClaudeSSEStream(res, events, {
|
|
1324
|
-
latency,
|
|
1325
|
-
streamingProfile: fixture.streamingProfile,
|
|
1326
|
-
recordedTimings: fixture.recordedTimings,
|
|
1327
|
-
replaySpeed: fixture.replaySpeed ?? defaults.replaySpeed,
|
|
1328
|
-
signal: interruption?.signal,
|
|
1329
|
-
onChunkSent: interruption?.tick
|
|
1330
|
-
})) {
|
|
1331
|
-
if (!res.writableEnded) res.destroy();
|
|
1332
|
-
journalEntry.response.interrupted = true;
|
|
1333
|
-
journalEntry.response.interruptReason = interruption?.reason() ?? "client aborted";
|
|
1334
|
-
}
|
|
1335
|
-
interruption?.cleanup();
|
|
1383
|
+
await require_deliver.deliverStream(res, journalEntry, fixture, require_sse_writer.typedSseCodec(), events, streamTiming);
|
|
1336
1384
|
}
|
|
1337
1385
|
return;
|
|
1338
1386
|
}
|
|
@@ -1358,26 +1406,11 @@ async function handleMessages(req, res, raw, fixtures, journal, defaults, setCor
|
|
|
1358
1406
|
defaults,
|
|
1359
1407
|
testId
|
|
1360
1408
|
});
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
res.end(JSON.stringify(body));
|
|
1365
|
-
} else {
|
|
1409
|
+
require_helpers.rejectGeneratedFiles(response, "Anthropic Messages");
|
|
1410
|
+
if (claudeReq.stream !== true) await require_deliver.deliverJson(res, journalEntry, fixture, () => buildClaudeTextResponse(response.content, completionReq.model, effReasoning, overrides, effReasoningSignature, effRedactedThinking), jsonOpts(require_timing.servedPayloadLength(response, effReasoning)));
|
|
1411
|
+
else {
|
|
1366
1412
|
const events = buildClaudeTextStreamEvents(response.content, completionReq.model, chunkSize, effReasoning, overrides, effReasoningSignature, effRedactedThinking);
|
|
1367
|
-
|
|
1368
|
-
if (!await writeClaudeSSEStream(res, events, {
|
|
1369
|
-
latency,
|
|
1370
|
-
streamingProfile: fixture.streamingProfile,
|
|
1371
|
-
recordedTimings: fixture.recordedTimings,
|
|
1372
|
-
replaySpeed: fixture.replaySpeed ?? defaults.replaySpeed,
|
|
1373
|
-
signal: interruption?.signal,
|
|
1374
|
-
onChunkSent: interruption?.tick
|
|
1375
|
-
})) {
|
|
1376
|
-
if (!res.writableEnded) res.destroy();
|
|
1377
|
-
journalEntry.response.interrupted = true;
|
|
1378
|
-
journalEntry.response.interruptReason = interruption?.reason() ?? "client aborted";
|
|
1379
|
-
}
|
|
1380
|
-
interruption?.cleanup();
|
|
1413
|
+
await require_deliver.deliverStream(res, journalEntry, fixture, require_sse_writer.typedSseCodec(), events, streamTiming);
|
|
1381
1414
|
}
|
|
1382
1415
|
return;
|
|
1383
1416
|
}
|
|
@@ -1403,27 +1436,12 @@ async function handleMessages(req, res, raw, fixtures, journal, defaults, setCor
|
|
|
1403
1436
|
defaults,
|
|
1404
1437
|
testId
|
|
1405
1438
|
});
|
|
1439
|
+
require_helpers.rejectGeneratedFiles(response, "Anthropic Messages");
|
|
1406
1440
|
const functionToolCalls = require_helpers.requireEmittedFunctionToolCalls(response, "Anthropic Messages");
|
|
1407
|
-
if (claudeReq.stream !== true)
|
|
1408
|
-
|
|
1409
|
-
res.writeHead(200, { "Content-Type": "application/json" });
|
|
1410
|
-
res.end(JSON.stringify(body));
|
|
1411
|
-
} else {
|
|
1441
|
+
if (claudeReq.stream !== true) await require_deliver.deliverJson(res, journalEntry, fixture, () => buildClaudeToolCallResponse(functionToolCalls, completionReq.model, logger, effReasoning, overrides, effReasoningSignature, effRedactedThinking), jsonOpts(require_timing.servedPayloadLength(response, effReasoning)));
|
|
1442
|
+
else {
|
|
1412
1443
|
const events = buildClaudeToolCallStreamEvents(functionToolCalls, completionReq.model, chunkSize, logger, effReasoning, overrides, effReasoningSignature, effRedactedThinking);
|
|
1413
|
-
|
|
1414
|
-
if (!await writeClaudeSSEStream(res, events, {
|
|
1415
|
-
latency,
|
|
1416
|
-
streamingProfile: fixture.streamingProfile,
|
|
1417
|
-
recordedTimings: fixture.recordedTimings,
|
|
1418
|
-
replaySpeed: fixture.replaySpeed ?? defaults.replaySpeed,
|
|
1419
|
-
signal: interruption?.signal,
|
|
1420
|
-
onChunkSent: interruption?.tick
|
|
1421
|
-
})) {
|
|
1422
|
-
if (!res.writableEnded) res.destroy();
|
|
1423
|
-
journalEntry.response.interrupted = true;
|
|
1424
|
-
journalEntry.response.interruptReason = interruption?.reason() ?? "client aborted";
|
|
1425
|
-
}
|
|
1426
|
-
interruption?.cleanup();
|
|
1444
|
+
await require_deliver.deliverStream(res, journalEntry, fixture, require_sse_writer.typedSseCodec(), events, streamTiming);
|
|
1427
1445
|
}
|
|
1428
1446
|
return;
|
|
1429
1447
|
}
|