@copilotkit/aimock 1.44.0 → 1.45.1

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.
Files changed (278) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/CHANGELOG.md +42 -0
  4. package/README.md +6 -0
  5. package/dist/agui-types.d.cts.map +1 -1
  6. package/dist/aimock-cli.cjs +31 -5
  7. package/dist/aimock-cli.cjs.map +1 -1
  8. package/dist/aimock-cli.d.cts.map +1 -1
  9. package/dist/aimock-cli.d.ts.map +1 -1
  10. package/dist/aimock-cli.js +31 -5
  11. package/dist/aimock-cli.js.map +1 -1
  12. package/dist/bedrock-converse.cjs +312 -39
  13. package/dist/bedrock-converse.cjs.map +1 -1
  14. package/dist/bedrock-converse.d.cts.map +1 -1
  15. package/dist/bedrock-converse.d.ts.map +1 -1
  16. package/dist/bedrock-converse.js +313 -40
  17. package/dist/bedrock-converse.js.map +1 -1
  18. package/dist/bedrock.cjs +195 -42
  19. package/dist/bedrock.cjs.map +1 -1
  20. package/dist/bedrock.d.cts.map +1 -1
  21. package/dist/bedrock.d.ts.map +1 -1
  22. package/dist/bedrock.js +196 -43
  23. package/dist/bedrock.js.map +1 -1
  24. package/dist/chaos.cjs +1 -0
  25. package/dist/chaos.js +1 -1
  26. package/dist/cli.cjs +152 -5
  27. package/dist/cli.cjs.map +1 -1
  28. package/dist/cli.js +155 -8
  29. package/dist/cli.js.map +1 -1
  30. package/dist/cohere.cjs +132 -71
  31. package/dist/cohere.cjs.map +1 -1
  32. package/dist/cohere.d.cts.map +1 -1
  33. package/dist/cohere.d.ts.map +1 -1
  34. package/dist/cohere.js +133 -72
  35. package/dist/cohere.js.map +1 -1
  36. package/dist/config-loader.cjs +62 -4
  37. package/dist/config-loader.cjs.map +1 -1
  38. package/dist/config-loader.d.cts +37 -4
  39. package/dist/config-loader.d.cts.map +1 -1
  40. package/dist/config-loader.d.ts +37 -4
  41. package/dist/config-loader.d.ts.map +1 -1
  42. package/dist/config-loader.js +62 -4
  43. package/dist/config-loader.js.map +1 -1
  44. package/dist/fakes-plugin.cjs +131 -0
  45. package/dist/fakes-plugin.cjs.map +1 -0
  46. package/dist/fakes-plugin.d.cts +23 -0
  47. package/dist/fakes-plugin.d.cts.map +1 -0
  48. package/dist/fakes-plugin.d.ts +23 -0
  49. package/dist/fakes-plugin.d.ts.map +1 -0
  50. package/dist/fakes-plugin.js +127 -0
  51. package/dist/fakes-plugin.js.map +1 -0
  52. package/dist/fixture-loader-services.cjs +1 -1
  53. package/dist/fixture-loader-services.cjs.map +1 -1
  54. package/dist/fixture-loader-services.js +1 -1
  55. package/dist/fixture-loader-services.js.map +1 -1
  56. package/dist/fixture-loader.cjs +389 -27
  57. package/dist/fixture-loader.cjs.map +1 -1
  58. package/dist/fixture-loader.d.cts +47 -3
  59. package/dist/fixture-loader.d.cts.map +1 -1
  60. package/dist/fixture-loader.d.ts +47 -3
  61. package/dist/fixture-loader.d.ts.map +1 -1
  62. package/dist/fixture-loader.js +384 -29
  63. package/dist/fixture-loader.js.map +1 -1
  64. package/dist/gemini-interactions.cjs +250 -52
  65. package/dist/gemini-interactions.cjs.map +1 -1
  66. package/dist/gemini-interactions.d.cts.map +1 -1
  67. package/dist/gemini-interactions.d.ts.map +1 -1
  68. package/dist/gemini-interactions.js +251 -53
  69. package/dist/gemini-interactions.js.map +1 -1
  70. package/dist/gemini.cjs +226 -37
  71. package/dist/gemini.cjs.map +1 -1
  72. package/dist/gemini.d.cts.map +1 -1
  73. package/dist/gemini.d.ts.map +1 -1
  74. package/dist/gemini.js +227 -38
  75. package/dist/gemini.js.map +1 -1
  76. package/dist/helpers.cjs +881 -7
  77. package/dist/helpers.cjs.map +1 -1
  78. package/dist/helpers.d.cts +85 -4
  79. package/dist/helpers.d.cts.map +1 -1
  80. package/dist/helpers.d.ts +85 -4
  81. package/dist/helpers.d.ts.map +1 -1
  82. package/dist/helpers.js +839 -8
  83. package/dist/helpers.js.map +1 -1
  84. package/dist/index.cjs +9 -0
  85. package/dist/index.d.cts +7 -5
  86. package/dist/index.d.ts +7 -5
  87. package/dist/index.js +4 -3
  88. package/dist/jest.cjs +31 -7
  89. package/dist/jest.cjs.map +1 -1
  90. package/dist/jest.d.cts +43 -3
  91. package/dist/jest.d.cts.map +1 -1
  92. package/dist/jest.d.ts +43 -3
  93. package/dist/jest.d.ts.map +1 -1
  94. package/dist/jest.js +33 -9
  95. package/dist/jest.js.map +1 -1
  96. package/dist/journal.cjs +135 -13
  97. package/dist/journal.cjs.map +1 -1
  98. package/dist/journal.d.cts +38 -4
  99. package/dist/journal.d.cts.map +1 -1
  100. package/dist/journal.d.ts +38 -4
  101. package/dist/journal.d.ts.map +1 -1
  102. package/dist/journal.js +135 -13
  103. package/dist/journal.js.map +1 -1
  104. package/dist/live-sanitize.cjs +4 -32
  105. package/dist/live-sanitize.cjs.map +1 -1
  106. package/dist/live-sanitize.js +4 -32
  107. package/dist/live-sanitize.js.map +1 -1
  108. package/dist/llmock.cjs +123 -30
  109. package/dist/llmock.cjs.map +1 -1
  110. package/dist/llmock.d.cts +16 -1
  111. package/dist/llmock.d.cts.map +1 -1
  112. package/dist/llmock.d.ts +16 -1
  113. package/dist/llmock.d.ts.map +1 -1
  114. package/dist/llmock.js +125 -32
  115. package/dist/llmock.js.map +1 -1
  116. package/dist/mcp-fakes-mount.cjs +1 -0
  117. package/dist/mcp-fakes-mount.cjs.map +1 -1
  118. package/dist/mcp-fakes-mount.js +1 -0
  119. package/dist/mcp-fakes-mount.js.map +1 -1
  120. package/dist/mcp-fakes-report.cjs +111 -0
  121. package/dist/mcp-fakes-report.cjs.map +1 -0
  122. package/dist/mcp-fakes-report.d.cts +40 -0
  123. package/dist/mcp-fakes-report.d.cts.map +1 -0
  124. package/dist/mcp-fakes-report.d.ts +40 -0
  125. package/dist/mcp-fakes-report.d.ts.map +1 -0
  126. package/dist/mcp-fakes-report.js +107 -0
  127. package/dist/mcp-fakes-report.js.map +1 -0
  128. package/dist/mcp-fakes.cjs +331 -8
  129. package/dist/mcp-fakes.cjs.map +1 -1
  130. package/dist/mcp-fakes.d.cts +406 -3
  131. package/dist/mcp-fakes.d.cts.map +1 -1
  132. package/dist/mcp-fakes.d.ts +406 -3
  133. package/dist/mcp-fakes.d.ts.map +1 -1
  134. package/dist/mcp-fakes.js +331 -8
  135. package/dist/mcp-fakes.js.map +1 -1
  136. package/dist/mcp-handler.cjs +216 -17
  137. package/dist/mcp-handler.cjs.map +1 -1
  138. package/dist/mcp-handler.d.cts +49 -0
  139. package/dist/mcp-handler.d.cts.map +1 -0
  140. package/dist/mcp-handler.d.ts +49 -0
  141. package/dist/mcp-handler.d.ts.map +1 -0
  142. package/dist/mcp-handler.js +214 -18
  143. package/dist/mcp-handler.js.map +1 -1
  144. package/dist/mcp-mock.cjs +112 -1
  145. package/dist/mcp-mock.cjs.map +1 -1
  146. package/dist/mcp-mock.d.cts +33 -2
  147. package/dist/mcp-mock.d.cts.map +1 -1
  148. package/dist/mcp-mock.d.ts +33 -2
  149. package/dist/mcp-mock.d.ts.map +1 -1
  150. package/dist/mcp-mock.js +112 -1
  151. package/dist/mcp-mock.js.map +1 -1
  152. package/dist/mcp-recorder.cjs +1224 -0
  153. package/dist/mcp-recorder.cjs.map +1 -0
  154. package/dist/mcp-recorder.d.cts +108 -0
  155. package/dist/mcp-recorder.d.cts.map +1 -0
  156. package/dist/mcp-recorder.d.ts +108 -0
  157. package/dist/mcp-recorder.d.ts.map +1 -0
  158. package/dist/mcp-recorder.js +1215 -0
  159. package/dist/mcp-recorder.js.map +1 -0
  160. package/dist/messages.cjs +362 -56
  161. package/dist/messages.cjs.map +1 -1
  162. package/dist/messages.d.cts.map +1 -1
  163. package/dist/messages.d.ts.map +1 -1
  164. package/dist/messages.js +363 -58
  165. package/dist/messages.js.map +1 -1
  166. package/dist/misbehavior.cjs +1523 -0
  167. package/dist/misbehavior.cjs.map +1 -0
  168. package/dist/misbehavior.d.cts +43 -0
  169. package/dist/misbehavior.d.cts.map +1 -0
  170. package/dist/misbehavior.d.ts +43 -0
  171. package/dist/misbehavior.d.ts.map +1 -0
  172. package/dist/misbehavior.js +1511 -0
  173. package/dist/misbehavior.js.map +1 -0
  174. package/dist/ollama.cjs +310 -112
  175. package/dist/ollama.cjs.map +1 -1
  176. package/dist/ollama.d.cts.map +1 -1
  177. package/dist/ollama.d.ts.map +1 -1
  178. package/dist/ollama.js +311 -113
  179. package/dist/ollama.js.map +1 -1
  180. package/dist/openrouter-video.cjs +30 -2
  181. package/dist/openrouter-video.cjs.map +1 -1
  182. package/dist/openrouter-video.d.cts.map +1 -1
  183. package/dist/openrouter-video.d.ts.map +1 -1
  184. package/dist/openrouter-video.js +30 -2
  185. package/dist/openrouter-video.js.map +1 -1
  186. package/dist/record-sanitize.cjs +231 -0
  187. package/dist/record-sanitize.cjs.map +1 -0
  188. package/dist/record-sanitize.js +224 -0
  189. package/dist/record-sanitize.js.map +1 -0
  190. package/dist/recorder.cjs +106 -5
  191. package/dist/recorder.cjs.map +1 -1
  192. package/dist/recorder.d.cts +2 -1
  193. package/dist/recorder.d.cts.map +1 -1
  194. package/dist/recorder.d.ts +2 -1
  195. package/dist/recorder.d.ts.map +1 -1
  196. package/dist/recorder.js +107 -7
  197. package/dist/recorder.js.map +1 -1
  198. package/dist/responses.cjs +667 -171
  199. package/dist/responses.cjs.map +1 -1
  200. package/dist/responses.d.cts +5 -13
  201. package/dist/responses.d.cts.map +1 -1
  202. package/dist/responses.d.ts +5 -13
  203. package/dist/responses.d.ts.map +1 -1
  204. package/dist/responses.js +665 -174
  205. package/dist/responses.js.map +1 -1
  206. package/dist/router.cjs +24 -2
  207. package/dist/router.cjs.map +1 -1
  208. package/dist/router.d.cts.map +1 -1
  209. package/dist/router.d.ts.map +1 -1
  210. package/dist/router.js +25 -4
  211. package/dist/router.js.map +1 -1
  212. package/dist/server.cjs +572 -76
  213. package/dist/server.cjs.map +1 -1
  214. package/dist/server.d.cts.map +1 -1
  215. package/dist/server.d.ts.map +1 -1
  216. package/dist/server.js +574 -78
  217. package/dist/server.js.map +1 -1
  218. package/dist/stream-collapse.cjs +236 -34
  219. package/dist/stream-collapse.cjs.map +1 -1
  220. package/dist/stream-collapse.d.cts +39 -4
  221. package/dist/stream-collapse.d.cts.map +1 -1
  222. package/dist/stream-collapse.d.ts +39 -4
  223. package/dist/stream-collapse.d.ts.map +1 -1
  224. package/dist/stream-collapse.js +235 -35
  225. package/dist/stream-collapse.js.map +1 -1
  226. package/dist/types.cjs.map +1 -1
  227. package/dist/types.d.cts +315 -4
  228. package/dist/types.d.cts.map +1 -1
  229. package/dist/types.d.ts +315 -4
  230. package/dist/types.d.ts.map +1 -1
  231. package/dist/types.js.map +1 -1
  232. package/dist/validate-cli.cjs +36 -4
  233. package/dist/validate-cli.cjs.map +1 -1
  234. package/dist/validate-cli.js +37 -5
  235. package/dist/validate-cli.js.map +1 -1
  236. package/dist/vector-types.d.ts.map +1 -1
  237. package/dist/version.cjs +45 -0
  238. package/dist/version.cjs.map +1 -0
  239. package/dist/version.js +44 -0
  240. package/dist/version.js.map +1 -0
  241. package/dist/vitest.cjs +66 -10
  242. package/dist/vitest.cjs.map +1 -1
  243. package/dist/vitest.d.cts +35 -4
  244. package/dist/vitest.d.cts.map +1 -1
  245. package/dist/vitest.d.ts +35 -4
  246. package/dist/vitest.d.ts.map +1 -1
  247. package/dist/vitest.js +67 -11
  248. package/dist/vitest.js.map +1 -1
  249. package/dist/watcher.cjs +6 -2
  250. package/dist/watcher.cjs.map +1 -1
  251. package/dist/watcher.js +6 -2
  252. package/dist/watcher.js.map +1 -1
  253. package/dist/ws-gemini-live.cjs +210 -101
  254. package/dist/ws-gemini-live.cjs.map +1 -1
  255. package/dist/ws-gemini-live.d.cts +9 -3
  256. package/dist/ws-gemini-live.d.cts.map +1 -1
  257. package/dist/ws-gemini-live.d.ts +9 -3
  258. package/dist/ws-gemini-live.d.ts.map +1 -1
  259. package/dist/ws-gemini-live.js +211 -102
  260. package/dist/ws-gemini-live.js.map +1 -1
  261. package/dist/ws-realtime.cjs +175 -62
  262. package/dist/ws-realtime.cjs.map +1 -1
  263. package/dist/ws-realtime.d.cts +8 -3
  264. package/dist/ws-realtime.d.cts.map +1 -1
  265. package/dist/ws-realtime.d.ts +8 -3
  266. package/dist/ws-realtime.d.ts.map +1 -1
  267. package/dist/ws-realtime.js +176 -63
  268. package/dist/ws-realtime.js.map +1 -1
  269. package/dist/ws-responses.cjs +109 -16
  270. package/dist/ws-responses.cjs.map +1 -1
  271. package/dist/ws-responses.d.cts +8 -12
  272. package/dist/ws-responses.d.cts.map +1 -1
  273. package/dist/ws-responses.d.ts +8 -12
  274. package/dist/ws-responses.d.ts.map +1 -1
  275. package/dist/ws-responses.js +111 -18
  276. package/dist/ws-responses.js.map +1 -1
  277. package/package.json +10 -2
  278. package/skills/write-fixtures/SKILL.md +179 -21
