@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.
Files changed (183) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/CHANGELOG.md +12 -1
  4. package/README.md +4 -1
  5. package/dist/aws-event-stream.cjs +20 -23
  6. package/dist/aws-event-stream.cjs.map +1 -1
  7. package/dist/aws-event-stream.d.cts +0 -1
  8. package/dist/aws-event-stream.d.ts +0 -1
  9. package/dist/aws-event-stream.js +20 -24
  10. package/dist/aws-event-stream.js.map +1 -1
  11. package/dist/bedrock-converse.cjs +79 -82
  12. package/dist/bedrock-converse.cjs.map +1 -1
  13. package/dist/bedrock-converse.d.cts.map +1 -1
  14. package/dist/bedrock-converse.d.ts.map +1 -1
  15. package/dist/bedrock-converse.js +80 -83
  16. package/dist/bedrock-converse.js.map +1 -1
  17. package/dist/bedrock.cjs +112 -121
  18. package/dist/bedrock.cjs.map +1 -1
  19. package/dist/bedrock.d.cts.map +1 -1
  20. package/dist/bedrock.d.ts.map +1 -1
  21. package/dist/bedrock.js +113 -122
  22. package/dist/bedrock.js.map +1 -1
  23. package/dist/cohere.cjs +58 -106
  24. package/dist/cohere.cjs.map +1 -1
  25. package/dist/cohere.d.cts.map +1 -1
  26. package/dist/cohere.d.ts.map +1 -1
  27. package/dist/cohere.js +60 -108
  28. package/dist/cohere.js.map +1 -1
  29. package/dist/config-loader.d.cts.map +1 -1
  30. package/dist/config-loader.d.ts.map +1 -1
  31. package/dist/fixture-loader.cjs +103 -2
  32. package/dist/fixture-loader.cjs.map +1 -1
  33. package/dist/fixture-loader.d.cts.map +1 -1
  34. package/dist/fixture-loader.d.ts.map +1 -1
  35. package/dist/fixture-loader.js +104 -4
  36. package/dist/fixture-loader.js.map +1 -1
  37. package/dist/gemini-interactions.cjs +63 -121
  38. package/dist/gemini-interactions.cjs.map +1 -1
  39. package/dist/gemini-interactions.d.cts.map +1 -1
  40. package/dist/gemini-interactions.d.ts.map +1 -1
  41. package/dist/gemini-interactions.js +65 -123
  42. package/dist/gemini-interactions.js.map +1 -1
  43. package/dist/gemini.cjs +242 -155
  44. package/dist/gemini.cjs.map +1 -1
  45. package/dist/gemini.d.cts.map +1 -1
  46. package/dist/gemini.d.ts.map +1 -1
  47. package/dist/gemini.js +244 -157
  48. package/dist/gemini.js.map +1 -1
  49. package/dist/helpers.cjs +400 -7
  50. package/dist/helpers.cjs.map +1 -1
  51. package/dist/helpers.d.cts +51 -1
  52. package/dist/helpers.d.cts.map +1 -1
  53. package/dist/helpers.d.ts +51 -1
  54. package/dist/helpers.d.ts.map +1 -1
  55. package/dist/helpers.js +391 -8
  56. package/dist/helpers.js.map +1 -1
  57. package/dist/index.cjs +4 -2
  58. package/dist/index.d.cts +4 -4
  59. package/dist/index.d.ts +4 -4
  60. package/dist/index.js +4 -4
  61. package/dist/mcp-fakes.d.cts.map +1 -1
  62. package/dist/messages.cjs +159 -141
  63. package/dist/messages.cjs.map +1 -1
  64. package/dist/messages.d.cts.map +1 -1
  65. package/dist/messages.d.ts.map +1 -1
  66. package/dist/messages.js +161 -143
  67. package/dist/messages.js.map +1 -1
  68. package/dist/misbehavior.cjs +62 -3
  69. package/dist/misbehavior.cjs.map +1 -1
  70. package/dist/misbehavior.d.cts.map +1 -1
  71. package/dist/misbehavior.d.ts.map +1 -1
  72. package/dist/misbehavior.js +63 -4
  73. package/dist/misbehavior.js.map +1 -1
  74. package/dist/ndjson-writer.cjs +21 -23
  75. package/dist/ndjson-writer.cjs.map +1 -1
  76. package/dist/ndjson-writer.d.cts +0 -1
  77. package/dist/ndjson-writer.d.cts.map +1 -1
  78. package/dist/ndjson-writer.d.ts +0 -1
  79. package/dist/ndjson-writer.d.ts.map +1 -1
  80. package/dist/ndjson-writer.js +21 -24
  81. package/dist/ndjson-writer.js.map +1 -1
  82. package/dist/ollama.cjs +153 -147
  83. package/dist/ollama.cjs.map +1 -1
  84. package/dist/ollama.d.cts.map +1 -1
  85. package/dist/ollama.d.ts.map +1 -1
  86. package/dist/ollama.js +155 -149
  87. package/dist/ollama.js.map +1 -1
  88. package/dist/openrouter-chat.cjs +1 -1
  89. package/dist/openrouter-chat.js +1 -1
  90. package/dist/raw-validation.cjs +177 -0
  91. package/dist/raw-validation.cjs.map +1 -0
  92. package/dist/raw-validation.js +176 -0
  93. package/dist/raw-validation.js.map +1 -0
  94. package/dist/response/deliver.cjs +144 -0
  95. package/dist/response/deliver.cjs.map +1 -0
  96. package/dist/response/deliver.js +143 -0
  97. package/dist/response/deliver.js.map +1 -0
  98. package/dist/response/frame-loop.cjs +102 -0
  99. package/dist/response/frame-loop.cjs.map +1 -0
  100. package/dist/response/frame-loop.js +100 -0
  101. package/dist/response/frame-loop.js.map +1 -0
  102. package/dist/response/lifecycle.cjs +223 -0
  103. package/dist/response/lifecycle.cjs.map +1 -0
  104. package/dist/response/lifecycle.js +211 -0
  105. package/dist/response/lifecycle.js.map +1 -0
  106. package/dist/response/proxy.cjs +71 -0
  107. package/dist/response/proxy.cjs.map +1 -0
  108. package/dist/response/proxy.js +70 -0
  109. package/dist/response/proxy.js.map +1 -0
  110. package/dist/response/raw.cjs +228 -0
  111. package/dist/response/raw.cjs.map +1 -0
  112. package/dist/response/raw.js +227 -0
  113. package/dist/response/raw.js.map +1 -0
  114. package/dist/response/timing.cjs +157 -0
  115. package/dist/response/timing.cjs.map +1 -0
  116. package/dist/response/timing.js +150 -0
  117. package/dist/response/timing.js.map +1 -0
  118. package/dist/responses.cjs +269 -148
  119. package/dist/responses.cjs.map +1 -1
  120. package/dist/responses.d.cts.map +1 -1
  121. package/dist/responses.d.ts.map +1 -1
  122. package/dist/responses.js +270 -149
  123. package/dist/responses.js.map +1 -1
  124. package/dist/router.cjs +20 -4
  125. package/dist/router.cjs.map +1 -1
  126. package/dist/router.d.cts +7 -1
  127. package/dist/router.d.cts.map +1 -1
  128. package/dist/router.d.ts +7 -1
  129. package/dist/router.d.ts.map +1 -1
  130. package/dist/router.js +21 -5
  131. package/dist/router.js.map +1 -1
  132. package/dist/server.cjs +89 -120
  133. package/dist/server.cjs.map +1 -1
  134. package/dist/server.d.cts.map +1 -1
  135. package/dist/server.d.ts.map +1 -1
  136. package/dist/server.js +91 -122
  137. package/dist/server.js.map +1 -1
  138. package/dist/sse-writer.cjs +49 -30
  139. package/dist/sse-writer.cjs.map +1 -1
  140. package/dist/sse-writer.d.cts +0 -1
  141. package/dist/sse-writer.d.cts.map +1 -1
  142. package/dist/sse-writer.d.ts +0 -1
  143. package/dist/sse-writer.d.ts.map +1 -1
  144. package/dist/sse-writer.js +49 -31
  145. package/dist/sse-writer.js.map +1 -1
  146. package/dist/transcription.cjs +19 -33
  147. package/dist/transcription.cjs.map +1 -1
  148. package/dist/transcription.d.cts.map +1 -1
  149. package/dist/transcription.d.ts.map +1 -1
  150. package/dist/transcription.js +20 -34
  151. package/dist/transcription.js.map +1 -1
  152. package/dist/types.cjs.map +1 -1
  153. package/dist/types.d.cts +173 -10
  154. package/dist/types.d.cts.map +1 -1
  155. package/dist/types.d.ts +173 -10
  156. package/dist/types.d.ts.map +1 -1
  157. package/dist/types.js.map +1 -1
  158. package/dist/ws-framing.cjs +12 -6
  159. package/dist/ws-framing.cjs.map +1 -1
  160. package/dist/ws-framing.d.cts +2 -0
  161. package/dist/ws-framing.d.cts.map +1 -1
  162. package/dist/ws-framing.d.ts +2 -0
  163. package/dist/ws-framing.d.ts.map +1 -1
  164. package/dist/ws-framing.js +12 -6
  165. package/dist/ws-framing.js.map +1 -1
  166. package/dist/ws-gemini-live.cjs +120 -93
  167. package/dist/ws-gemini-live.cjs.map +1 -1
  168. package/dist/ws-gemini-live.js +120 -93
  169. package/dist/ws-gemini-live.js.map +1 -1
  170. package/dist/ws-realtime.cjs +80 -70
  171. package/dist/ws-realtime.cjs.map +1 -1
  172. package/dist/ws-realtime.d.cts.map +1 -1
  173. package/dist/ws-realtime.d.ts.map +1 -1
  174. package/dist/ws-realtime.js +80 -70
  175. package/dist/ws-realtime.js.map +1 -1
  176. package/dist/ws-responses.cjs +72 -53
  177. package/dist/ws-responses.cjs.map +1 -1
  178. package/dist/ws-responses.d.cts.map +1 -1
  179. package/dist/ws-responses.d.ts.map +1 -1
  180. package/dist/ws-responses.js +73 -54
  181. package/dist/ws-responses.js.map +1 -1
  182. package/package.json +1 -1
  183. package/skills/write-fixtures/SKILL.md +34 -2
@@ -1,12 +1,15 @@
1
1
  import { renderError } from "./response/errors.js";
2
2
  import { resolveRequestDecisions, strictOverrideField } from "./response/policy.js";
3
- import { extractOverrides, flattenHeaders, generateToolCallId, getContext, getTestId, isCombinedFixtureResponse, isErrorResponse, isJsonObject, isTextResponse, isToolCallResponse, prepareOpenAIChatMisbehavior, requireEmittedFunctionToolCalls, resolveFixtureBlockOutcome, resolveOpenAIChatMisbehaviorUsage, resolveResponse, servedToolArgs, strictNoMatchLogLine, strictNoMatchMessage } from "./helpers.js";
4
- import { calculateDelay, delay, writeErrorResponse } from "./sse-writer.js";
3
+ import { extractOverrides, flattenHeaders, generateToolCallId, getContext, getTestId, isCombinedFixtureResponse, isErrorResponse, isJsonObject, isRawResponse, isTextResponse, isToolCallResponse, prepareOpenAIChatMisbehavior, requireEmittedFunctionToolCalls, resolveFixtureBlockOutcome, resolveOpenAIChatMisbehaviorUsage, resolveResponse, servedToolArgs, strictNoMatchLogLine, strictNoMatchMessage } from "./helpers.js";
4
+ import { sseCodec, writeErrorResponse } from "./sse-writer.js";
5
5
  import { applyChaosAsync } from "./chaos.js";
6
6
  import { planMisbehavior, recordMisbehaviorOutcome } from "./misbehavior.js";
7
7
  import { matchFixtureDiagnostic } from "./router.js";
8
8
  import { proxyAndRecord } from "./recorder.js";
9
- import { createInterruptionSignal } from "./interruption.js";
9
+ import { misbehaviorPayloadLength, servedPayloadLength } from "./response/timing.js";
10
+ import { deliverJson, deliverStream } from "./response/deliver.js";
11
+ import { proxyInterruption, withHangUpDetection } from "./response/proxy.js";
12
+ import { serveRawResponse } from "./response/raw.js";
10
13
  import { render } from "./response/usage/codecs/interactions.js";
11
14
 
12
15
  //#region src/gemini-interactions.ts
@@ -489,33 +492,10 @@ function buildInteractionsContentWithToolCallsSSEEvents(content, toolCalls, inte
489
492
  });
490
493
  return events;
491
494
  }
492
- async function writeGeminiInteractionsSSEStream(res, events, optionsOrLatency) {
493
- const opts = typeof optionsOrLatency === "number" ? { latency: optionsOrLatency } : optionsOrLatency ?? {};
494
- const latency = opts.latency ?? 0;
495
- const profile = opts.streamingProfile;
496
- const { recordedTimings, replaySpeed } = opts;
497
- const signal = opts.signal;
498
- const onChunkSent = opts.onChunkSent;
499
- if (res.writableEnded) return true;
500
- res.setHeader("Content-Type", "text/event-stream");
501
- res.setHeader("Cache-Control", "no-cache");
502
- res.setHeader("Connection", "keep-alive");
503
- let chunkIndex = 0;
504
- for (const event of events) {
505
- const chunkDelay = calculateDelay(chunkIndex, profile, latency, recordedTimings, replaySpeed);
506
- if (chunkDelay > 0) await delay(chunkDelay, signal);
507
- if (signal?.aborted) return false;
508
- if (res.writableEnded) return true;
509
- res.write(`data: ${JSON.stringify(event)}\n\n`);
510
- if (event.event_type === "step.delta") {
511
- onChunkSent?.();
512
- chunkIndex++;
513
- }
514
- if (signal?.aborted) return false;
515
- }
516
- if (!res.writableEnded) res.end();
517
- return true;
518
- }
495
+ const interactionsCodec = {
496
+ ...sseCodec({ done: false }),
497
+ counts: (event) => event.event_type === "step.delta"
498
+ };
519
499
  function prepareInteractionsMisbehavior(plan, streaming) {
520
500
  if (!streaming && plan.summary.fault === "stop-length-mid-tool") {
521
501
  const response = plan.response;
@@ -611,7 +591,7 @@ async function handleGeminiInteractions(req, res, raw, fixtures, journal, defaul
611
591
  const streaming = interactionsReq.stream !== false;
612
592
  const model = completionReq.model;
613
593
  const testId = getTestId(req);
614
- const { fixture, skippedBySequenceOrTurn } = matchFixtureDiagnostic(fixtures, completionReq, journal.getFixtureMatchCountsForTest(testId), defaults.requestTransform);
594
+ const { fixture, skippedBySequenceOrTurn } = matchFixtureDiagnostic(fixtures, completionReq, journal.getFixtureMatchCountsForTest(testId), defaults.requestTransform, { wire: "gemini-interactions" });
615
595
  if (fixture) journal.incrementFixtureMatchCount(fixture, fixtures, testId);
616
596
  if (await applyChaosAsync(res, fixture, defaults.chaos, req.headers, req.url, journal, {
617
597
  method: req.method ?? "POST",
@@ -643,7 +623,7 @@ async function handleGeminiInteractions(req, res, raw, fixtures, journal, defaul
643
623
  return;
644
624
  }
645
625
  if (decisions.upstream && defaults.record) {
646
- const outcome = await proxyAndRecord(req, res, completionReq, "gemini-interactions", urlPath, fixtures, defaults, raw);
626
+ const { outcome, clientHungUp } = await withHangUpDetection(req, res, () => proxyAndRecord(req, res, completionReq, "gemini-interactions", urlPath, fixtures, defaults, raw));
647
627
  if (outcome === "handled_by_hook") return;
648
628
  if (outcome !== "not_configured") {
649
629
  journal.add({
@@ -654,7 +634,8 @@ async function handleGeminiInteractions(req, res, raw, fixtures, journal, defaul
654
634
  response: {
655
635
  status: res.statusCode ?? 200,
656
636
  fixture: null,
657
- source: "proxy"
637
+ source: "proxy",
638
+ ...proxyInterruption(res, clientHungUp)
658
639
  }
659
640
  });
660
641
  return;
@@ -720,6 +701,42 @@ async function handleGeminiInteractions(req, res, raw, fixtures, journal, defaul
720
701
  const latency = fixture.latency ?? defaults.latency;
721
702
  const chunkSize = Math.max(1, fixture.chunkSize ?? defaults.chunkSize);
722
703
  const replaySpeed = fixture.replaySpeed ?? defaults.replaySpeed;
704
+ const streamTiming = {
705
+ latency,
706
+ streamingProfile: fixture.streamingProfile,
707
+ recordedTimings: fixture.recordedTimings,
708
+ replaySpeed
709
+ };
710
+ const applied = misbehavior.kind === "applied" ? misbehavior : void 0;
711
+ const jsonOpts = () => ({
712
+ surface: "other",
713
+ payloadLength: applied ? misbehaviorPayloadLength(applied, void 0) : servedPayloadLength(response),
714
+ chunkSize,
715
+ recordedTimings: fixture.recordedTimings,
716
+ duplicateId: !!applied?.duplicateId,
717
+ replaySpeed
718
+ });
719
+ const markIncomplete = (body) => applied?.summary.fault === "stop-length-mid-tool" ? {
720
+ ...body,
721
+ status: "incomplete"
722
+ } : body;
723
+ if (isRawResponse(response)) {
724
+ await serveRawResponse({
725
+ wire: "gemini-interactions",
726
+ stream: streaming,
727
+ req,
728
+ res,
729
+ fixture,
730
+ response,
731
+ body: completionReq,
732
+ journal,
733
+ defaults,
734
+ testId,
735
+ misbehaviorSummary: misbehavior.summary,
736
+ errorWire: "interactions"
737
+ });
738
+ return;
739
+ }
723
740
  if (isErrorResponse(response)) {
724
741
  const status = response.status ?? 500;
725
742
  recordMisbehaviorOutcome({
@@ -764,8 +781,8 @@ async function handleGeminiInteractions(req, res, raw, fixtures, journal, defaul
764
781
  const usage = render({ usage: appliedUsage }, "applied-reasoning-only");
765
782
  if (!streaming) {
766
783
  const overrides = isTextResponse(response) || isToolCallResponse(response) || isCombinedFixtureResponse(response) ? extractOverrides(response) : void 0;
767
- res.writeHead(200, { "Content-Type": "application/json" });
768
- res.end(JSON.stringify({
784
+ const reasoning = misbehavior.reasoning ?? "";
785
+ await deliverJson(res, journalEntry, fixture, () => ({
769
786
  id: interactionId,
770
787
  model: overrides?.model ?? model,
771
788
  status: "incomplete",
@@ -774,14 +791,14 @@ async function handleGeminiInteractions(req, res, raw, fixtures, journal, defaul
774
791
  signature: "",
775
792
  summary: [{
776
793
  type: "text",
777
- text: misbehavior.reasoning ?? ""
794
+ text: reasoning
778
795
  }]
779
796
  }],
780
797
  usage
781
- }));
798
+ }), jsonOpts());
782
799
  return;
783
800
  }
784
- const events = [
801
+ await deliverStream(res, journalEntry, fixture, interactionsCodec, [
785
802
  {
786
803
  event_type: "interaction.created",
787
804
  interaction: {
@@ -822,21 +839,7 @@ async function handleGeminiInteractions(req, res, raw, fixtures, journal, defaul
822
839
  },
823
840
  event_id: nextEventId()
824
841
  }
825
- ];
826
- const interruption = createInterruptionSignal(fixture);
827
- if (!await writeGeminiInteractionsSSEStream(res, events, {
828
- latency,
829
- streamingProfile: fixture.streamingProfile,
830
- recordedTimings: fixture.recordedTimings,
831
- replaySpeed,
832
- signal: interruption?.signal,
833
- onChunkSent: interruption?.tick
834
- })) {
835
- if (!res.writableEnded) res.destroy();
836
- journalEntry.response.interrupted = true;
837
- journalEntry.response.interruptReason = interruption?.reason();
838
- }
839
- interruption?.cleanup();
842
+ ], streamTiming);
840
843
  return;
841
844
  }
842
845
  if (isCombinedFixtureResponse(response)) {
@@ -864,29 +867,10 @@ async function handleGeminiInteractions(req, res, raw, fixtures, journal, defaul
864
867
  const functionToolCalls = requireEmittedFunctionToolCalls(response, "Gemini Interactions");
865
868
  const interactionId = nextInteractionId();
866
869
  if (!streaming) {
867
- const body = buildInteractionsContentWithToolCallsResponse(response.content ?? "", functionToolCalls, model, interactionId, logger, overrides, response.blocks);
868
- res.writeHead(200, { "Content-Type": "application/json" });
869
- res.end(JSON.stringify(misbehavior.kind === "applied" && misbehavior.summary.fault === "stop-length-mid-tool" ? {
870
- ...body,
871
- status: "incomplete"
872
- } : body));
873
- } else {
874
- const events = buildInteractionsContentWithToolCallsSSEEvents(response.content ?? "", functionToolCalls, interactionId, chunkSize, logger, overrides, response.blocks);
875
- const interruption = createInterruptionSignal(fixture);
876
- if (!await writeGeminiInteractionsSSEStream(res, finalizeInteractionsMisbehavior(events, misbehavior), {
877
- latency,
878
- streamingProfile: fixture.streamingProfile,
879
- recordedTimings: fixture.recordedTimings,
880
- replaySpeed,
881
- signal: interruption?.signal,
882
- onChunkSent: interruption?.tick
883
- })) {
884
- if (!res.writableEnded) res.destroy();
885
- journalEntry.response.interrupted = true;
886
- journalEntry.response.interruptReason = interruption?.reason();
887
- }
888
- interruption?.cleanup();
889
- }
870
+ const content = response.content ?? "";
871
+ const blocks = response.blocks;
872
+ await deliverJson(res, journalEntry, fixture, () => markIncomplete(buildInteractionsContentWithToolCallsResponse(content, functionToolCalls, model, interactionId, logger, overrides, blocks)), jsonOpts());
873
+ } else await deliverStream(res, journalEntry, fixture, interactionsCodec, finalizeInteractionsMisbehavior(buildInteractionsContentWithToolCallsSSEEvents(response.content ?? "", functionToolCalls, interactionId, chunkSize, logger, overrides, response.blocks), misbehavior), streamTiming);
890
874
  return;
891
875
  }
892
876
  if (isTextResponse(response)) {
@@ -913,29 +897,9 @@ async function handleGeminiInteractions(req, res, raw, fixtures, journal, defaul
913
897
  });
914
898
  const interactionId = nextInteractionId();
915
899
  if (!streaming) {
916
- const body = buildInteractionsTextResponse(response.content, model, interactionId, overrides);
917
- res.writeHead(200, { "Content-Type": "application/json" });
918
- res.end(JSON.stringify(misbehavior.kind === "applied" && misbehavior.summary.fault === "stop-length-mid-tool" ? {
919
- ...body,
920
- status: "incomplete"
921
- } : body));
922
- } else {
923
- const events = buildInteractionsTextSSEEvents(response.content, interactionId, chunkSize, overrides);
924
- const interruption = createInterruptionSignal(fixture);
925
- if (!await writeGeminiInteractionsSSEStream(res, finalizeInteractionsMisbehavior(events, misbehavior), {
926
- latency,
927
- streamingProfile: fixture.streamingProfile,
928
- recordedTimings: fixture.recordedTimings,
929
- replaySpeed,
930
- signal: interruption?.signal,
931
- onChunkSent: interruption?.tick
932
- })) {
933
- if (!res.writableEnded) res.destroy();
934
- journalEntry.response.interrupted = true;
935
- journalEntry.response.interruptReason = interruption?.reason();
936
- }
937
- interruption?.cleanup();
938
- }
900
+ const content = response.content;
901
+ await deliverJson(res, journalEntry, fixture, () => markIncomplete(buildInteractionsTextResponse(content, model, interactionId, overrides)), jsonOpts());
902
+ } else await deliverStream(res, journalEntry, fixture, interactionsCodec, finalizeInteractionsMisbehavior(buildInteractionsTextSSEEvents(response.content, interactionId, chunkSize, overrides), misbehavior), streamTiming);
939
903
  return;
940
904
  }
941
905
  if (isToolCallResponse(response)) {
@@ -962,30 +926,8 @@ async function handleGeminiInteractions(req, res, raw, fixtures, journal, defaul
962
926
  });
963
927
  const functionToolCalls = requireEmittedFunctionToolCalls(response, "Gemini Interactions");
964
928
  const interactionId = nextInteractionId();
965
- if (!streaming) {
966
- const body = buildInteractionsToolCallResponse(functionToolCalls, model, interactionId, logger, overrides);
967
- res.writeHead(200, { "Content-Type": "application/json" });
968
- res.end(JSON.stringify(misbehavior.kind === "applied" && misbehavior.summary.fault === "stop-length-mid-tool" ? {
969
- ...body,
970
- status: "incomplete"
971
- } : body));
972
- } else {
973
- const events = buildInteractionsToolCallSSEEvents(functionToolCalls, interactionId, logger, overrides);
974
- const interruption = createInterruptionSignal(fixture);
975
- if (!await writeGeminiInteractionsSSEStream(res, finalizeInteractionsMisbehavior(events, misbehavior), {
976
- latency,
977
- streamingProfile: fixture.streamingProfile,
978
- recordedTimings: fixture.recordedTimings,
979
- replaySpeed,
980
- signal: interruption?.signal,
981
- onChunkSent: interruption?.tick
982
- })) {
983
- if (!res.writableEnded) res.destroy();
984
- journalEntry.response.interrupted = true;
985
- journalEntry.response.interruptReason = interruption?.reason();
986
- }
987
- interruption?.cleanup();
988
- }
929
+ if (!streaming) await deliverJson(res, journalEntry, fixture, () => markIncomplete(buildInteractionsToolCallResponse(functionToolCalls, model, interactionId, logger, overrides)), jsonOpts());
930
+ else await deliverStream(res, journalEntry, fixture, interactionsCodec, finalizeInteractionsMisbehavior(buildInteractionsToolCallSSEEvents(functionToolCalls, interactionId, logger, overrides), misbehavior), streamTiming);
989
931
  return;
990
932
  }
991
933
  recordMisbehaviorOutcome({
@@ -1 +1 @@
1
- {"version":3,"file":"gemini-interactions.js","names":["renderInteractionsUsage"],"sources":["../src/gemini-interactions.ts"],"sourcesContent":["/**\n * Google Gemini Interactions API support.\n *\n * Translates incoming Interactions requests into the ChatCompletionRequest\n * format used by the fixture router, and converts fixture responses back\n * into the Gemini Interactions format — either a single JSON response or\n * an SSE stream with event_type-based framing.\n */\nimport { resolveRequestDecisions, strictOverrideField } from \"./response/policy.js\";\nimport { renderError } from \"./response/errors.js\";\n\nimport type * as http from \"node:http\";\nimport type {\n ChatCompletionRequest,\n ChatMessage,\n Fixture,\n FixtureBlock,\n HandlerDefaults,\n RecordedTimings,\n ResponseOverrides,\n StreamingProfile,\n ToolCall,\n ToolDefinition,\n} from \"./types.js\";\nimport {\n requireEmittedFunctionToolCalls,\n isTextResponse,\n isToolCallResponse,\n isCombinedFixtureResponse,\n isErrorResponse,\n extractOverrides,\n generateToolCallId,\n flattenHeaders,\n isJsonObject,\n getTestId,\n getContext,\n resolveResponse,\n strictNoMatchMessage,\n strictNoMatchLogLine,\n resolveFixtureBlockOutcome,\n servedToolArgs,\n prepareOpenAIChatMisbehavior,\n resolveOpenAIChatMisbehaviorUsage,\n} from \"./helpers.js\";\nimport { matchFixtureDiagnostic } from \"./router.js\";\nimport { writeErrorResponse, delay, calculateDelay } from \"./sse-writer.js\";\nimport { createInterruptionSignal } from \"./interruption.js\";\nimport type { Journal } from \"./journal.js\";\nimport type { Logger } from \"./logger.js\";\nimport { applyChaosAsync } from \"./chaos.js\";\nimport { proxyAndRecord } from \"./recorder.js\";\nimport { render as renderInteractionsUsage } from \"./response/usage/codecs/interactions.js\";\nimport {\n planMisbehavior,\n recordMisbehaviorOutcome,\n type MisbehaviorPlan,\n type MisbehaviorSkip,\n} from \"./misbehavior.js\";\n\n// ─── Interactions request types ────────────────────────────────────────────\n\ninterface InteractionsContentBlock {\n type: string;\n text?: string;\n name?: string;\n call_id?: string;\n id?: string;\n arguments?: Record<string, unknown>;\n output?: unknown;\n result?: unknown;\n}\n\ninterface InteractionsTurn {\n role: string;\n content?: InteractionsContentBlock[];\n parts?: InteractionsContentBlock[];\n}\n\n/**\n * Top-level Step envelope accepted by the live Gemini Interactions API.\n * The SDK's TypeScript union does not include Step[], but the wire contract\n * does — clients following the live API send these at the top level of `input`.\n * Discriminated by `type`; no `role` field (distinguishes from Turn[]).\n */\ninterface InteractionsStep {\n type: string;\n content?: InteractionsContentBlock[];\n call_id?: string;\n id?: string;\n name?: string;\n result?: unknown;\n output?: unknown;\n is_error?: boolean;\n signature?: string;\n}\n\n/** Step types whose payload is a tool/agent result keyed by call_id. */\nconst STEP_RESULT_TYPES = new Set<string>([\n \"function_result\",\n \"code_execution_result\",\n \"url_context_result\",\n \"google_search_result\",\n \"google_maps_result\",\n \"mcp_server_tool_result\",\n \"file_search_result\",\n]);\n\n/** All recognized top-level Step types (used as the Step[] discriminator). */\nconst STEP_TYPES = new Set<string>([\"user_input\", \"model_output\", ...STEP_RESULT_TYPES]);\n\ninterface InteractionsFunctionTool {\n type: \"function\";\n name: string;\n description?: string;\n parameters?: object;\n}\n\ninterface InteractionsRequest {\n model?: string;\n input?: string | InteractionsTurn[] | InteractionsStep[] | InteractionsContentBlock[];\n system_instruction?: string;\n tools?: InteractionsFunctionTool[];\n generation_config?: {\n temperature?: number;\n max_output_tokens?: number;\n [key: string]: unknown;\n };\n stream?: boolean;\n previous_interaction_id?: string;\n [key: string]: unknown;\n}\n\n// Check unsupported numeric input and containers consumed by the selected variant.\n// Other values retain their existing normalization, including omitted input.\nfunction validateInteractionsInput(input: unknown): string | null {\n if (typeof input === \"number\") return \"input must not be a number\";\n if (!Array.isArray(input)) return null;\n const first = input[0];\n if (isJsonObject(first) && \"role\" in first) {\n for (const [index, turn] of input.entries()) {\n if (!isJsonObject(turn)) continue;\n const blocks = turn.content ?? turn.parts;\n // Empty strings, falsy values and empty arrays bypass the converter's\n // filters. Preserve that behavior and the content ?? parts selection.\n if (typeof blocks === \"string\" && blocks.length > 0) {\n const field = turn.content != null ? \"content\" : \"parts\";\n return `input[${index}].${field} must be an array`;\n }\n }\n } else if (\n !(isJsonObject(first) && typeof first.type === \"string\" && STEP_TYPES.has(first.type))\n ) {\n const nullIndex = input.findIndex((part) => part === null);\n if (nullIndex !== -1) return `input[${nullIndex}] must be an object`;\n }\n return null;\n}\n\n// Preserve tools that the converter ignores; reject only these unsafe reads.\nfunction validateInteractionsTools(tools: unknown): string | null {\n if (typeof tools === \"string\" && tools.length > 0) return \"tools must be an array\";\n if (Array.isArray(tools)) {\n const nullIndex = tools.findIndex((tool) => tool === null);\n if (nullIndex !== -1) return `tools[${nullIndex}] must be an object`;\n }\n return null;\n}\n\n// ─── Input conversion: Interactions → ChatCompletionRequest ───────────────\n\nexport function geminiInteractionsToCompletionRequest(\n req: InteractionsRequest,\n): ChatCompletionRequest {\n const messages: ChatMessage[] = [];\n const model = req.model ?? \"gemini-2.5-flash\";\n\n // system_instruction → system message\n if (req.system_instruction) {\n messages.push({ role: \"system\", content: req.system_instruction });\n }\n\n // Parse input\n if (req.input !== undefined) {\n if (typeof req.input === \"string\") {\n // Simple string input → single user message\n messages.push({ role: \"user\", content: req.input });\n } else if (Array.isArray(req.input)) {\n // Could be Turn[], Step[], or Content[]\n const firstItem = req.input[0] as\n | InteractionsTurn\n | InteractionsStep\n | InteractionsContentBlock\n | undefined;\n const isStepArray =\n !!firstItem &&\n !(\"role\" in firstItem) &&\n typeof firstItem.type === \"string\" &&\n STEP_TYPES.has(firstItem.type);\n\n if (firstItem && \"role\" in firstItem) {\n // Turn[] format\n for (const turn of req.input as InteractionsTurn[]) {\n const role = turn.role === \"model\" ? \"assistant\" : turn.role;\n const blocks = turn.content ?? turn.parts;\n if (!blocks || blocks.length === 0) {\n if (role === \"user\" || role === \"assistant\") {\n messages.push({ role: role as \"user\" | \"assistant\", content: \"\" });\n }\n continue;\n }\n\n // Check for function_call or function_result parts\n const funcCallParts = blocks.filter((p) => p.type === \"function_call\");\n const funcResultParts = blocks.filter((p) => p.type === \"function_result\");\n const textParts = blocks.filter((p) => p.type === \"text\");\n\n if (funcCallParts.length > 0) {\n // Assistant tool call message\n const textContent = textParts.map((p) => p.text ?? \"\").join(\"\");\n messages.push({\n role: \"assistant\",\n content: textContent || null,\n tool_calls: funcCallParts.map((p) => ({\n id: p.id ?? p.call_id ?? generateToolCallId(),\n type: \"function\" as const,\n function: {\n name: p.name ?? \"\",\n arguments: JSON.stringify(p.arguments ?? {}),\n },\n })),\n });\n } else if (funcResultParts.length > 0) {\n // Tool response messages\n for (const part of funcResultParts) {\n const resultValue = part.result ?? part.output;\n messages.push({\n role: \"tool\",\n content:\n typeof resultValue === \"string\" ? resultValue : JSON.stringify(resultValue ?? \"\"),\n tool_call_id: part.call_id ?? part.id ?? \"\",\n });\n }\n // Any text parts alongside → separate user message\n if (textParts.length > 0) {\n const text = textParts.map((p) => p.text ?? \"\").join(\"\");\n if (text) {\n messages.push({ role: \"user\", content: text });\n }\n }\n } else {\n // Text-only turn\n const text = textParts.map((p) => p.text ?? \"\").join(\"\");\n if (role === \"user\" || role === \"assistant\" || role === \"system\") {\n messages.push({\n role: role as \"user\" | \"assistant\" | \"system\",\n content: text,\n });\n }\n }\n }\n } else if (isStepArray) {\n // Step[] format — the wire contract Google's /v1beta/interactions accepts.\n for (const step of req.input as InteractionsStep[]) {\n if (step.type === \"user_input\") {\n const text = (step.content ?? [])\n .filter((p) => p.type === \"text\")\n .map((p) => p.text ?? \"\")\n .join(\"\");\n messages.push({ role: \"user\", content: text });\n } else if (step.type === \"model_output\") {\n const blocks = step.content ?? [];\n const funcCallParts = blocks.filter((p) => p.type === \"function_call\");\n const textParts = blocks.filter((p) => p.type === \"text\");\n const textContent = textParts.map((p) => p.text ?? \"\").join(\"\");\n\n if (funcCallParts.length > 0) {\n messages.push({\n role: \"assistant\",\n content: textContent || null,\n tool_calls: funcCallParts.map((p) => ({\n id: p.id ?? p.call_id ?? generateToolCallId(),\n type: \"function\" as const,\n function: {\n name: p.name ?? \"\",\n arguments: JSON.stringify(p.arguments ?? {}),\n },\n })),\n });\n } else {\n messages.push({ role: \"assistant\", content: textContent });\n }\n } else if (STEP_RESULT_TYPES.has(step.type)) {\n const resultValue = step.result ?? step.output;\n messages.push({\n role: \"tool\",\n content:\n typeof resultValue === \"string\" ? resultValue : JSON.stringify(resultValue ?? \"\"),\n tool_call_id: step.call_id ?? step.id ?? \"\",\n });\n }\n }\n } else {\n // Content[] format — single user message with content blocks\n const textParts = (req.input as InteractionsContentBlock[]).filter(\n (p) => p.type === \"text\",\n );\n const text = textParts.map((p) => p.text ?? \"\").join(\"\");\n messages.push({ role: \"user\", content: text || \"\" });\n }\n }\n }\n\n // Convert tools\n let tools: ToolDefinition[] | undefined;\n if (req.tools && req.tools.length > 0) {\n const funcTools = req.tools.filter((t) => t.type === \"function\");\n if (funcTools.length > 0) {\n tools = funcTools.map((t) => ({\n type: \"function\" as const,\n function: {\n name: t.name,\n description: t.description,\n parameters: t.parameters,\n },\n }));\n }\n }\n\n return {\n model,\n messages,\n stream: req.stream !== false, // default true\n temperature: req.generation_config?.temperature,\n max_tokens: req.generation_config?.max_output_tokens,\n tools,\n };\n}\n\n// ─── Interaction ID generation ────────────────────────────────────────────\n\nlet interactionCounter = 0;\n\nexport function resetInteractionCounter(): void {\n interactionCounter = 0;\n}\n\nfunction nextInteractionId(): string {\n return `aimock-int-${interactionCounter++}`;\n}\n\n// ─── Usage helpers ────────────────────────────────────────────────────────\n\nfunction interactionsUsage(overrides?: ResponseOverrides): {\n total_input_tokens: number;\n total_output_tokens: number;\n total_tokens: number;\n} {\n return renderInteractionsUsage(overrides);\n}\n\n// ─── Response building: fixture → Interactions format ─────────────────────\n\nexport function buildInteractionsTextResponse(\n content: string,\n model: string,\n interactionId: string,\n overrides?: ResponseOverrides,\n): object {\n return {\n id: interactionId,\n status: \"completed\",\n model: overrides?.model ?? model,\n role: \"model\",\n output_text: content,\n steps: [{ type: \"model_output\", content: [{ type: \"text\", text: content }] }],\n usage: interactionsUsage(overrides),\n };\n}\n\n// Build a single SDK 2.x function_call step from a fixture tool call,\n// serving malformed arguments as `{}` (or rejecting them under strictToolArguments).\nfunction buildFunctionCallStep(tc: ToolCall, logger: Logger): object {\n const args = servedToolArgs(tc, \"object\", logger);\n return {\n type: \"function_call\",\n id: tc.id || generateToolCallId(),\n name: tc.name,\n arguments: args.value,\n };\n}\n\nexport function buildInteractionsToolCallResponse(\n toolCalls: ToolCall[],\n model: string,\n interactionId: string,\n logger: Logger,\n overrides?: ResponseOverrides,\n): object {\n return {\n id: interactionId,\n status: \"requires_action\",\n model: overrides?.model ?? model,\n role: \"model\",\n steps: toolCalls.map((tc) => buildFunctionCallStep(tc, logger)),\n usage: interactionsUsage(overrides),\n };\n}\n\nexport function buildInteractionsContentWithToolCallsResponse(\n content: string,\n toolCalls: ToolCall[],\n model: string,\n interactionId: string,\n logger: Logger,\n overrides?: ResponseOverrides,\n blocks?: FixtureBlock[],\n): object {\n const steps: object[] = [];\n // Collect output_text in step order so the top-level field mirrors the\n // concatenated text steps regardless of where they appear in `steps`.\n let outputText = \"\";\n\n let hasToolCalls = true;\n if (blocks && blocks.length > 0) {\n // NEW PATH: the non-stream `steps[]` array is index/step-addressed and\n // ordered, so emit one step per block in fixture ARRAY ORDER. A toolCall\n // block placed before a text block therefore yields a function_call step\n // ahead of the model_output step — tool-first, the opposite of the legacy\n // (text-step-always-first) shape below.\n const outcome = resolveFixtureBlockOutcome(blocks);\n const ordered = outcome.ordered;\n hasToolCalls = outcome.hasToolCalls;\n for (const block of ordered) {\n if (block.type === \"text\") {\n steps.push({ type: \"model_output\", content: [{ type: \"text\", text: block.text }] });\n outputText += block.text;\n } else {\n steps.push(\n buildFunctionCallStep(\n { name: block.name, arguments: block.arguments, id: block.id },\n logger,\n ),\n );\n }\n }\n } else {\n // LEGACY PATH: a single text step first, then function_call steps —\n // unchanged from the pre-blocks behavior (text always leads `steps`).\n steps.push({ type: \"model_output\", content: [{ type: \"text\", text: content }] });\n outputText = content;\n for (const tc of toolCalls) {\n steps.push(buildFunctionCallStep(tc, logger));\n }\n }\n\n return {\n id: interactionId,\n status: hasToolCalls ? \"requires_action\" : \"completed\",\n model: overrides?.model ?? model,\n role: \"model\",\n output_text: outputText,\n steps,\n usage: interactionsUsage(overrides),\n };\n}\n\n// ─── SSE event builders ──────────────────────────────────────────────────\n\ninterface InteractionsSSEEvent {\n event_type: string;\n [key: string]: unknown;\n}\n\nlet eventIdCounter = 0;\n\nexport function resetEventIdCounter(): void {\n eventIdCounter = 0;\n}\n\nfunction nextEventId(): string {\n return `evt_${++eventIdCounter}`;\n}\n\nexport function buildInteractionsTextSSEEvents(\n content: string,\n interactionId: string,\n chunkSize: number,\n overrides?: ResponseOverrides,\n): InteractionsSSEEvent[] {\n const events: InteractionsSSEEvent[] = [];\n\n // interaction.created\n events.push({\n event_type: \"interaction.created\",\n interaction: { id: interactionId, status: \"in_progress\" },\n event_id: nextEventId(),\n });\n\n // step.start — text step is the model_output\n events.push({\n event_type: \"step.start\",\n index: 0,\n step: { type: \"model_output\" },\n event_id: nextEventId(),\n });\n\n // step.delta(s) — inner delta shape is unchanged ({ type: \"text\", text })\n if (content.length === 0) {\n events.push({\n event_type: \"step.delta\",\n index: 0,\n delta: { type: \"text\", text: \"\" },\n event_id: nextEventId(),\n });\n } else {\n for (let i = 0; i < content.length; i += chunkSize) {\n const slice = content.slice(i, i + chunkSize);\n events.push({\n event_type: \"step.delta\",\n index: 0,\n delta: { type: \"text\", text: slice },\n event_id: nextEventId(),\n });\n }\n }\n\n // step.stop\n events.push({\n event_type: \"step.stop\",\n index: 0,\n event_id: nextEventId(),\n });\n\n // interaction.completed\n events.push({\n event_type: \"interaction.completed\",\n interaction: {\n id: interactionId,\n status: \"completed\",\n usage: interactionsUsage(overrides),\n },\n event_id: nextEventId(),\n });\n\n return events;\n}\n\nexport function buildInteractionsToolCallSSEEvents(\n toolCalls: ToolCall[],\n interactionId: string,\n logger: Logger,\n overrides?: ResponseOverrides,\n): InteractionsSSEEvent[] {\n const events: InteractionsSSEEvent[] = [];\n\n // interaction.created\n events.push({\n event_type: \"interaction.created\",\n interaction: { id: interactionId, status: \"in_progress\" },\n event_id: nextEventId(),\n });\n\n // Each tool call gets its own step.start/delta/stop bracket. In SDK 2.x the\n // call identity (id, name) lives on step.start; the arguments stream as a\n // dedicated `arguments_delta` carrying a JSON-string fragment, and step.start\n // carries an empty `arguments: {}` placeholder.\n for (let idx = 0; idx < toolCalls.length; idx++) {\n const tc = toolCalls[idx];\n const args = servedToolArgs(tc, \"string\", logger);\n\n events.push({\n event_type: \"step.start\",\n index: idx,\n step: {\n type: \"function_call\",\n id: tc.id || generateToolCallId(),\n name: tc.name,\n arguments: {},\n },\n event_id: nextEventId(),\n });\n\n // arguments_delta.arguments is a string fragment. The real SDK may split\n // the args across several fragments that concatenate into valid JSON; the\n // mock emits the whole serialized object as one fragment (a valid\n // degenerate case the collapser handles identically).\n events.push({\n event_type: \"step.delta\",\n index: idx,\n delta: {\n type: \"arguments_delta\",\n arguments: args.text,\n },\n event_id: nextEventId(),\n });\n\n events.push({\n event_type: \"step.stop\",\n index: idx,\n event_id: nextEventId(),\n });\n }\n\n // interaction.completed\n events.push({\n event_type: \"interaction.completed\",\n interaction: {\n id: interactionId,\n status: \"requires_action\",\n usage: interactionsUsage(overrides),\n },\n event_id: nextEventId(),\n });\n\n return events;\n}\n\n// Emit the step.start/delta(s)/stop bracket for a text (model_output) step at\n// a given step `index`. Inner delta shape ({ type: \"text\", text }) and the\n// empty-content single-empty-delta behavior are unchanged from the legacy path.\nfunction pushTextStepEvents(\n events: InteractionsSSEEvent[],\n index: number,\n content: string,\n chunkSize: number,\n): void {\n events.push({\n event_type: \"step.start\",\n index,\n step: { type: \"model_output\" },\n event_id: nextEventId(),\n });\n\n if (content.length === 0) {\n events.push({\n event_type: \"step.delta\",\n index,\n delta: { type: \"text\", text: \"\" },\n event_id: nextEventId(),\n });\n } else {\n for (let i = 0; i < content.length; i += chunkSize) {\n const slice = content.slice(i, i + chunkSize);\n events.push({\n event_type: \"step.delta\",\n index,\n delta: { type: \"text\", text: slice },\n event_id: nextEventId(),\n });\n }\n }\n\n events.push({\n event_type: \"step.stop\",\n index,\n event_id: nextEventId(),\n });\n}\n\n// Emit the step.start/arguments_delta/stop bracket for a function_call step at\n// a given step `index`. Identity (id, name) lives on step.start with an empty\n// `arguments: {}` placeholder; the arguments stream as a single\n// `arguments_delta` JSON-string fragment — unchanged from the legacy path.\nfunction pushFunctionCallStepEvents(\n events: InteractionsSSEEvent[],\n index: number,\n tc: ToolCall,\n logger: Logger,\n): void {\n const args = servedToolArgs(tc, \"string\", logger);\n\n events.push({\n event_type: \"step.start\",\n index,\n step: {\n type: \"function_call\",\n id: tc.id || generateToolCallId(),\n name: tc.name,\n arguments: {},\n },\n event_id: nextEventId(),\n });\n\n events.push({\n event_type: \"step.delta\",\n index,\n delta: {\n type: \"arguments_delta\",\n arguments: args.text,\n },\n event_id: nextEventId(),\n });\n\n events.push({\n event_type: \"step.stop\",\n index,\n event_id: nextEventId(),\n });\n}\n\nexport function buildInteractionsContentWithToolCallsSSEEvents(\n content: string,\n toolCalls: ToolCall[],\n interactionId: string,\n chunkSize: number,\n logger: Logger,\n overrides?: ResponseOverrides,\n blocks?: FixtureBlock[],\n): InteractionsSSEEvent[] {\n const events: InteractionsSSEEvent[] = [];\n\n // interaction.created\n events.push({\n event_type: \"interaction.created\",\n interaction: { id: interactionId, status: \"in_progress\" },\n event_id: nextEventId(),\n });\n\n let hasToolCalls = true;\n if (blocks && blocks.length > 0) {\n // NEW PATH: stream one step per block in fixture ARRAY ORDER. The step\n // `index` increments with array position, so a toolCall block before a text\n // block yields a function_call step at a LOWER index than the model_output\n // step — tool-first, the opposite of the legacy (text-at-index-0) shape.\n const outcome = resolveFixtureBlockOutcome(blocks);\n const ordered = outcome.ordered;\n hasToolCalls = outcome.hasToolCalls;\n let idx = 0;\n for (const block of ordered) {\n if (block.type === \"text\") {\n pushTextStepEvents(events, idx, block.text, chunkSize);\n } else {\n pushFunctionCallStepEvents(\n events,\n idx,\n {\n name: block.name,\n arguments: block.arguments,\n id: block.id,\n },\n logger,\n );\n }\n idx += 1;\n }\n } else {\n // LEGACY PATH: text content at index 0 (model_output step), tool calls at\n // index 1+ — byte-for-byte unchanged from the pre-blocks behavior.\n pushTextStepEvents(events, 0, content, chunkSize);\n for (let i = 0; i < toolCalls.length; i++) {\n pushFunctionCallStepEvents(events, i + 1, toolCalls[i], logger);\n }\n }\n\n // interaction.completed\n events.push({\n event_type: \"interaction.completed\",\n interaction: {\n id: interactionId,\n status: hasToolCalls ? \"requires_action\" : \"completed\",\n usage: interactionsUsage(overrides),\n },\n event_id: nextEventId(),\n });\n\n return events;\n}\n\n// ─── SSE writer for Interactions streaming ────────────────────────────────\n\ninterface InteractionsStreamOptions {\n latency?: number;\n streamingProfile?: StreamingProfile;\n recordedTimings?: RecordedTimings;\n replaySpeed?: number;\n signal?: AbortSignal;\n onChunkSent?: () => void;\n}\n\nexport async function writeGeminiInteractionsSSEStream(\n res: http.ServerResponse,\n events: InteractionsSSEEvent[],\n optionsOrLatency?: number | InteractionsStreamOptions,\n): Promise<boolean> {\n const opts: InteractionsStreamOptions =\n typeof optionsOrLatency === \"number\" ? { latency: optionsOrLatency } : (optionsOrLatency ?? {});\n const latency = opts.latency ?? 0;\n const profile = opts.streamingProfile;\n const { recordedTimings, replaySpeed } = opts;\n const signal = opts.signal;\n const onChunkSent = opts.onChunkSent;\n\n if (res.writableEnded) return true;\n res.setHeader(\"Content-Type\", \"text/event-stream\");\n res.setHeader(\"Cache-Control\", \"no-cache\");\n res.setHeader(\"Connection\", \"keep-alive\");\n\n let chunkIndex = 0;\n for (const event of events) {\n const chunkDelay = calculateDelay(chunkIndex, profile, latency, recordedTimings, replaySpeed);\n if (chunkDelay > 0) await delay(chunkDelay, signal);\n if (signal?.aborted) return false;\n if (res.writableEnded) return true;\n // Data-only SSE (no event: prefix, no [DONE])\n res.write(`data: ${JSON.stringify(event)}\\n\\n`);\n // Only count step deltas for truncateAfterChunks — framing events\n // (interaction.created, step.start, step.stop, interaction.completed)\n // should not consume chunk budget or trigger the chunk-sent callback.\n if (event.event_type === \"step.delta\") {\n onChunkSent?.();\n chunkIndex++;\n }\n if (signal?.aborted) return false;\n }\n\n if (!res.writableEnded) {\n res.end();\n }\n return true;\n}\n\n// ─── Request handler ──────────────────────────────────────────────────────\n\nfunction prepareInteractionsMisbehavior(\n plan: MisbehaviorPlan,\n streaming: boolean,\n): MisbehaviorPlan {\n if (!streaming && plan.summary.fault === \"stop-length-mid-tool\") {\n // The object wire cannot represent the selected partial argument string.\n // Earlier completed calls/text survive; omit the cut call before allocating\n // IDs and preparing servedToolCalls/usage.\n const response = plan.response;\n const combined = isCombinedFixtureResponse(response);\n const outcome =\n combined && response.blocks?.length ? resolveFixtureBlockOutcome(response.blocks) : undefined;\n const calls =\n outcome?.toolCalls ??\n (combined || isToolCallResponse(response) ? (response.toolCalls ?? []) : []);\n const toolCalls = calls.filter((_, index) => index !== plan.target?.index);\n let callIndex = 0;\n const blocks = outcome?.ordered.filter(\n (block) => block.type === \"text\" || callIndex++ !== plan.target?.index,\n );\n plan = { ...plan, response: { ...response, toolCalls, ...(blocks ? { blocks } : {}) } };\n }\n // Both modes always emit IDs. The shared preparation preserves effective\n // block order and allocates each wire/journal identity exactly once.\n return prepareOpenAIChatMisbehavior(plan);\n}\n\nfunction finalizeInteractionsMisbehavior(\n events: InteractionsSSEEvent[],\n plan: MisbehaviorPlan | MisbehaviorSkip,\n): InteractionsSSEEvent[] {\n if (plan.kind !== \"applied\" || plan.summary.fault !== \"stop-length-mid-tool\") return events;\n // Approved modeled K5 contract: keep the cut arguments and step.stop, then\n // terminate incomplete. Native truncated-tool closure remains unverified.\n return events.map((event) =>\n event.event_type === \"interaction.completed\" && isJsonObject(event.interaction)\n ? { ...event, interaction: { ...event.interaction, status: \"incomplete\" } }\n : event,\n );\n}\n\nexport async function handleGeminiInteractions(\n req: http.IncomingMessage,\n res: http.ServerResponse,\n raw: string,\n fixtures: Fixture[],\n journal: Journal,\n defaults: HandlerDefaults,\n setCorsHeaders: (res: http.ServerResponse) => void,\n): Promise<void> {\n const { logger } = defaults;\n setCorsHeaders(res);\n\n const urlPath = req.url ?? \"/v1beta/interactions\";\n\n let interactionsReq: InteractionsRequest;\n try {\n interactionsReq = JSON.parse(raw) as InteractionsRequest;\n } catch (parseErr) {\n const detail = parseErr instanceof Error ? parseErr.message : \"unknown\";\n journal.add({\n method: req.method ?? \"POST\",\n path: urlPath,\n headers: flattenHeaders(req.headers),\n body: null,\n response: { status: 400, fixture: null },\n });\n writeErrorResponse(\n res,\n 400,\n renderError(\"interactions\", \"validation\", {\n message: `Malformed JSON body: ${detail}`,\n code: \"INVALID_ARGUMENT\",\n }).body,\n );\n return;\n }\n\n // Reject bodies that parsed but are not a JSON object (e.g. `null`) before\n // touching fields — otherwise `interactionsReq.stream` throws a TypeError that\n // surfaces as a 500 instead of a 400.\n if (!isJsonObject(interactionsReq)) {\n journal.add({\n method: req.method ?? \"POST\",\n path: urlPath,\n headers: flattenHeaders(req.headers),\n body: null,\n response: { status: 400, fixture: null },\n });\n writeErrorResponse(\n res,\n 400,\n renderError(\"interactions\", \"validation\", {\n message: \"Request body must be a JSON object\",\n code: \"INVALID_ARGUMENT\",\n }).body,\n );\n return;\n }\n\n const validationError =\n validateInteractionsInput(interactionsReq.input) ??\n validateInteractionsTools(interactionsReq.tools);\n if (validationError) {\n journal.add({\n method: req.method ?? \"POST\",\n path: urlPath,\n headers: flattenHeaders(req.headers),\n body: null,\n response: { status: 400, fixture: null },\n });\n writeErrorResponse(\n res,\n 400,\n renderError(\"interactions\", \"validation\", {\n message: validationError,\n code: \"INVALID_ARGUMENT\",\n }).body,\n );\n return;\n }\n\n // Convert to ChatCompletionRequest for fixture matching\n const completionReq = geminiInteractionsToCompletionRequest(interactionsReq);\n // Keep \"chat\" rather than \"gemini-interactions\" — the router's endpoint\n // compatibility filter (router.ts) treats \"chat\" as a pass-through that\n // matches any unendpointed fixture. Switching to \"gemini-interactions\"\n // would make the request fall into the multimedia guard branch, preventing\n // generic chat fixtures from matching and breaking existing users. The\n // recorder would also start emitting `endpoint: \"gemini-interactions\"` in\n // recorded fixtures, creating a one-way compatibility break.\n completionReq._endpointType = \"chat\";\n completionReq._context = getContext(req);\n\n const streaming = interactionsReq.stream !== false; // default true\n const model = completionReq.model;\n\n const testId = getTestId(req);\n const { fixture, skippedBySequenceOrTurn } = matchFixtureDiagnostic(\n fixtures,\n completionReq,\n journal.getFixtureMatchCountsForTest(testId),\n defaults.requestTransform,\n );\n\n if (fixture) {\n journal.incrementFixtureMatchCount(fixture, fixtures, testId);\n }\n\n if (\n await applyChaosAsync(\n res,\n fixture,\n defaults.chaos,\n req.headers,\n req.url,\n journal,\n {\n method: req.method ?? \"POST\",\n path: urlPath,\n headers: flattenHeaders(req.headers),\n body: completionReq,\n },\n fixture ? \"fixture\" : \"proxy\",\n defaults.registry,\n defaults.logger,\n )\n )\n return;\n\n if (!fixture) {\n const decisions = resolveRequestDecisions(defaults.strict, req.headers);\n if (decisions.fixtureMiss) {\n const strictStatus = 503;\n const strictMessage = strictNoMatchMessage(skippedBySequenceOrTurn);\n logger.error(strictNoMatchLogLine(req.method ?? \"POST\", urlPath, skippedBySequenceOrTurn));\n journal.add({\n method: req.method ?? \"POST\",\n path: urlPath,\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: {\n status: strictStatus,\n fixture: null,\n ...strictOverrideField(defaults.strict, req.headers),\n },\n });\n writeErrorResponse(\n res,\n strictStatus,\n renderError(\"interactions\", \"strict-miss\", {\n message: strictMessage,\n code: \"UNAVAILABLE\",\n }).body,\n );\n return;\n }\n if (decisions.upstream && defaults.record) {\n const outcome = await proxyAndRecord(\n req,\n res,\n completionReq,\n \"gemini-interactions\",\n urlPath,\n fixtures,\n defaults,\n raw,\n );\n if (outcome === \"handled_by_hook\") return;\n if (outcome !== \"not_configured\") {\n journal.add({\n method: req.method ?? \"POST\",\n path: urlPath,\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: {\n status: res.statusCode ?? 200,\n fixture: null,\n source: \"proxy\",\n },\n });\n return;\n }\n }\n journal.add({\n method: req.method ?? \"POST\",\n path: urlPath,\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: {\n status: 404,\n fixture: null,\n ...strictOverrideField(defaults.strict, req.headers),\n },\n });\n writeErrorResponse(\n res,\n 404,\n renderError(\"interactions\", \"fixture-miss\", {\n message: \"No fixture matched\",\n code: \"NOT_FOUND\",\n }).body,\n );\n return;\n }\n\n let response = await resolveResponse(fixture, completionReq);\n let misbehavior = planMisbehavior({\n wire: \"gemini-interactions\",\n fixture,\n response,\n request: completionReq,\n stream: streaming,\n emitsToolCallIds: true,\n defaults,\n rawHeaders: req.headers,\n url: req.url,\n });\n if (misbehavior.kind === \"error\") {\n const entry = journal.add({\n method: req.method ?? \"POST\",\n path: urlPath,\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: { status: misbehavior.status, fixture },\n });\n recordMisbehaviorOutcome({ entry, summary: misbehavior.summary, defaults, testId });\n if (!misbehavior.summary?.evaluations.some((evaluation) => evaluation.outcome === \"error\")) {\n logger.error(`${req.method ?? \"POST\"} ${urlPath}: ${misbehavior.message}`);\n }\n writeErrorResponse(\n res,\n misbehavior.status,\n renderError(\"interactions\", \"misbehavior\", {\n message: misbehavior.message,\n code: misbehavior.code,\n }).body,\n );\n return;\n }\n if (misbehavior.kind === \"applied\") {\n misbehavior = prepareInteractionsMisbehavior(misbehavior, streaming);\n response = misbehavior.response;\n }\n const appliedUsage =\n misbehavior.kind === \"applied\"\n ? resolveOpenAIChatMisbehaviorUsage(misbehavior, completionReq)\n : undefined;\n const latency = fixture.latency ?? defaults.latency;\n const chunkSize = Math.max(1, fixture.chunkSize ?? defaults.chunkSize);\n const replaySpeed = fixture.replaySpeed ?? defaults.replaySpeed;\n\n // Error response\n if (isErrorResponse(response)) {\n const status = response.status ?? 500;\n const journalEntry = journal.add({\n method: req.method ?? \"POST\",\n path: urlPath,\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: { status, fixture },\n });\n recordMisbehaviorOutcome({\n entry: journalEntry,\n summary: misbehavior.summary,\n defaults,\n testId,\n });\n writeErrorResponse(\n res,\n status,\n renderError(\"interactions\", \"fixture\", {\n message: response.error.message,\n code: response.error.type ?? \"ERROR\",\n }).body,\n { retryAfter: response.retryAfter },\n );\n return;\n }\n\n // The content+tool and tool-call branches draw their interaction id after\n // requireEmittedFunctionToolCalls, so a fixture it rejects (a custom tool\n // call or a malformed toolCalls entry) consumes no id. A rejection raised\n // later, inside a builder, comes after the draw and does consume an id:\n // invalid JSON arguments on the non-streaming path, or a malformed block\n // from resolveFixtureBlockOutcome (the streaming path has also consumed\n // event ids by then).\n\n if (misbehavior.kind === \"applied\" && misbehavior.summary.fault === \"reasoning-only\") {\n const journalEntry = journal.add({\n method: req.method ?? \"POST\",\n path: urlPath,\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: { status: 200, fixture },\n });\n recordMisbehaviorOutcome({\n entry: journalEntry,\n summary: misbehavior.summary,\n defaults,\n testId,\n });\n // Reasoning-only output carries no tool call, so it has no tool-call check\n // to pass before drawing its id.\n const interactionId = nextInteractionId();\n const usage = renderInteractionsUsage({ usage: appliedUsage }, \"applied-reasoning-only\");\n if (!streaming) {\n const overrides =\n isTextResponse(response) ||\n isToolCallResponse(response) ||\n isCombinedFixtureResponse(response)\n ? extractOverrides(response)\n : undefined;\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(\n JSON.stringify({\n id: interactionId,\n model: overrides?.model ?? model,\n status: \"incomplete\",\n steps: [\n {\n type: \"thought\",\n signature: \"\",\n summary: [{ type: \"text\", text: misbehavior.reasoning ?? \"\" }],\n },\n ],\n usage,\n }),\n );\n return;\n }\n const events: InteractionsSSEEvent[] = [\n {\n event_type: \"interaction.created\",\n interaction: { id: interactionId, status: \"in_progress\" },\n event_id: nextEventId(),\n },\n { event_type: \"step.start\", index: 0, step: { type: \"thought\" }, event_id: nextEventId() },\n {\n event_type: \"step.delta\",\n index: 0,\n delta: {\n type: \"thought_summary\",\n content: { type: \"text\", text: misbehavior.reasoning ?? \"\" },\n },\n event_id: nextEventId(),\n },\n { event_type: \"step.stop\", index: 0, event_id: nextEventId() },\n {\n event_type: \"interaction.completed\",\n interaction: {\n id: interactionId,\n status: \"incomplete\",\n usage,\n },\n event_id: nextEventId(),\n },\n ];\n const interruption = createInterruptionSignal(fixture);\n const completed = await writeGeminiInteractionsSSEStream(res, events, {\n latency,\n streamingProfile: fixture.streamingProfile,\n recordedTimings: fixture.recordedTimings,\n replaySpeed,\n signal: interruption?.signal,\n onChunkSent: interruption?.tick,\n });\n if (!completed) {\n if (!res.writableEnded) res.destroy();\n journalEntry.response.interrupted = true;\n journalEntry.response.interruptReason = interruption?.reason();\n }\n interruption?.cleanup();\n return;\n }\n\n // Content + tool calls response\n if (isCombinedFixtureResponse(response)) {\n if (response.webSearches?.length) {\n logger.warn(\n \"webSearches in fixture response are not supported for Gemini Interactions API — ignoring\",\n );\n }\n const overrides = {\n ...extractOverrides(response),\n ...(appliedUsage ? { usage: appliedUsage } : {}),\n };\n const journalEntry = journal.add({\n method: req.method ?? \"POST\",\n path: urlPath,\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: { status: 200, fixture },\n });\n recordMisbehaviorOutcome({\n entry: journalEntry,\n summary: misbehavior.summary,\n defaults,\n testId,\n });\n const functionToolCalls = requireEmittedFunctionToolCalls(response, \"Gemini Interactions\");\n const interactionId = nextInteractionId();\n if (!streaming) {\n const body = buildInteractionsContentWithToolCallsResponse(\n response.content ?? \"\",\n functionToolCalls,\n model,\n interactionId,\n logger,\n overrides,\n response.blocks,\n );\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(\n JSON.stringify(\n misbehavior.kind === \"applied\" && misbehavior.summary.fault === \"stop-length-mid-tool\"\n ? { ...body, status: \"incomplete\" }\n : body,\n ),\n );\n } else {\n const events = buildInteractionsContentWithToolCallsSSEEvents(\n response.content ?? \"\",\n functionToolCalls,\n interactionId,\n chunkSize,\n logger,\n overrides,\n response.blocks,\n );\n const interruption = createInterruptionSignal(fixture);\n const completed = await writeGeminiInteractionsSSEStream(\n res,\n finalizeInteractionsMisbehavior(events, misbehavior),\n {\n latency,\n streamingProfile: fixture.streamingProfile,\n recordedTimings: fixture.recordedTimings,\n replaySpeed,\n signal: interruption?.signal,\n onChunkSent: interruption?.tick,\n },\n );\n if (!completed) {\n if (!res.writableEnded) res.destroy();\n journalEntry.response.interrupted = true;\n journalEntry.response.interruptReason = interruption?.reason();\n }\n interruption?.cleanup();\n }\n return;\n }\n\n // Text response\n if (isTextResponse(response)) {\n if (response.webSearches?.length) {\n logger.warn(\n \"webSearches in fixture response are not supported for Gemini Interactions API — ignoring\",\n );\n }\n const overrides = {\n ...extractOverrides(response),\n ...(appliedUsage ? { usage: appliedUsage } : {}),\n };\n const journalEntry = journal.add({\n method: req.method ?? \"POST\",\n path: urlPath,\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: { status: 200, fixture },\n });\n recordMisbehaviorOutcome({\n entry: journalEntry,\n summary: misbehavior.summary,\n defaults,\n testId,\n });\n const interactionId = nextInteractionId();\n if (!streaming) {\n const body = buildInteractionsTextResponse(response.content, model, interactionId, overrides);\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(\n JSON.stringify(\n misbehavior.kind === \"applied\" && misbehavior.summary.fault === \"stop-length-mid-tool\"\n ? { ...body, status: \"incomplete\" }\n : body,\n ),\n );\n } else {\n const events = buildInteractionsTextSSEEvents(\n response.content,\n interactionId,\n chunkSize,\n overrides,\n );\n const interruption = createInterruptionSignal(fixture);\n const completed = await writeGeminiInteractionsSSEStream(\n res,\n finalizeInteractionsMisbehavior(events, misbehavior),\n {\n latency,\n streamingProfile: fixture.streamingProfile,\n recordedTimings: fixture.recordedTimings,\n replaySpeed,\n signal: interruption?.signal,\n onChunkSent: interruption?.tick,\n },\n );\n if (!completed) {\n if (!res.writableEnded) res.destroy();\n journalEntry.response.interrupted = true;\n journalEntry.response.interruptReason = interruption?.reason();\n }\n interruption?.cleanup();\n }\n return;\n }\n\n // Tool call response\n if (isToolCallResponse(response)) {\n if (response.webSearches?.length) {\n logger.warn(\n \"webSearches in fixture response are not supported for Gemini Interactions API — ignoring\",\n );\n }\n const overrides = {\n ...extractOverrides(response),\n ...(appliedUsage ? { usage: appliedUsage } : {}),\n };\n const journalEntry = journal.add({\n method: req.method ?? \"POST\",\n path: urlPath,\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: { status: 200, fixture },\n });\n recordMisbehaviorOutcome({\n entry: journalEntry,\n summary: misbehavior.summary,\n defaults,\n testId,\n });\n const functionToolCalls = requireEmittedFunctionToolCalls(response, \"Gemini Interactions\");\n const interactionId = nextInteractionId();\n if (!streaming) {\n const body = buildInteractionsToolCallResponse(\n functionToolCalls,\n model,\n interactionId,\n logger,\n overrides,\n );\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(\n JSON.stringify(\n misbehavior.kind === \"applied\" && misbehavior.summary.fault === \"stop-length-mid-tool\"\n ? { ...body, status: \"incomplete\" }\n : body,\n ),\n );\n } else {\n const events = buildInteractionsToolCallSSEEvents(\n functionToolCalls,\n interactionId,\n logger,\n overrides,\n );\n const interruption = createInterruptionSignal(fixture);\n const completed = await writeGeminiInteractionsSSEStream(\n res,\n finalizeInteractionsMisbehavior(events, misbehavior),\n {\n latency,\n streamingProfile: fixture.streamingProfile,\n recordedTimings: fixture.recordedTimings,\n replaySpeed,\n signal: interruption?.signal,\n onChunkSent: interruption?.tick,\n },\n );\n if (!completed) {\n if (!res.writableEnded) res.destroy();\n journalEntry.response.interrupted = true;\n journalEntry.response.interruptReason = interruption?.reason();\n }\n interruption?.cleanup();\n }\n return;\n }\n\n // Unknown response type\n const journalEntry = journal.add({\n method: req.method ?? \"POST\",\n path: urlPath,\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: { status: 500, fixture },\n });\n recordMisbehaviorOutcome({ entry: journalEntry, summary: misbehavior.summary, defaults, testId });\n writeErrorResponse(\n res,\n 500,\n renderError(\"interactions\", \"unknown-response\", {\n message: \"Fixture response did not match any known type\",\n code: \"INTERNAL\",\n }).body,\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"gemini-interactions.js","names":["renderInteractionsUsage"],"sources":["../src/gemini-interactions.ts"],"sourcesContent":["/**\n * Google Gemini Interactions API support.\n *\n * Translates incoming Interactions requests into the ChatCompletionRequest\n * format used by the fixture router, and converts fixture responses back\n * into the Gemini Interactions format — either a single JSON response or\n * an SSE stream with event_type-based framing.\n */\nimport { resolveRequestDecisions, strictOverrideField } from \"./response/policy.js\";\nimport { renderError } from \"./response/errors.js\";\nimport { serveRawResponse } from \"./response/raw.js\";\n\nimport type * as http from \"node:http\";\nimport type {\n ChatCompletionRequest,\n ChatMessage,\n Fixture,\n FixtureBlock,\n HandlerDefaults,\n RecordedTimings,\n ResponseOverrides,\n StreamingProfile,\n ToolCall,\n ToolDefinition,\n} from \"./types.js\";\nimport {\n requireEmittedFunctionToolCalls,\n isTextResponse,\n isToolCallResponse,\n isCombinedFixtureResponse,\n isErrorResponse,\n isRawResponse,\n extractOverrides,\n generateToolCallId,\n flattenHeaders,\n isJsonObject,\n getTestId,\n getContext,\n resolveResponse,\n strictNoMatchMessage,\n strictNoMatchLogLine,\n resolveFixtureBlockOutcome,\n servedToolArgs,\n prepareOpenAIChatMisbehavior,\n resolveOpenAIChatMisbehaviorUsage,\n} from \"./helpers.js\";\nimport { matchFixtureDiagnostic } from \"./router.js\";\nimport { writeErrorResponse, sseCodec } from \"./sse-writer.js\";\nimport { httpSink, runFrameLoop, type FrameCodec } from \"./response/frame-loop.js\";\nimport { deliverJson, deliverStream } from \"./response/deliver.js\";\nimport { proxyInterruption, withHangUpDetection } from \"./response/proxy.js\";\nimport { misbehaviorPayloadLength, servedPayloadLength } from \"./response/timing.js\";\nimport type { Journal } from \"./journal.js\";\nimport type { Logger } from \"./logger.js\";\nimport { applyChaosAsync } from \"./chaos.js\";\nimport { proxyAndRecord } from \"./recorder.js\";\nimport { render as renderInteractionsUsage } from \"./response/usage/codecs/interactions.js\";\nimport {\n planMisbehavior,\n recordMisbehaviorOutcome,\n type MisbehaviorPlan,\n type MisbehaviorSkip,\n} from \"./misbehavior.js\";\n\n// ─── Interactions request types ────────────────────────────────────────────\n\ninterface InteractionsContentBlock {\n type: string;\n text?: string;\n name?: string;\n call_id?: string;\n id?: string;\n arguments?: Record<string, unknown>;\n output?: unknown;\n result?: unknown;\n}\n\ninterface InteractionsTurn {\n role: string;\n content?: InteractionsContentBlock[];\n parts?: InteractionsContentBlock[];\n}\n\n/**\n * Top-level Step envelope accepted by the live Gemini Interactions API.\n * The SDK's TypeScript union does not include Step[], but the wire contract\n * does — clients following the live API send these at the top level of `input`.\n * Discriminated by `type`; no `role` field (distinguishes from Turn[]).\n */\ninterface InteractionsStep {\n type: string;\n content?: InteractionsContentBlock[];\n call_id?: string;\n id?: string;\n name?: string;\n result?: unknown;\n output?: unknown;\n is_error?: boolean;\n signature?: string;\n}\n\n/** Step types whose payload is a tool/agent result keyed by call_id. */\nconst STEP_RESULT_TYPES = new Set<string>([\n \"function_result\",\n \"code_execution_result\",\n \"url_context_result\",\n \"google_search_result\",\n \"google_maps_result\",\n \"mcp_server_tool_result\",\n \"file_search_result\",\n]);\n\n/** All recognized top-level Step types (used as the Step[] discriminator). */\nconst STEP_TYPES = new Set<string>([\"user_input\", \"model_output\", ...STEP_RESULT_TYPES]);\n\ninterface InteractionsFunctionTool {\n type: \"function\";\n name: string;\n description?: string;\n parameters?: object;\n}\n\ninterface InteractionsRequest {\n model?: string;\n input?: string | InteractionsTurn[] | InteractionsStep[] | InteractionsContentBlock[];\n system_instruction?: string;\n tools?: InteractionsFunctionTool[];\n generation_config?: {\n temperature?: number;\n max_output_tokens?: number;\n [key: string]: unknown;\n };\n stream?: boolean;\n previous_interaction_id?: string;\n [key: string]: unknown;\n}\n\n// Check unsupported numeric input and containers consumed by the selected variant.\n// Other values retain their existing normalization, including omitted input.\nfunction validateInteractionsInput(input: unknown): string | null {\n if (typeof input === \"number\") return \"input must not be a number\";\n if (!Array.isArray(input)) return null;\n const first = input[0];\n if (isJsonObject(first) && \"role\" in first) {\n for (const [index, turn] of input.entries()) {\n if (!isJsonObject(turn)) continue;\n const blocks = turn.content ?? turn.parts;\n // Empty strings, falsy values and empty arrays bypass the converter's\n // filters. Preserve that behavior and the content ?? parts selection.\n if (typeof blocks === \"string\" && blocks.length > 0) {\n const field = turn.content != null ? \"content\" : \"parts\";\n return `input[${index}].${field} must be an array`;\n }\n }\n } else if (\n !(isJsonObject(first) && typeof first.type === \"string\" && STEP_TYPES.has(first.type))\n ) {\n const nullIndex = input.findIndex((part) => part === null);\n if (nullIndex !== -1) return `input[${nullIndex}] must be an object`;\n }\n return null;\n}\n\n// Preserve tools that the converter ignores; reject only these unsafe reads.\nfunction validateInteractionsTools(tools: unknown): string | null {\n if (typeof tools === \"string\" && tools.length > 0) return \"tools must be an array\";\n if (Array.isArray(tools)) {\n const nullIndex = tools.findIndex((tool) => tool === null);\n if (nullIndex !== -1) return `tools[${nullIndex}] must be an object`;\n }\n return null;\n}\n\n// ─── Input conversion: Interactions → ChatCompletionRequest ───────────────\n\nexport function geminiInteractionsToCompletionRequest(\n req: InteractionsRequest,\n): ChatCompletionRequest {\n const messages: ChatMessage[] = [];\n const model = req.model ?? \"gemini-2.5-flash\";\n\n // system_instruction → system message\n if (req.system_instruction) {\n messages.push({ role: \"system\", content: req.system_instruction });\n }\n\n // Parse input\n if (req.input !== undefined) {\n if (typeof req.input === \"string\") {\n // Simple string input → single user message\n messages.push({ role: \"user\", content: req.input });\n } else if (Array.isArray(req.input)) {\n // Could be Turn[], Step[], or Content[]\n const firstItem = req.input[0] as\n | InteractionsTurn\n | InteractionsStep\n | InteractionsContentBlock\n | undefined;\n const isStepArray =\n !!firstItem &&\n !(\"role\" in firstItem) &&\n typeof firstItem.type === \"string\" &&\n STEP_TYPES.has(firstItem.type);\n\n if (firstItem && \"role\" in firstItem) {\n // Turn[] format\n for (const turn of req.input as InteractionsTurn[]) {\n const role = turn.role === \"model\" ? \"assistant\" : turn.role;\n const blocks = turn.content ?? turn.parts;\n if (!blocks || blocks.length === 0) {\n if (role === \"user\" || role === \"assistant\") {\n messages.push({ role: role as \"user\" | \"assistant\", content: \"\" });\n }\n continue;\n }\n\n // Check for function_call or function_result parts\n const funcCallParts = blocks.filter((p) => p.type === \"function_call\");\n const funcResultParts = blocks.filter((p) => p.type === \"function_result\");\n const textParts = blocks.filter((p) => p.type === \"text\");\n\n if (funcCallParts.length > 0) {\n // Assistant tool call message\n const textContent = textParts.map((p) => p.text ?? \"\").join(\"\");\n messages.push({\n role: \"assistant\",\n content: textContent || null,\n tool_calls: funcCallParts.map((p) => ({\n id: p.id ?? p.call_id ?? generateToolCallId(),\n type: \"function\" as const,\n function: {\n name: p.name ?? \"\",\n arguments: JSON.stringify(p.arguments ?? {}),\n },\n })),\n });\n } else if (funcResultParts.length > 0) {\n // Tool response messages\n for (const part of funcResultParts) {\n const resultValue = part.result ?? part.output;\n messages.push({\n role: \"tool\",\n content:\n typeof resultValue === \"string\" ? resultValue : JSON.stringify(resultValue ?? \"\"),\n tool_call_id: part.call_id ?? part.id ?? \"\",\n });\n }\n // Any text parts alongside → separate user message\n if (textParts.length > 0) {\n const text = textParts.map((p) => p.text ?? \"\").join(\"\");\n if (text) {\n messages.push({ role: \"user\", content: text });\n }\n }\n } else {\n // Text-only turn\n const text = textParts.map((p) => p.text ?? \"\").join(\"\");\n if (role === \"user\" || role === \"assistant\" || role === \"system\") {\n messages.push({\n role: role as \"user\" | \"assistant\" | \"system\",\n content: text,\n });\n }\n }\n }\n } else if (isStepArray) {\n // Step[] format — the wire contract Google's /v1beta/interactions accepts.\n for (const step of req.input as InteractionsStep[]) {\n if (step.type === \"user_input\") {\n const text = (step.content ?? [])\n .filter((p) => p.type === \"text\")\n .map((p) => p.text ?? \"\")\n .join(\"\");\n messages.push({ role: \"user\", content: text });\n } else if (step.type === \"model_output\") {\n const blocks = step.content ?? [];\n const funcCallParts = blocks.filter((p) => p.type === \"function_call\");\n const textParts = blocks.filter((p) => p.type === \"text\");\n const textContent = textParts.map((p) => p.text ?? \"\").join(\"\");\n\n if (funcCallParts.length > 0) {\n messages.push({\n role: \"assistant\",\n content: textContent || null,\n tool_calls: funcCallParts.map((p) => ({\n id: p.id ?? p.call_id ?? generateToolCallId(),\n type: \"function\" as const,\n function: {\n name: p.name ?? \"\",\n arguments: JSON.stringify(p.arguments ?? {}),\n },\n })),\n });\n } else {\n messages.push({ role: \"assistant\", content: textContent });\n }\n } else if (STEP_RESULT_TYPES.has(step.type)) {\n const resultValue = step.result ?? step.output;\n messages.push({\n role: \"tool\",\n content:\n typeof resultValue === \"string\" ? resultValue : JSON.stringify(resultValue ?? \"\"),\n tool_call_id: step.call_id ?? step.id ?? \"\",\n });\n }\n }\n } else {\n // Content[] format — single user message with content blocks\n const textParts = (req.input as InteractionsContentBlock[]).filter(\n (p) => p.type === \"text\",\n );\n const text = textParts.map((p) => p.text ?? \"\").join(\"\");\n messages.push({ role: \"user\", content: text || \"\" });\n }\n }\n }\n\n // Convert tools\n let tools: ToolDefinition[] | undefined;\n if (req.tools && req.tools.length > 0) {\n const funcTools = req.tools.filter((t) => t.type === \"function\");\n if (funcTools.length > 0) {\n tools = funcTools.map((t) => ({\n type: \"function\" as const,\n function: {\n name: t.name,\n description: t.description,\n parameters: t.parameters,\n },\n }));\n }\n }\n\n return {\n model,\n messages,\n stream: req.stream !== false, // default true\n temperature: req.generation_config?.temperature,\n max_tokens: req.generation_config?.max_output_tokens,\n tools,\n };\n}\n\n// ─── Interaction ID generation ────────────────────────────────────────────\n\nlet interactionCounter = 0;\n\nexport function resetInteractionCounter(): void {\n interactionCounter = 0;\n}\n\nfunction nextInteractionId(): string {\n return `aimock-int-${interactionCounter++}`;\n}\n\n// ─── Usage helpers ────────────────────────────────────────────────────────\n\nfunction interactionsUsage(overrides?: ResponseOverrides): {\n total_input_tokens: number;\n total_output_tokens: number;\n total_tokens: number;\n} {\n return renderInteractionsUsage(overrides);\n}\n\n// ─── Response building: fixture → Interactions format ─────────────────────\n\nexport function buildInteractionsTextResponse(\n content: string,\n model: string,\n interactionId: string,\n overrides?: ResponseOverrides,\n): object {\n return {\n id: interactionId,\n status: \"completed\",\n model: overrides?.model ?? model,\n role: \"model\",\n output_text: content,\n steps: [{ type: \"model_output\", content: [{ type: \"text\", text: content }] }],\n usage: interactionsUsage(overrides),\n };\n}\n\n// Build a single SDK 2.x function_call step from a fixture tool call,\n// serving malformed arguments as `{}` (or rejecting them under strictToolArguments).\nfunction buildFunctionCallStep(tc: ToolCall, logger: Logger): object {\n const args = servedToolArgs(tc, \"object\", logger);\n return {\n type: \"function_call\",\n id: tc.id || generateToolCallId(),\n name: tc.name,\n arguments: args.value,\n };\n}\n\nexport function buildInteractionsToolCallResponse(\n toolCalls: ToolCall[],\n model: string,\n interactionId: string,\n logger: Logger,\n overrides?: ResponseOverrides,\n): object {\n return {\n id: interactionId,\n status: \"requires_action\",\n model: overrides?.model ?? model,\n role: \"model\",\n steps: toolCalls.map((tc) => buildFunctionCallStep(tc, logger)),\n usage: interactionsUsage(overrides),\n };\n}\n\nexport function buildInteractionsContentWithToolCallsResponse(\n content: string,\n toolCalls: ToolCall[],\n model: string,\n interactionId: string,\n logger: Logger,\n overrides?: ResponseOverrides,\n blocks?: FixtureBlock[],\n): object {\n const steps: object[] = [];\n // Collect output_text in step order so the top-level field mirrors the\n // concatenated text steps regardless of where they appear in `steps`.\n let outputText = \"\";\n\n let hasToolCalls = true;\n if (blocks && blocks.length > 0) {\n // NEW PATH: the non-stream `steps[]` array is index/step-addressed and\n // ordered, so emit one step per block in fixture ARRAY ORDER. A toolCall\n // block placed before a text block therefore yields a function_call step\n // ahead of the model_output step — tool-first, the opposite of the legacy\n // (text-step-always-first) shape below.\n const outcome = resolveFixtureBlockOutcome(blocks);\n const ordered = outcome.ordered;\n hasToolCalls = outcome.hasToolCalls;\n for (const block of ordered) {\n if (block.type === \"text\") {\n steps.push({ type: \"model_output\", content: [{ type: \"text\", text: block.text }] });\n outputText += block.text;\n } else {\n steps.push(\n buildFunctionCallStep(\n { name: block.name, arguments: block.arguments, id: block.id },\n logger,\n ),\n );\n }\n }\n } else {\n // LEGACY PATH: a single text step first, then function_call steps —\n // unchanged from the pre-blocks behavior (text always leads `steps`).\n steps.push({ type: \"model_output\", content: [{ type: \"text\", text: content }] });\n outputText = content;\n for (const tc of toolCalls) {\n steps.push(buildFunctionCallStep(tc, logger));\n }\n }\n\n return {\n id: interactionId,\n status: hasToolCalls ? \"requires_action\" : \"completed\",\n model: overrides?.model ?? model,\n role: \"model\",\n output_text: outputText,\n steps,\n usage: interactionsUsage(overrides),\n };\n}\n\n// ─── SSE event builders ──────────────────────────────────────────────────\n\ninterface InteractionsSSEEvent {\n event_type: string;\n [key: string]: unknown;\n}\n\nlet eventIdCounter = 0;\n\nexport function resetEventIdCounter(): void {\n eventIdCounter = 0;\n}\n\nfunction nextEventId(): string {\n return `evt_${++eventIdCounter}`;\n}\n\nexport function buildInteractionsTextSSEEvents(\n content: string,\n interactionId: string,\n chunkSize: number,\n overrides?: ResponseOverrides,\n): InteractionsSSEEvent[] {\n const events: InteractionsSSEEvent[] = [];\n\n // interaction.created\n events.push({\n event_type: \"interaction.created\",\n interaction: { id: interactionId, status: \"in_progress\" },\n event_id: nextEventId(),\n });\n\n // step.start — text step is the model_output\n events.push({\n event_type: \"step.start\",\n index: 0,\n step: { type: \"model_output\" },\n event_id: nextEventId(),\n });\n\n // step.delta(s) — inner delta shape is unchanged ({ type: \"text\", text })\n if (content.length === 0) {\n events.push({\n event_type: \"step.delta\",\n index: 0,\n delta: { type: \"text\", text: \"\" },\n event_id: nextEventId(),\n });\n } else {\n for (let i = 0; i < content.length; i += chunkSize) {\n const slice = content.slice(i, i + chunkSize);\n events.push({\n event_type: \"step.delta\",\n index: 0,\n delta: { type: \"text\", text: slice },\n event_id: nextEventId(),\n });\n }\n }\n\n // step.stop\n events.push({\n event_type: \"step.stop\",\n index: 0,\n event_id: nextEventId(),\n });\n\n // interaction.completed\n events.push({\n event_type: \"interaction.completed\",\n interaction: {\n id: interactionId,\n status: \"completed\",\n usage: interactionsUsage(overrides),\n },\n event_id: nextEventId(),\n });\n\n return events;\n}\n\nexport function buildInteractionsToolCallSSEEvents(\n toolCalls: ToolCall[],\n interactionId: string,\n logger: Logger,\n overrides?: ResponseOverrides,\n): InteractionsSSEEvent[] {\n const events: InteractionsSSEEvent[] = [];\n\n // interaction.created\n events.push({\n event_type: \"interaction.created\",\n interaction: { id: interactionId, status: \"in_progress\" },\n event_id: nextEventId(),\n });\n\n // Each tool call gets its own step.start/delta/stop bracket. In SDK 2.x the\n // call identity (id, name) lives on step.start; the arguments stream as a\n // dedicated `arguments_delta` carrying a JSON-string fragment, and step.start\n // carries an empty `arguments: {}` placeholder.\n for (let idx = 0; idx < toolCalls.length; idx++) {\n const tc = toolCalls[idx];\n const args = servedToolArgs(tc, \"string\", logger);\n\n events.push({\n event_type: \"step.start\",\n index: idx,\n step: {\n type: \"function_call\",\n id: tc.id || generateToolCallId(),\n name: tc.name,\n arguments: {},\n },\n event_id: nextEventId(),\n });\n\n // arguments_delta.arguments is a string fragment. The real SDK may split\n // the args across several fragments that concatenate into valid JSON; the\n // mock emits the whole serialized object as one fragment (a valid\n // degenerate case the collapser handles identically).\n events.push({\n event_type: \"step.delta\",\n index: idx,\n delta: {\n type: \"arguments_delta\",\n arguments: args.text,\n },\n event_id: nextEventId(),\n });\n\n events.push({\n event_type: \"step.stop\",\n index: idx,\n event_id: nextEventId(),\n });\n }\n\n // interaction.completed\n events.push({\n event_type: \"interaction.completed\",\n interaction: {\n id: interactionId,\n status: \"requires_action\",\n usage: interactionsUsage(overrides),\n },\n event_id: nextEventId(),\n });\n\n return events;\n}\n\n// Emit the step.start/delta(s)/stop bracket for a text (model_output) step at\n// a given step `index`. Inner delta shape ({ type: \"text\", text }) and the\n// empty-content single-empty-delta behavior are unchanged from the legacy path.\nfunction pushTextStepEvents(\n events: InteractionsSSEEvent[],\n index: number,\n content: string,\n chunkSize: number,\n): void {\n events.push({\n event_type: \"step.start\",\n index,\n step: { type: \"model_output\" },\n event_id: nextEventId(),\n });\n\n if (content.length === 0) {\n events.push({\n event_type: \"step.delta\",\n index,\n delta: { type: \"text\", text: \"\" },\n event_id: nextEventId(),\n });\n } else {\n for (let i = 0; i < content.length; i += chunkSize) {\n const slice = content.slice(i, i + chunkSize);\n events.push({\n event_type: \"step.delta\",\n index,\n delta: { type: \"text\", text: slice },\n event_id: nextEventId(),\n });\n }\n }\n\n events.push({\n event_type: \"step.stop\",\n index,\n event_id: nextEventId(),\n });\n}\n\n// Emit the step.start/arguments_delta/stop bracket for a function_call step at\n// a given step `index`. Identity (id, name) lives on step.start with an empty\n// `arguments: {}` placeholder; the arguments stream as a single\n// `arguments_delta` JSON-string fragment — unchanged from the legacy path.\nfunction pushFunctionCallStepEvents(\n events: InteractionsSSEEvent[],\n index: number,\n tc: ToolCall,\n logger: Logger,\n): void {\n const args = servedToolArgs(tc, \"string\", logger);\n\n events.push({\n event_type: \"step.start\",\n index,\n step: {\n type: \"function_call\",\n id: tc.id || generateToolCallId(),\n name: tc.name,\n arguments: {},\n },\n event_id: nextEventId(),\n });\n\n events.push({\n event_type: \"step.delta\",\n index,\n delta: {\n type: \"arguments_delta\",\n arguments: args.text,\n },\n event_id: nextEventId(),\n });\n\n events.push({\n event_type: \"step.stop\",\n index,\n event_id: nextEventId(),\n });\n}\n\nexport function buildInteractionsContentWithToolCallsSSEEvents(\n content: string,\n toolCalls: ToolCall[],\n interactionId: string,\n chunkSize: number,\n logger: Logger,\n overrides?: ResponseOverrides,\n blocks?: FixtureBlock[],\n): InteractionsSSEEvent[] {\n const events: InteractionsSSEEvent[] = [];\n\n // interaction.created\n events.push({\n event_type: \"interaction.created\",\n interaction: { id: interactionId, status: \"in_progress\" },\n event_id: nextEventId(),\n });\n\n let hasToolCalls = true;\n if (blocks && blocks.length > 0) {\n // NEW PATH: stream one step per block in fixture ARRAY ORDER. The step\n // `index` increments with array position, so a toolCall block before a text\n // block yields a function_call step at a LOWER index than the model_output\n // step — tool-first, the opposite of the legacy (text-at-index-0) shape.\n const outcome = resolveFixtureBlockOutcome(blocks);\n const ordered = outcome.ordered;\n hasToolCalls = outcome.hasToolCalls;\n let idx = 0;\n for (const block of ordered) {\n if (block.type === \"text\") {\n pushTextStepEvents(events, idx, block.text, chunkSize);\n } else {\n pushFunctionCallStepEvents(\n events,\n idx,\n {\n name: block.name,\n arguments: block.arguments,\n id: block.id,\n },\n logger,\n );\n }\n idx += 1;\n }\n } else {\n // LEGACY PATH: text content at index 0 (model_output step), tool calls at\n // index 1+ — byte-for-byte unchanged from the pre-blocks behavior.\n pushTextStepEvents(events, 0, content, chunkSize);\n for (let i = 0; i < toolCalls.length; i++) {\n pushFunctionCallStepEvents(events, i + 1, toolCalls[i], logger);\n }\n }\n\n // interaction.completed\n events.push({\n event_type: \"interaction.completed\",\n interaction: {\n id: interactionId,\n status: hasToolCalls ? \"requires_action\" : \"completed\",\n usage: interactionsUsage(overrides),\n },\n event_id: nextEventId(),\n });\n\n return events;\n}\n\n// ─── SSE writer for Interactions streaming ────────────────────────────────\n\n// Data-only SSE (no event: prefix, no [DONE]). Only step deltas count for the\n// chunk index and truncateAfterChunks: framing events (interaction.created,\n// step.start, step.stop, interaction.completed) pay the delay at the current\n// index but do not consume chunk budget or trigger the chunk-sent callback.\nconst interactionsCodec: FrameCodec<InteractionsSSEEvent> = {\n ...sseCodec<InteractionsSSEEvent>({ done: false }),\n counts: (event) => event.event_type === \"step.delta\",\n};\n\ninterface InteractionsStreamOptions {\n latency?: number;\n streamingProfile?: StreamingProfile;\n recordedTimings?: RecordedTimings;\n replaySpeed?: number;\n signal?: AbortSignal;\n onChunkSent?: () => void;\n}\n\nexport async function writeGeminiInteractionsSSEStream(\n res: http.ServerResponse,\n events: InteractionsSSEEvent[],\n optionsOrLatency?: number | InteractionsStreamOptions,\n): Promise<boolean> {\n const opts: InteractionsStreamOptions =\n typeof optionsOrLatency === \"number\" ? { latency: optionsOrLatency } : (optionsOrLatency ?? {});\n // No lifecycle: a caller gets close-awareness only through its own `signal`.\n // With no events an aborted signal does not stop the writer (it still ends\n // the response and returns true), so the loop gets no signal then.\n return runFrameLoop(\n httpSink(res),\n events,\n interactionsCodec,\n {\n latency: opts.latency,\n streamingProfile: opts.streamingProfile,\n recordedTimings: opts.recordedTimings,\n replaySpeed: opts.replaySpeed,\n },\n { signal: events.length > 0 ? opts.signal : undefined, onChunkSent: opts.onChunkSent },\n );\n}\n\n// ─── Request handler ──────────────────────────────────────────────────────\n\nfunction prepareInteractionsMisbehavior(\n plan: MisbehaviorPlan,\n streaming: boolean,\n): MisbehaviorPlan {\n if (!streaming && plan.summary.fault === \"stop-length-mid-tool\") {\n // The object wire cannot represent the selected partial argument string.\n // Earlier completed calls/text survive; omit the cut call before allocating\n // IDs and preparing servedToolCalls/usage.\n const response = plan.response;\n const combined = isCombinedFixtureResponse(response);\n const outcome =\n combined && response.blocks?.length ? resolveFixtureBlockOutcome(response.blocks) : undefined;\n const calls =\n outcome?.toolCalls ??\n (combined || isToolCallResponse(response) ? (response.toolCalls ?? []) : []);\n const toolCalls = calls.filter((_, index) => index !== plan.target?.index);\n let callIndex = 0;\n const blocks = outcome?.ordered.filter(\n (block) => block.type === \"text\" || callIndex++ !== plan.target?.index,\n );\n plan = { ...plan, response: { ...response, toolCalls, ...(blocks ? { blocks } : {}) } };\n }\n // Both modes always emit IDs. The shared preparation preserves effective\n // block order and allocates each wire/journal identity exactly once.\n return prepareOpenAIChatMisbehavior(plan);\n}\n\nfunction finalizeInteractionsMisbehavior(\n events: InteractionsSSEEvent[],\n plan: MisbehaviorPlan | MisbehaviorSkip,\n): InteractionsSSEEvent[] {\n if (plan.kind !== \"applied\" || plan.summary.fault !== \"stop-length-mid-tool\") return events;\n // Approved modeled K5 contract: keep the cut arguments and step.stop, then\n // terminate incomplete. Native truncated-tool closure remains unverified.\n return events.map((event) =>\n event.event_type === \"interaction.completed\" && isJsonObject(event.interaction)\n ? { ...event, interaction: { ...event.interaction, status: \"incomplete\" } }\n : event,\n );\n}\n\nexport async function handleGeminiInteractions(\n req: http.IncomingMessage,\n res: http.ServerResponse,\n raw: string,\n fixtures: Fixture[],\n journal: Journal,\n defaults: HandlerDefaults,\n setCorsHeaders: (res: http.ServerResponse) => void,\n): Promise<void> {\n const { logger } = defaults;\n setCorsHeaders(res);\n\n const urlPath = req.url ?? \"/v1beta/interactions\";\n\n let interactionsReq: InteractionsRequest;\n try {\n interactionsReq = JSON.parse(raw) as InteractionsRequest;\n } catch (parseErr) {\n const detail = parseErr instanceof Error ? parseErr.message : \"unknown\";\n journal.add({\n method: req.method ?? \"POST\",\n path: urlPath,\n headers: flattenHeaders(req.headers),\n body: null,\n response: { status: 400, fixture: null },\n });\n writeErrorResponse(\n res,\n 400,\n renderError(\"interactions\", \"validation\", {\n message: `Malformed JSON body: ${detail}`,\n code: \"INVALID_ARGUMENT\",\n }).body,\n );\n return;\n }\n\n // Reject bodies that parsed but are not a JSON object (e.g. `null`) before\n // touching fields — otherwise `interactionsReq.stream` throws a TypeError that\n // surfaces as a 500 instead of a 400.\n if (!isJsonObject(interactionsReq)) {\n journal.add({\n method: req.method ?? \"POST\",\n path: urlPath,\n headers: flattenHeaders(req.headers),\n body: null,\n response: { status: 400, fixture: null },\n });\n writeErrorResponse(\n res,\n 400,\n renderError(\"interactions\", \"validation\", {\n message: \"Request body must be a JSON object\",\n code: \"INVALID_ARGUMENT\",\n }).body,\n );\n return;\n }\n\n const validationError =\n validateInteractionsInput(interactionsReq.input) ??\n validateInteractionsTools(interactionsReq.tools);\n if (validationError) {\n journal.add({\n method: req.method ?? \"POST\",\n path: urlPath,\n headers: flattenHeaders(req.headers),\n body: null,\n response: { status: 400, fixture: null },\n });\n writeErrorResponse(\n res,\n 400,\n renderError(\"interactions\", \"validation\", {\n message: validationError,\n code: \"INVALID_ARGUMENT\",\n }).body,\n );\n return;\n }\n\n // Convert to ChatCompletionRequest for fixture matching\n const completionReq = geminiInteractionsToCompletionRequest(interactionsReq);\n // Keep \"chat\" rather than \"gemini-interactions\" — the router's endpoint\n // compatibility filter (router.ts) treats \"chat\" as a pass-through that\n // matches any unendpointed fixture. Switching to \"gemini-interactions\"\n // would make the request fall into the multimedia guard branch, preventing\n // generic chat fixtures from matching and breaking existing users. The\n // recorder would also start emitting `endpoint: \"gemini-interactions\"` in\n // recorded fixtures, creating a one-way compatibility break.\n completionReq._endpointType = \"chat\";\n completionReq._context = getContext(req);\n\n const streaming = interactionsReq.stream !== false; // default true\n const model = completionReq.model;\n\n const testId = getTestId(req);\n const { fixture, skippedBySequenceOrTurn } = matchFixtureDiagnostic(\n fixtures,\n completionReq,\n journal.getFixtureMatchCountsForTest(testId),\n defaults.requestTransform,\n { wire: \"gemini-interactions\" },\n );\n\n if (fixture) {\n journal.incrementFixtureMatchCount(fixture, fixtures, testId);\n }\n\n if (\n await applyChaosAsync(\n res,\n fixture,\n defaults.chaos,\n req.headers,\n req.url,\n journal,\n {\n method: req.method ?? \"POST\",\n path: urlPath,\n headers: flattenHeaders(req.headers),\n body: completionReq,\n },\n fixture ? \"fixture\" : \"proxy\",\n defaults.registry,\n defaults.logger,\n )\n )\n return;\n\n if (!fixture) {\n const decisions = resolveRequestDecisions(defaults.strict, req.headers);\n if (decisions.fixtureMiss) {\n const strictStatus = 503;\n const strictMessage = strictNoMatchMessage(skippedBySequenceOrTurn);\n logger.error(strictNoMatchLogLine(req.method ?? \"POST\", urlPath, skippedBySequenceOrTurn));\n journal.add({\n method: req.method ?? \"POST\",\n path: urlPath,\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: {\n status: strictStatus,\n fixture: null,\n ...strictOverrideField(defaults.strict, req.headers),\n },\n });\n writeErrorResponse(\n res,\n strictStatus,\n renderError(\"interactions\", \"strict-miss\", {\n message: strictMessage,\n code: \"UNAVAILABLE\",\n }).body,\n );\n return;\n }\n if (decisions.upstream && defaults.record) {\n const { outcome, clientHungUp } = await withHangUpDetection(req, res, () =>\n proxyAndRecord(\n req,\n res,\n completionReq,\n \"gemini-interactions\",\n urlPath,\n fixtures,\n defaults,\n raw,\n ),\n );\n if (outcome === \"handled_by_hook\") return;\n if (outcome !== \"not_configured\") {\n journal.add({\n method: req.method ?? \"POST\",\n path: urlPath,\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: {\n status: res.statusCode ?? 200,\n fixture: null,\n source: \"proxy\",\n ...proxyInterruption(res, clientHungUp),\n },\n });\n return;\n }\n }\n journal.add({\n method: req.method ?? \"POST\",\n path: urlPath,\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: {\n status: 404,\n fixture: null,\n ...strictOverrideField(defaults.strict, req.headers),\n },\n });\n writeErrorResponse(\n res,\n 404,\n renderError(\"interactions\", \"fixture-miss\", {\n message: \"No fixture matched\",\n code: \"NOT_FOUND\",\n }).body,\n );\n return;\n }\n\n let response = await resolveResponse(fixture, completionReq);\n let misbehavior = planMisbehavior({\n wire: \"gemini-interactions\",\n fixture,\n response,\n request: completionReq,\n stream: streaming,\n emitsToolCallIds: true,\n defaults,\n rawHeaders: req.headers,\n url: req.url,\n });\n if (misbehavior.kind === \"error\") {\n const entry = journal.add({\n method: req.method ?? \"POST\",\n path: urlPath,\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: { status: misbehavior.status, fixture },\n });\n recordMisbehaviorOutcome({ entry, summary: misbehavior.summary, defaults, testId });\n if (!misbehavior.summary?.evaluations.some((evaluation) => evaluation.outcome === \"error\")) {\n logger.error(`${req.method ?? \"POST\"} ${urlPath}: ${misbehavior.message}`);\n }\n writeErrorResponse(\n res,\n misbehavior.status,\n renderError(\"interactions\", \"misbehavior\", {\n message: misbehavior.message,\n code: misbehavior.code,\n }).body,\n );\n return;\n }\n if (misbehavior.kind === \"applied\") {\n misbehavior = prepareInteractionsMisbehavior(misbehavior, streaming);\n response = misbehavior.response;\n }\n const appliedUsage =\n misbehavior.kind === \"applied\"\n ? resolveOpenAIChatMisbehaviorUsage(misbehavior, completionReq)\n : undefined;\n const latency = fixture.latency ?? defaults.latency;\n const chunkSize = Math.max(1, fixture.chunkSize ?? defaults.chunkSize);\n const replaySpeed = fixture.replaySpeed ?? defaults.replaySpeed;\n const streamTiming = {\n latency,\n streamingProfile: fixture.streamingProfile,\n recordedTimings: fixture.recordedTimings,\n replaySpeed,\n };\n // Non-streaming replies size their (opt-in) wait from the served payload.\n // An applied plan is merged into `response`, so every JSON site below is a\n // misbehavior path when one applied. Interactions serves no fixture\n // reasoning, so there is no effective reasoning to count.\n const applied = misbehavior.kind === \"applied\" ? misbehavior : undefined;\n const jsonOpts = () => ({\n surface: \"other\" as const,\n payloadLength: applied\n ? misbehaviorPayloadLength(applied, undefined)\n : servedPayloadLength(response),\n chunkSize,\n recordedTimings: fixture.recordedTimings,\n duplicateId: !!applied?.duplicateId,\n replaySpeed,\n });\n const markIncomplete = <T extends object>(body: T): T | (T & { status: \"incomplete\" }) =>\n applied?.summary.fault === \"stop-length-mid-tool\" ? { ...body, status: \"incomplete\" } : body;\n\n // Raw passthrough (#549): the authored events or JSON, framed for this wire.\n // `streaming` is the handler's own rule: a stream unless `stream` is false.\n if (isRawResponse(response)) {\n await serveRawResponse({\n wire: \"gemini-interactions\",\n stream: streaming,\n req,\n res,\n fixture,\n response,\n body: completionReq,\n journal,\n defaults,\n testId,\n misbehaviorSummary: misbehavior.summary,\n errorWire: \"interactions\",\n });\n return;\n }\n\n // Error response\n if (isErrorResponse(response)) {\n const status = response.status ?? 500;\n const journalEntry = journal.add({\n method: req.method ?? \"POST\",\n path: urlPath,\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: { status, fixture },\n });\n recordMisbehaviorOutcome({\n entry: journalEntry,\n summary: misbehavior.summary,\n defaults,\n testId,\n });\n writeErrorResponse(\n res,\n status,\n renderError(\"interactions\", \"fixture\", {\n message: response.error.message,\n code: response.error.type ?? \"ERROR\",\n }).body,\n { retryAfter: response.retryAfter },\n );\n return;\n }\n\n // The content+tool and tool-call branches draw their interaction id after\n // requireEmittedFunctionToolCalls, so a fixture it rejects (a custom tool\n // call or a malformed toolCalls entry) consumes no id. A rejection raised\n // later, inside a builder, comes after the draw and does consume an id:\n // invalid JSON arguments on the non-streaming path, or a malformed block\n // from resolveFixtureBlockOutcome (the streaming path has also consumed\n // event ids by then).\n\n if (misbehavior.kind === \"applied\" && misbehavior.summary.fault === \"reasoning-only\") {\n const journalEntry = journal.add({\n method: req.method ?? \"POST\",\n path: urlPath,\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: { status: 200, fixture },\n });\n recordMisbehaviorOutcome({\n entry: journalEntry,\n summary: misbehavior.summary,\n defaults,\n testId,\n });\n // Reasoning-only output carries no tool call, so it has no tool-call check\n // to pass before drawing its id.\n const interactionId = nextInteractionId();\n const usage = renderInteractionsUsage({ usage: appliedUsage }, \"applied-reasoning-only\");\n if (!streaming) {\n const overrides =\n isTextResponse(response) ||\n isToolCallResponse(response) ||\n isCombinedFixtureResponse(response)\n ? extractOverrides(response)\n : undefined;\n const reasoning = misbehavior.reasoning ?? \"\";\n await deliverJson(\n res,\n journalEntry,\n fixture,\n () => ({\n id: interactionId,\n model: overrides?.model ?? model,\n status: \"incomplete\",\n steps: [\n {\n type: \"thought\",\n signature: \"\",\n summary: [{ type: \"text\", text: reasoning }],\n },\n ],\n usage,\n }),\n jsonOpts(),\n );\n return;\n }\n const events: InteractionsSSEEvent[] = [\n {\n event_type: \"interaction.created\",\n interaction: { id: interactionId, status: \"in_progress\" },\n event_id: nextEventId(),\n },\n { event_type: \"step.start\", index: 0, step: { type: \"thought\" }, event_id: nextEventId() },\n {\n event_type: \"step.delta\",\n index: 0,\n delta: {\n type: \"thought_summary\",\n content: { type: \"text\", text: misbehavior.reasoning ?? \"\" },\n },\n event_id: nextEventId(),\n },\n { event_type: \"step.stop\", index: 0, event_id: nextEventId() },\n {\n event_type: \"interaction.completed\",\n interaction: {\n id: interactionId,\n status: \"incomplete\",\n usage,\n },\n event_id: nextEventId(),\n },\n ];\n await deliverStream(res, journalEntry, fixture, interactionsCodec, events, streamTiming);\n return;\n }\n\n // Content + tool calls response\n if (isCombinedFixtureResponse(response)) {\n if (response.webSearches?.length) {\n logger.warn(\n \"webSearches in fixture response are not supported for Gemini Interactions API — ignoring\",\n );\n }\n const overrides = {\n ...extractOverrides(response),\n ...(appliedUsage ? { usage: appliedUsage } : {}),\n };\n const journalEntry = journal.add({\n method: req.method ?? \"POST\",\n path: urlPath,\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: { status: 200, fixture },\n });\n recordMisbehaviorOutcome({\n entry: journalEntry,\n summary: misbehavior.summary,\n defaults,\n testId,\n });\n const functionToolCalls = requireEmittedFunctionToolCalls(response, \"Gemini Interactions\");\n const interactionId = nextInteractionId();\n if (!streaming) {\n const content = response.content ?? \"\";\n const blocks = response.blocks;\n await deliverJson(\n res,\n journalEntry,\n fixture,\n () =>\n markIncomplete(\n buildInteractionsContentWithToolCallsResponse(\n content,\n functionToolCalls,\n model,\n interactionId,\n logger,\n overrides,\n blocks,\n ),\n ),\n jsonOpts(),\n );\n } else {\n const events = buildInteractionsContentWithToolCallsSSEEvents(\n response.content ?? \"\",\n functionToolCalls,\n interactionId,\n chunkSize,\n logger,\n overrides,\n response.blocks,\n );\n await deliverStream(\n res,\n journalEntry,\n fixture,\n interactionsCodec,\n finalizeInteractionsMisbehavior(events, misbehavior),\n streamTiming,\n );\n }\n return;\n }\n\n // Text response\n if (isTextResponse(response)) {\n if (response.webSearches?.length) {\n logger.warn(\n \"webSearches in fixture response are not supported for Gemini Interactions API — ignoring\",\n );\n }\n const overrides = {\n ...extractOverrides(response),\n ...(appliedUsage ? { usage: appliedUsage } : {}),\n };\n const journalEntry = journal.add({\n method: req.method ?? \"POST\",\n path: urlPath,\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: { status: 200, fixture },\n });\n recordMisbehaviorOutcome({\n entry: journalEntry,\n summary: misbehavior.summary,\n defaults,\n testId,\n });\n const interactionId = nextInteractionId();\n if (!streaming) {\n const content = response.content;\n await deliverJson(\n res,\n journalEntry,\n fixture,\n () =>\n markIncomplete(buildInteractionsTextResponse(content, model, interactionId, overrides)),\n jsonOpts(),\n );\n } else {\n const events = buildInteractionsTextSSEEvents(\n response.content,\n interactionId,\n chunkSize,\n overrides,\n );\n await deliverStream(\n res,\n journalEntry,\n fixture,\n interactionsCodec,\n finalizeInteractionsMisbehavior(events, misbehavior),\n streamTiming,\n );\n }\n return;\n }\n\n // Tool call response\n if (isToolCallResponse(response)) {\n if (response.webSearches?.length) {\n logger.warn(\n \"webSearches in fixture response are not supported for Gemini Interactions API — ignoring\",\n );\n }\n const overrides = {\n ...extractOverrides(response),\n ...(appliedUsage ? { usage: appliedUsage } : {}),\n };\n const journalEntry = journal.add({\n method: req.method ?? \"POST\",\n path: urlPath,\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: { status: 200, fixture },\n });\n recordMisbehaviorOutcome({\n entry: journalEntry,\n summary: misbehavior.summary,\n defaults,\n testId,\n });\n const functionToolCalls = requireEmittedFunctionToolCalls(response, \"Gemini Interactions\");\n const interactionId = nextInteractionId();\n if (!streaming) {\n await deliverJson(\n res,\n journalEntry,\n fixture,\n () =>\n markIncomplete(\n buildInteractionsToolCallResponse(\n functionToolCalls,\n model,\n interactionId,\n logger,\n overrides,\n ),\n ),\n jsonOpts(),\n );\n } else {\n const events = buildInteractionsToolCallSSEEvents(\n functionToolCalls,\n interactionId,\n logger,\n overrides,\n );\n await deliverStream(\n res,\n journalEntry,\n fixture,\n interactionsCodec,\n finalizeInteractionsMisbehavior(events, misbehavior),\n streamTiming,\n );\n }\n return;\n }\n\n // Unknown response type\n const journalEntry = journal.add({\n method: req.method ?? \"POST\",\n path: urlPath,\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: { status: 500, fixture },\n });\n recordMisbehaviorOutcome({ entry: journalEntry, summary: misbehavior.summary, defaults, testId });\n writeErrorResponse(\n res,\n 500,\n renderError(\"interactions\", \"unknown-response\", {\n message: \"Fixture response did not match any known type\",\n code: \"INTERNAL\",\n }).body,\n );\n}\n"],"mappings":"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"}