package/dist/messages.js CHANGED
@@ -1,6 +1,7 @@
1
- import { extractOverrides, flattenHeaders, generateMessageId, generateToolUseId, getContext, getTestId, isContentWithToolCallsResponse, isErrorResponse, isJsonObject, isTextResponse, isToolCallResponse, resolveFixtureBlockOutcome, resolveReasoningArtifactsForModel, resolveReasoningForModel, resolveResponse, resolveStrictMode, strictNoMatchLogLine, strictNoMatchMessage, strictOverrideField } from "./helpers.js";
1
+ import { estimatePromptTokens, estimateTokens, extractOverrides, flattenHeaders, generateMessageId, generateToolUseId, getContext, getTestId, isCombinedFixtureResponse, isErrorResponse, isJsonObject, isTextResponse, isToolCallResponse, requireEmittedFunctionToolCalls, resolveFixtureBlockOutcome, resolveReasoningArtifactsForModel, resolveReasoningForModel, resolveResponse, resolveStrictMode, servedToolArgs, strictNoMatchLogLine, strictNoMatchMessage, strictOverrideField, toolArgsForWire } from "./helpers.js";
2
2
  import { calculateDelay, delay, writeErrorResponse } from "./sse-writer.js";
3
3
  import { applyChaosAsync } from "./chaos.js";
4
+ import { planMisbehavior, recordMisbehaviorOutcome } from "./misbehavior.js";
4
5
  import { matchFixtureDiagnostic, recordMatchOptions } from "./router.js";
5
6
  import { proxyAndRecord } from "./recorder.js";
6
7
  import { createInterruptionSignal } from "./interruption.js";
@@ -385,14 +386,7 @@ function buildClaudeToolCallStreamEvents(toolCalls, model, chunkSize, logger, re
385
386
  }
386
387
  for (const tc of toolCalls) {
387
388
  const toolUseId = tc.id || generateToolUseId();
388
- let argsObj;
389
- try {
390
- argsObj = JSON.parse(tc.arguments || "{}");
391
- } catch {
392
- logger.warn(`Malformed JSON in fixture tool call arguments for "${tc.name}": ${tc.arguments}`);
393
- argsObj = {};
394
- }
395
- const argsJson = JSON.stringify(argsObj);
389
+ const argsJson = servedToolArgs(tc, "string", logger).text;
396
390
  events.push({
397
391
  type: "content_block_start",
398
392
  index: blockIndex,
@@ -481,13 +475,7 @@ function buildClaudeToolCallResponse(toolCalls, model, logger, reasoning, overri
481
475
  signature: reasoningSignature ?? PLACEHOLDER_SIGNATURE
482
476
  });
483
477
  for (const tc of toolCalls) {
484
- let argsObj;
485
- try {
486
- argsObj = JSON.parse(tc.arguments || "{}");
487
- } catch {
488
- logger.warn(`Malformed JSON in fixture tool call arguments for "${tc.name}": ${tc.arguments}`);
489
- argsObj = {};
490
- }
478
+ const argsObj = servedToolArgs(tc, "object", logger).value;
491
479
  contentBlocks.push({
492
480
  type: "tool_use",
493
481
  id: tc.id || generateToolUseId(),
@@ -591,14 +579,7 @@ function buildClaudeContentWithToolCallsStreamEvents(content, toolCalls, model,
591
579
  blockIndex++;
592
580
  } else {
593
581
  const toolUseId = block.id || generateToolUseId();
594
- let argsObj;
595
- try {
596
- argsObj = JSON.parse(block.arguments || "{}");
597
- } catch {
598
- logger.warn(`Malformed JSON in fixture tool call arguments for "${block.name}": ${block.arguments}`);
599
- argsObj = {};
600
- }
601
- const argsJson = JSON.stringify(argsObj);
582
+ const argsJson = servedToolArgs(block, "string", logger).text;
602
583
  events.push({
603
584
  type: "content_block_start",
604
585
  index: blockIndex,
@@ -663,14 +644,7 @@ function buildClaudeContentWithToolCallsStreamEvents(content, toolCalls, model,
663
644
  blockIndex++;
664
645
  for (const tc of toolCalls) {
665
646
  const toolUseId = tc.id || generateToolUseId();
666
- let argsObj;
667
- try {
668
- argsObj = JSON.parse(tc.arguments || "{}");
669
- } catch {
670
- logger.warn(`Malformed JSON in fixture tool call arguments for "${tc.name}": ${tc.arguments}`);
671
- argsObj = {};
672
- }
673
- const argsJson = JSON.stringify(argsObj);
647
+ const argsJson = servedToolArgs(tc, "string", logger).text;
674
648
  events.push({
675
649
  type: "content_block_start",
676
650
  index: blockIndex,
@@ -718,13 +692,7 @@ function buildClaudeContentWithToolCallsResponse(content, toolCalls, model, logg
718
692
  signature: reasoningSignature ?? PLACEHOLDER_SIGNATURE
719
693
  });
720
694
  const toolUseBlock = (tc) => {
721
- let argsObj;
722
- try {
723
- argsObj = JSON.parse(tc.arguments || "{}");
724
- } catch {
725
- logger.warn(`Malformed JSON in fixture tool call arguments for "${tc.name}": ${tc.arguments}`);
726
- argsObj = {};
727
- }
695
+ const argsObj = servedToolArgs(tc, "object", logger).value;
728
696
  return {
729
697
  type: "tool_use",
730
698
  id: tc.id || generateToolUseId(),
@@ -764,6 +732,227 @@ function buildClaudeContentWithToolCallsResponse(content, toolCalls, model, logg
764
732
  usage: claudeUsage(overrides)
765
733
  };
766
734
  }
735
+ /**
736
+ * Prepare an already-selected Claude fault once for Messages or InvokeModel.
737
+ * IDs, canonical served arguments and usage describe the full emitted output,
738
+ * even when the transport later interrupts delivery. Never mutates the plan.
739
+ */
740
+ function prepareClaudeMisbehavior(plan, context) {
741
+ const response = plan.response;
742
+ if (!(isTextResponse(response) || isToolCallResponse(response) || isCombinedFixtureResponse(response))) throw new TypeError("Claude misbehavior requires a chat response");
743
+ const outcome = isCombinedFixtureResponse(response) && response.blocks?.length ? resolveFixtureBlockOutcome(response.blocks) : void 0;
744
+ const toolCalls = (outcome?.toolCalls ?? ("toolCalls" in response ? response.toolCalls ?? [] : [])).map((call, index) => ({
745
+ ...call,
746
+ ...!context.stream && plan.summary.fault === "stop-length-mid-tool" && index === plan.summary.target?.index ? { arguments: "{}" } : {},
747
+ ...index === plan.duplicateId?.destinationIndex ? {} : { id: call.id || generateToolUseId() }
748
+ }));
749
+ if (plan.duplicateId) toolCalls[plan.duplicateId.destinationIndex].id = toolCalls[plan.duplicateId.sourceIndex].id;
750
+ let preparedResponse = { ...response };
751
+ delete preparedResponse.usage;
752
+ if (outcome) {
753
+ let index = 0;
754
+ preparedResponse = {
755
+ ...preparedResponse,
756
+ content: outcome.content,
757
+ toolCalls,
758
+ blocks: outcome.ordered.map((block) => block.type === "text" ? { ...block } : {
759
+ ...block,
760
+ ...toolCalls[index++]
761
+ })
762
+ };
763
+ } else if ("toolCalls" in response) preparedResponse = {
764
+ ...preparedResponse,
765
+ toolCalls
766
+ };
767
+ const servedToolCalls = toolCalls.map((call) => {
768
+ const args = toolArgsForWire(call);
769
+ return {
770
+ name: call.name,
771
+ id: call.id,
772
+ arguments: args.kind === "parsed" ? args.text : args.raw
773
+ };
774
+ });
775
+ const preparedPlan = {
776
+ ...plan,
777
+ response: preparedResponse,
778
+ summary: {
779
+ ...plan.summary,
780
+ servedToolCalls
781
+ }
782
+ };
783
+ const reasoning = plan.summary.fault === "reasoning-only" ? plan.reasoning ?? "" : resolveReasoningForModel(response.reasoning, context.request.model, context.strict, context.logger);
784
+ const artifacts = resolveReasoningArtifactsForModel(response.reasoningSignature, response.redactedThinking, context.request.model, context.strict, context.logger);
785
+ const content = outcome?.content ?? ("content" in response ? response.content ?? "" : "");
786
+ const usage = {
787
+ input_tokens: estimatePromptTokens(context.request.messages),
788
+ output_tokens: estimateTokens(content + servedToolCalls.map((call) => call.name + call.arguments).join("") + (reasoning ?? "") + (plan.refusal ?? ""))
789
+ };
790
+ const overrides = {
791
+ ...extractOverrides(response),
792
+ ...plan.stop === "length" ? { finishReason: "length" } : {},
793
+ usage
794
+ };
795
+ const model = context.request.model;
796
+ if (plan.summary.fault === "reasoning-only") {
797
+ const thought = reasoning ?? "";
798
+ const signature = artifacts.reasoningSignature ?? PLACEHOLDER_SIGNATURE;
799
+ const body = {
800
+ id: overrides.id ?? generateMessageId(),
801
+ type: "message",
802
+ role: overrides.role ?? "assistant",
803
+ content: [{
804
+ type: "thinking",
805
+ thinking: thought,
806
+ signature
807
+ }],
808
+ model: overrides.model ?? model,
809
+ stop_reason: "max_tokens",
810
+ stop_sequence: null,
811
+ usage
812
+ };
813
+ if (!context.stream) return {
814
+ stream: false,
815
+ body,
816
+ plan: preparedPlan,
817
+ usage
818
+ };
819
+ const events = [{
820
+ type: "message_start",
821
+ message: {
822
+ ...body,
823
+ content: [],
824
+ stop_reason: null
825
+ }
826
+ }, {
827
+ type: "content_block_start",
828
+ index: 0,
829
+ content_block: {
830
+ type: "thinking",
831
+ thinking: "",
832
+ signature: ""
833
+ }
834
+ }];
835
+ const size = Math.max(1, context.chunkSize);
836
+ for (let offset = 0; offset < thought.length; offset += size) events.push({
837
+ type: "content_block_delta",
838
+ index: 0,
839
+ delta: {
840
+ type: "thinking_delta",
841
+ thinking: thought.slice(offset, offset + size)
842
+ }
843
+ });
844
+ events.push({
845
+ type: "content_block_delta",
846
+ index: 0,
847
+ delta: {
848
+ type: "signature_delta",
849
+ signature
850
+ }
851
+ }, {
852
+ type: "content_block_stop",
853
+ index: 0
854
+ }, {
855
+ type: "message_delta",
856
+ delta: {
857
+ stop_reason: "max_tokens",
858
+ stop_sequence: null
859
+ },
860
+ usage: { output_tokens: usage.output_tokens }
861
+ }, { type: "message_stop" });
862
+ return {
863
+ stream: true,
864
+ events,
865
+ plan: preparedPlan,
866
+ usage
867
+ };
868
+ }
869
+ if (plan.summary.fault === "empty-response" || plan.summary.fault === "refusal") {
870
+ const stop = plan.stop === "refusal" ? "refusal" : "end_turn";
871
+ const detail = plan.stop === "refusal" ? { stop_details: {
872
+ type: "refusal",
873
+ category: plan.refusalCategory ?? null,
874
+ explanation: plan.refusal ?? ""
875
+ } } : {};
876
+ const body = {
877
+ id: overrides.id ?? generateMessageId(),
878
+ type: "message",
879
+ role: overrides.role ?? "assistant",
880
+ content: [],
881
+ model: overrides.model ?? model,
882
+ stop_reason: stop,
883
+ stop_sequence: null,
884
+ ...detail,
885
+ usage
886
+ };
887
+ if (!context.stream) return {
888
+ stream: false,
889
+ body,
890
+ plan: preparedPlan,
891
+ usage
892
+ };
893
+ return {
894
+ stream: true,
895
+ events: [
896
+ {
897
+ type: "message_start",
898
+ message: {
899
+ ...body,
900
+ stop_reason: null,
901
+ ..."stop_details" in detail ? { stop_details: void 0 } : {}
902
+ }
903
+ },
904
+ {
905
+ type: "message_delta",
906
+ delta: {
907
+ stop_reason: stop,
908
+ stop_sequence: null,
909
+ ...detail
910
+ },
911
+ usage: { output_tokens: usage.output_tokens }
912
+ },
913
+ { type: "message_stop" }
914
+ ],
915
+ plan: preparedPlan,
916
+ usage
917
+ };
918
+ }
919
+ if (context.stream) {
920
+ const args = [
921
+ reasoning,
922
+ overrides,
923
+ artifacts.reasoningSignature,
924
+ artifacts.redactedThinking
925
+ ];
926
+ const events = isCombinedFixtureResponse(preparedResponse) ? buildClaudeContentWithToolCallsStreamEvents(preparedResponse.content ?? "", toolCalls, model, Math.max(1, context.chunkSize), context.logger, ...args, preparedResponse.blocks) : isToolCallResponse(preparedResponse) ? buildClaudeToolCallStreamEvents(toolCalls, model, Math.max(1, context.chunkSize), context.logger, ...args) : buildClaudeTextStreamEvents(content, model, Math.max(1, context.chunkSize), ...args);
927
+ if (plan.summary.fault === "stop-length-mid-tool") {
928
+ const cutIndex = events.filter((event) => event.type === "content_block_start" && isJsonObject(event.content_block) && event.content_block.type === "tool_use")[plan.target?.index ?? 0]?.index;
929
+ return {
930
+ stream: true,
931
+ events: events.filter((event) => !(event.type === "content_block_stop" && event.index === cutIndex)),
932
+ plan: preparedPlan,
933
+ usage
934
+ };
935
+ }
936
+ return {
937
+ stream: true,
938
+ events,
939
+ plan: preparedPlan,
940
+ usage
941
+ };
942
+ }
943
+ const args = [
944
+ reasoning,
945
+ overrides,
946
+ artifacts.reasoningSignature,
947
+ artifacts.redactedThinking
948
+ ];
949
+ return {
950
+ stream: false,
951
+ body: isCombinedFixtureResponse(preparedResponse) ? buildClaudeContentWithToolCallsResponse(preparedResponse.content ?? "", toolCalls, model, context.logger, ...args, preparedResponse.blocks) : isToolCallResponse(preparedResponse) ? buildClaudeToolCallResponse(toolCalls, model, context.logger, ...args) : buildClaudeTextResponse(content, model, ...args),
952
+ plan: preparedPlan,
953
+ usage
954
+ };
955
+ }
767
956
  async function writeClaudeSSEStream(res, events, optionsOrLatency) {
768
957
  const opts = typeof optionsOrLatency === "number" ? { latency: optionsOrLatency } : optionsOrLatency ?? {};
769
958
  const latency = opts.latency ?? 0;
@@ -972,20 +1161,111 @@ async function handleMessages(req, res, raw, fixtures, journal, defaults, setCor
972
1161
  return;
973
1162
  }
974
1163
  const response = await resolveResponse(fixture, completionReq);
1164
+ const misbehavior = planMisbehavior({
1165
+ wire: "anthropic",
1166
+ fixture,
1167
+ response,
1168
+ request: completionReq,
1169
+ stream: claudeReq.stream === true,
1170
+ emitsToolCallIds: true,
1171
+ defaults,
1172
+ rawHeaders: req.headers,
1173
+ url: req.url
1174
+ });
1175
+ if (misbehavior.kind === "error") {
1176
+ recordMisbehaviorOutcome({
1177
+ entry: journal.add({
1178
+ method: req.method ?? "POST",
1179
+ path: req.url ?? "/v1/messages",
1180
+ headers: flattenHeaders(req.headers),
1181
+ body: completionReq,
1182
+ response: {
1183
+ status: misbehavior.status,
1184
+ fixture
1185
+ }
1186
+ }),
1187
+ summary: misbehavior.summary,
1188
+ defaults,
1189
+ testId
1190
+ });
1191
+ if (!misbehavior.summary?.evaluations.length) logger.error(`POST /v1/messages: ${misbehavior.message}`);
1192
+ writeErrorResponse(res, misbehavior.status, JSON.stringify({
1193
+ type: "error",
1194
+ error: {
1195
+ type: misbehavior.status === 400 ? "invalid_request_error" : "api_error",
1196
+ code: misbehavior.code,
1197
+ message: misbehavior.message
1198
+ }
1199
+ }));
1200
+ return;
1201
+ }
975
1202
  const latency = fixture.latency ?? defaults.latency;
976
1203
  const chunkSize = Math.max(1, fixture.chunkSize ?? defaults.chunkSize);
977
- if (isErrorResponse(response)) {
978
- const status = response.status ?? 500;
979
- journal.add({
1204
+ if (misbehavior.kind === "applied") {
1205
+ const prepared = prepareClaudeMisbehavior(misbehavior, {
1206
+ request: completionReq,
1207
+ stream: claudeReq.stream === true,
1208
+ chunkSize,
1209
+ logger,
1210
+ strict: resolveStrictMode(defaults.strict, req.headers)
1211
+ });
1212
+ const entry = journal.add({
980
1213
  method: req.method ?? "POST",
981
1214
  path: req.url ?? "/v1/messages",
982
1215
  headers: flattenHeaders(req.headers),
983
1216
  body: completionReq,
984
1217
  response: {
985
- status,
1218
+ status: 200,
986
1219
  fixture
987
1220
  }
988
1221
  });
1222
+ recordMisbehaviorOutcome({
1223
+ entry,
1224
+ summary: prepared.plan.summary,
1225
+ defaults,
1226
+ testId
1227
+ });
1228
+ if (!prepared.stream) {
1229
+ res.writeHead(200, { "Content-Type": "application/json" });
1230
+ res.end(JSON.stringify(prepared.body));
1231
+ } else {
1232
+ const interruption = createInterruptionSignal(fixture);
1233
+ try {
1234
+ if (!await writeClaudeSSEStream(res, prepared.events, {
1235
+ latency,
1236
+ streamingProfile: fixture.streamingProfile,
1237
+ recordedTimings: fixture.recordedTimings,
1238
+ replaySpeed: fixture.replaySpeed ?? defaults.replaySpeed,
1239
+ signal: interruption?.signal,
1240
+ onChunkSent: interruption?.tick
1241
+ })) {
1242
+ if (!res.writableEnded) res.destroy();
1243
+ entry.response.interrupted = true;
1244
+ entry.response.interruptReason = interruption?.reason();
1245
+ }
1246
+ } finally {
1247
+ interruption?.cleanup();
1248
+ }
1249
+ }
1250
+ return;
1251
+ }
1252
+ if (isErrorResponse(response)) {
1253
+ const status = response.status ?? 500;
1254
+ recordMisbehaviorOutcome({
1255
+ entry: journal.add({
1256
+ method: req.method ?? "POST",
1257
+ path: req.url ?? "/v1/messages",
1258
+ headers: flattenHeaders(req.headers),
1259
+ body: completionReq,
1260
+ response: {
1261
+ status,
1262
+ fixture
1263
+ }
1264
+ }),
1265
+ summary: misbehavior.summary,
1266
+ defaults,
1267
+ testId
1268
+ });
989
1269
  const anthropicError = {
990
1270
  type: "error",
991
1271
  error: {
@@ -996,7 +1276,7 @@ async function handleMessages(req, res, raw, fixtures, journal, defaults, setCor
996
1276
  writeErrorResponse(res, status, JSON.stringify(anthropicError), { retryAfter: response.retryAfter });
997
1277
  return;
998
1278
  }
999
- if (isContentWithToolCallsResponse(response)) {
1279
+ if (isCombinedFixtureResponse(response)) {
1000
1280
  if (response.webSearches?.length) logger.warn("webSearches in fixture response are not supported for Claude Messages API — ignoring");
1001
1281
  const overrides = extractOverrides(response);
1002
1282
  const effectiveStrict = resolveStrictMode(defaults.strict, req.headers);
@@ -1012,12 +1292,19 @@ async function handleMessages(req, res, raw, fixtures, journal, defaults, setCor
1012
1292
  fixture
1013
1293
  }
1014
1294
  });
1295
+ recordMisbehaviorOutcome({
1296
+ entry: journalEntry,
1297
+ summary: misbehavior.summary,
1298
+ defaults,
1299
+ testId
1300
+ });
1301
+ const functionToolCalls = requireEmittedFunctionToolCalls(response, "Anthropic Messages");
1015
1302
  if (claudeReq.stream !== true) {
1016
- const body = buildClaudeContentWithToolCallsResponse(response.content ?? "", response.toolCalls ?? [], completionReq.model, logger, effReasoning, overrides, effReasoningSignature, effRedactedThinking, response.blocks);
1303
+ const body = buildClaudeContentWithToolCallsResponse(response.content ?? "", functionToolCalls, completionReq.model, logger, effReasoning, overrides, effReasoningSignature, effRedactedThinking, response.blocks);
1017
1304
  res.writeHead(200, { "Content-Type": "application/json" });
1018
1305
  res.end(JSON.stringify(body));
1019
1306
  } else {
1020
- const events = buildClaudeContentWithToolCallsStreamEvents(response.content ?? "", response.toolCalls ?? [], completionReq.model, chunkSize, logger, effReasoning, overrides, effReasoningSignature, effRedactedThinking, response.blocks);
1307
+ const events = buildClaudeContentWithToolCallsStreamEvents(response.content ?? "", functionToolCalls, completionReq.model, chunkSize, logger, effReasoning, overrides, effReasoningSignature, effRedactedThinking, response.blocks);
1021
1308
  const interruption = createInterruptionSignal(fixture);
1022
1309
  if (!await writeClaudeSSEStream(res, events, {
1023
1310
  latency,
@@ -1051,6 +1338,12 @@ async function handleMessages(req, res, raw, fixtures, journal, defaults, setCor
1051
1338
  fixture
1052
1339
  }
1053
1340
  });
1341
+ recordMisbehaviorOutcome({
1342
+ entry: journalEntry,
1343
+ summary: misbehavior.summary,
1344
+ defaults,
1345
+ testId
1346
+ });
1054
1347
  if (claudeReq.stream !== true) {
1055
1348
  const body = buildClaudeTextResponse(response.content, completionReq.model, effReasoning, overrides, effReasoningSignature, effRedactedThinking);
1056
1349
  res.writeHead(200, { "Content-Type": "application/json" });
@@ -1090,12 +1383,19 @@ async function handleMessages(req, res, raw, fixtures, journal, defaults, setCor
1090
1383
  fixture
1091
1384
  }
1092
1385
  });
1386
+ recordMisbehaviorOutcome({
1387
+ entry: journalEntry,
1388
+ summary: misbehavior.summary,
1389
+ defaults,
1390
+ testId
1391
+ });
1392
+ const functionToolCalls = requireEmittedFunctionToolCalls(response, "Anthropic Messages");
1093
1393
  if (claudeReq.stream !== true) {
1094
- const body = buildClaudeToolCallResponse(response.toolCalls, completionReq.model, logger, effReasoning, overrides, effReasoningSignature, effRedactedThinking);
1394
+ const body = buildClaudeToolCallResponse(functionToolCalls, completionReq.model, logger, effReasoning, overrides, effReasoningSignature, effRedactedThinking);
1095
1395
  res.writeHead(200, { "Content-Type": "application/json" });
1096
1396
  res.end(JSON.stringify(body));
1097
1397
  } else {
1098
- const events = buildClaudeToolCallStreamEvents(response.toolCalls, completionReq.model, chunkSize, logger, effReasoning, overrides, effReasoningSignature, effRedactedThinking);
1398
+ const events = buildClaudeToolCallStreamEvents(functionToolCalls, completionReq.model, chunkSize, logger, effReasoning, overrides, effReasoningSignature, effRedactedThinking);
1099
1399
  const interruption = createInterruptionSignal(fixture);
1100
1400
  if (!await writeClaudeSSEStream(res, events, {
1101
1401
  latency,
@@ -1113,15 +1413,20 @@ async function handleMessages(req, res, raw, fixtures, journal, defaults, setCor
1113
1413
  }
1114
1414
  return;
1115
1415
  }
1116
- journal.add({
1117
- method: req.method ?? "POST",
1118
- path: req.url ?? "/v1/messages",
1119
- headers: flattenHeaders(req.headers),
1120
- body: completionReq,
1121
- response: {
1122
- status: 500,
1123
- fixture
1124
- }
1416
+ recordMisbehaviorOutcome({
1417
+ entry: journal.add({
1418
+ method: req.method ?? "POST",
1419
+ path: req.url ?? "/v1/messages",
1420
+ headers: flattenHeaders(req.headers),
1421
+ body: completionReq,
1422
+ response: {
1423
+ status: 500,
1424
+ fixture
1425
+ }
1426
+ }),
1427
+ summary: misbehavior.summary,
1428
+ defaults,
1429
+ testId
1125
1430
  });
1126
1431
  writeErrorResponse(res, 500, JSON.stringify({ error: {
1127
1432
  message: "Fixture response did not match any known type",
@@ -1130,5 +1435,5 @@ async function handleMessages(req, res, raw, fixtures, journal, defaults, setCor
1130
1435
  }
1131
1436
 
1132
1437
  //#endregion
1133
- export { handleMessages };
1438
+ export { handleMessages, prepareClaudeMisbehavior };
1134
1439
  //# sourceMappingURL=messages.js.map