@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
@@ -8,7 +8,10 @@ const require_chaos = require('./chaos.cjs');
8
8
  const require_misbehavior = require('./misbehavior.cjs');
9
9
  const require_router = require('./router.cjs');
10
10
  const require_recorder = require('./recorder.cjs');
11
- const require_interruption = require('./interruption.cjs');
11
+ const require_timing = require('./response/timing.cjs');
12
+ const require_deliver = require('./response/deliver.cjs');
13
+ const require_proxy = require('./response/proxy.cjs');
14
+ const require_raw = require('./response/raw.cjs');
12
15
  const require_responses = require('./response/usage/codecs/responses.cjs');
13
16
  const require_finish = require('./response/finish.cjs');
14
17
  let node_crypto = require("node:crypto");
@@ -325,7 +328,7 @@ function itemId() {
325
328
  }
326
329
  function buildTextStreamEvents(content, model, chunkSize, reasoning, webSearches, overrides, emitEncryptedReasoning = false, synthesizeSummarylessReasoning = false) {
327
330
  const { respId, created, events, prefixOutputItems, nextOutputIndex } = buildResponsePreamble(model, chunkSize, reasoning, webSearches, overrides, emitEncryptedReasoning, synthesizeSummarylessReasoning);
328
- const { events: msgEvents, msgItem } = buildMessageOutputEvents(content, chunkSize, nextOutputIndex, overrides?.phase);
331
+ const { events: msgEvents, msgItem } = buildMessageOutputEvents(content, chunkSize, nextOutputIndex, overrides?.phase, buildAnnotations(content, overrides?.citations));
329
332
  events.push(...msgEvents);
330
333
  events.push({
331
334
  type: "response.completed",
@@ -434,6 +437,7 @@ function buildToolCallStreamEvents(toolCalls, model, chunkSize, reasoning, webSe
434
437
  }
435
438
  function toolCallStreamEvents(toolCalls, model, chunkSize, reasoning, webSearches, overrides, emitEncryptedReasoning = false, synthesizeSummarylessReasoning = false) {
436
439
  require_helpers.assertResponsesToolCalls(toolCalls);
440
+ require_helpers.fixtureCitations("", overrides?.citations);
437
441
  const { respId, created, events, prefixOutputItems, nextOutputIndex } = buildResponsePreamble(model, chunkSize, reasoning, webSearches, overrides, emitEncryptedReasoning, synthesizeSummarylessReasoning);
438
442
  const fcOutputItems = [];
439
443
  for (let idx = 0; idx < toolCalls.length; idx++) {
@@ -670,6 +674,79 @@ function buildWebSearchStreamEvents(queries, startOutputIndex) {
670
674
  }
671
675
  return events;
672
676
  }
677
+ /**
678
+ * A Responses `image_generation_call` output item for one fixture file (see
679
+ * `ResponseOutputItem.ImageGenerationCall` in the openai SDK): `result` holds
680
+ * the base64 bytes once completed and is null while in progress.
681
+ */
682
+ function imageGenerationCallItem(id, file, status) {
683
+ return {
684
+ type: "image_generation_call",
685
+ id,
686
+ status,
687
+ result: status === "completed" ? file.data : null
688
+ };
689
+ }
690
+ /**
691
+ * A fixture file that is not `image/*` reached the Responses API, which
692
+ * carries generated files only as `image_generation_call` items. Surfaces as
693
+ * the same coded 500 (`aimock_unsupported_generated_file`), naming `mimeType`.
694
+ */
695
+ var NonImageGeneratedFileError = class extends require_helpers.UnsupportedGeneratedFileError {
696
+ constructor(mimeType) {
697
+ super("the OpenAI Responses API");
698
+ this.message = `aimock: fixture "files" on the OpenAI Responses API carry only image/* files (as image_generation_call items); got mimeType "${mimeType}"`;
699
+ }
700
+ };
701
+ /**
702
+ * Throw {@link NonImageGeneratedFileError} for the first fixture file whose
703
+ * `mimeType` is not `image/*`. Called before any output item is built, so
704
+ * nothing reaches the client.
705
+ */
706
+ function requireImageFiles(files) {
707
+ const nonImage = files.find((f) => !f.mimeType.toLowerCase().startsWith("image/"));
708
+ if (nonImage) throw new NonImageGeneratedFileError(nonImage.mimeType);
709
+ }
710
+ /** Fixture `files` as completed `image_generation_call` items (non-streaming). */
711
+ function imageGenerationCallItems(files) {
712
+ const validated = require_helpers.fixtureFiles(files);
713
+ requireImageFiles(validated);
714
+ return validated.map((f) => imageGenerationCallItem(require_helpers.generateId("ig"), f, "completed"));
715
+ }
716
+ /**
717
+ * Streamed form of {@link imageGenerationCallItems}: per file, the item's
718
+ * `output_item.added`, the `image_generation_call` `in_progress`,
719
+ * `generating` and `completed` events, then `output_item.done` carrying the
720
+ * completed item.
721
+ */
722
+ function buildImageGenerationStreamEvents(files, startOutputIndex) {
723
+ requireImageFiles(files);
724
+ const events = [];
725
+ files.forEach((file, i) => {
726
+ const id = require_helpers.generateId("ig");
727
+ const outputIndex = startOutputIndex + i;
728
+ events.push({
729
+ type: "response.output_item.added",
730
+ output_index: outputIndex,
731
+ item: imageGenerationCallItem(id, file, "in_progress")
732
+ });
733
+ for (const phase of [
734
+ "in_progress",
735
+ "generating",
736
+ "completed"
737
+ ]) events.push({
738
+ type: `response.image_generation_call.${phase}`,
739
+ output_index: outputIndex,
740
+ item_id: id
741
+ });
742
+ events.push({
743
+ type: "response.output_item.done",
744
+ output_index: outputIndex,
745
+ item: imageGenerationCallItem(id, file, "completed")
746
+ });
747
+ });
748
+ return events;
749
+ }
673
750
  function buildResponsePreamble(model, chunkSize, reasoning, webSearches, overrides, emitEncryptedReasoning = false, synthesizeSummarylessReasoning = false, forceReasoning = false) {
674
751
  const respId = overrides?.id ?? responseId();
675
752
  const created = overrides?.created ?? Math.floor(Date.now() / 1e3);
@@ -713,6 +790,13 @@ function buildResponsePreamble(model, chunkSize, reasoning, webSearches, overrid
713
790
  for (const de of doneEvents) prefixOutputItems.push(de.item);
714
791
  nextOutputIndex += webSearches.length;
715
792
  }
793
+ const files = require_helpers.fixtureFiles(overrides?.files);
794
+ if (files.length > 0) {
795
+ const fileEvents = buildImageGenerationStreamEvents(files, nextOutputIndex);
796
+ events.push(...fileEvents);
797
+ for (const e of fileEvents) if (e.type === "response.output_item.done") prefixOutputItems.push(e.item);
798
+ nextOutputIndex += files.length;
799
+ }
716
800
  return {
717
801
  respId,
718
802
  created,
@@ -721,7 +805,84 @@ function buildResponsePreamble(model, chunkSize, reasoning, webSearches, overrid
721
805
  nextOutputIndex
722
806
  };
723
807
  }
724
- function buildMessageOutputEvents(content, chunkSize, outputIndex, phase) {
808
+ /**
809
+ * Fixture `citations` as `output_text` annotations for each text in `texts`
810
+ * (one per message item), in fixture order: a URL citation becomes
811
+ * `url_citation`, a file citation (`type: "file"`) becomes `file_citation`
812
+ * (`file_id`, `filename`, `index`; see `ResponseOutputText.FileCitation` in
813
+ * the openai SDK). Offsets index the joined text of all texts, as on every
814
+ * other provider. A URL citation is a range and goes only to the text holding
815
+ * its `startIndex`. A file citation is a point after the cited text, so it
816
+ * goes to the last text starting before its `index`: an `index` equal to a
817
+ * text's end stays at the end of that text (not at 0 of the next text), and
818
+ * an `index` of 0 goes to the first text. Each citation is rebased onto its
819
+ * text since Responses offsets are per `output_text`, with `end_index`
820
+ * clamped to it. Anthropic and Gemini need no point rule: they have no file
821
+ * citation. Anthropic URL citations follow the same start-owns-range rule;
822
+ * Gemini does not, and splits a citation that spans several text parts into
823
+ * one grounding support per part (see geminiGroundingSupports).
824
+ * Missing offsets take the defaults `resolveCitations` applies: a URL
825
+ * citation starts at 0 and ends at the end of the joined text, so one without
826
+ * offsets goes on the first text and covers all of it; a file citation's
827
+ * `index` defaults to the end of the joined text (the last text).
828
+ */
829
+ function blockAnnotations(texts, citations) {
830
+ const perText = texts.map(() => []);
831
+ if (texts.length === 0) {
832
+ require_helpers.fixtureCitations("", citations);
833
+ return perText;
834
+ }
835
+ const starts = [];
836
+ let offset = 0;
837
+ for (const text of texts) {
838
+ starts.push(offset);
839
+ offset += text.length;
840
+ }
841
+ const joinedLength = offset;
842
+ const ownerOf = (start) => {
843
+ let owner = 0;
844
+ for (let k = 1; k < starts.length; k++) if (starts[k] <= start) owner = k;
845
+ return owner;
846
+ };
847
+ const pointOwnerOf = (index) => {
848
+ let owner = 0;
849
+ for (let k = 1; k < starts.length; k++) if (starts[k] < index) owner = k;
850
+ return owner;
851
+ };
852
+ for (const c of require_helpers.fixtureCitations(texts.join(""), citations)) {
853
+ if (c.type === "file") {
854
+ const index = c.index ?? joinedLength;
855
+ const owner = pointOwnerOf(index);
856
+ perText[owner].push({
857
+ type: "file_citation",
858
+ file_id: c.fileId,
859
+ filename: c.filename,
860
+ index: index - starts[owner]
861
+ });
862
+ continue;
863
+ }
864
+ const start = c.startIndex ?? 0;
865
+ const end = c.endIndex ?? joinedLength;
866
+ const owner = ownerOf(start);
867
+ perText[owner].push({
868
+ type: "url_citation",
869
+ url: c.url,
870
+ title: c.title ?? c.url,
871
+ start_index: start - starts[owner],
872
+ end_index: Math.min(end - starts[owner], texts[owner].length)
873
+ });
874
+ }
875
+ return perText;
876
+ }
877
+ /** {@link blockAnnotations} for a turn with a single text. */
878
+ function buildAnnotations(content, citations) {
879
+ return blockAnnotations([content], citations)[0];
880
+ }
881
+ /** The annotations of each text block in `blocks`, in block order. */
882
+ function orderedBlockAnnotations(blocks, citations) {
883
+ return blockAnnotations(blocks.flatMap((b) => b.type === "text" ? [b.text] : []), citations);
884
+ }
885
+ function buildMessageOutputEvents(content, chunkSize, outputIndex, phase, annotations) {
725
886
  const msgId = itemId();
726
887
  const events = [];
727
888
  events.push({
@@ -761,6 +922,16 @@ function buildMessageOutputEvents(content, chunkSize, outputIndex, phase) {
761
922
  content_index: 0,
762
923
  text: content
763
924
  });
925
+ annotations.forEach((annotation, annotationIndex) => {
926
+ events.push({
927
+ type: "response.output_text.annotation.added",
928
+ item_id: msgId,
929
+ output_index: outputIndex,
930
+ content_index: 0,
931
+ annotation_index: annotationIndex,
932
+ annotation
933
+ });
934
+ });
764
935
  events.push({
765
936
  type: "response.content_part.done",
766
937
  item_id: msgId,
@@ -769,7 +940,7 @@ function buildMessageOutputEvents(content, chunkSize, outputIndex, phase) {
769
940
  part: {
770
941
  type: "output_text",
771
942
  text: content,
772
- annotations: []
943
+ annotations
773
944
  }
774
945
  });
775
946
  const msgItem = {
@@ -781,7 +952,7 @@ function buildMessageOutputEvents(content, chunkSize, outputIndex, phase) {
781
952
  content: [{
782
953
  type: "output_text",
783
954
  text: content,
784
- annotations: []
955
+ annotations
785
956
  }]
786
957
  };
787
958
  events.push({
@@ -836,24 +1007,6 @@ function buildFunctionCallOutputEvents(toolCall, chunkSize, outputIndex) {
836
1007
  };
837
1008
  }
838
1009
  /**
839
- * `text` cut into slices of at most `chunkSize` UTF-16 units, never between
840
- * the two halves of a surrogate pair: a cut that would land inside a pair
841
- * moves back before it, or, when that would leave the slice empty
842
- * (`chunkSize` 1), forward past it. The slices rejoin to `text` exactly.
843
- */
844
- function surrogateSafeChunks(text, chunkSize) {
845
- const chunks = [];
846
- for (let i = 0; i < text.length;) {
847
- let end = Math.min(i + chunkSize, text.length);
848
- const high = text.charCodeAt(end - 1);
849
- const low = text.charCodeAt(end);
850
- if (high >= 55296 && high <= 56319 && low >= 56320 && low <= 57343) end = end - 1 > i ? end - 1 : end + 1;
851
- chunks.push(text.slice(i, end));
852
- i = end;
853
- }
854
- return chunks;
855
- }
856
- /**
857
1010
  * Emit output_item.added → custom_tool_call_input.delta×N →
858
1011
  * custom_tool_call_input.done → output_item.done for one `custom_tool_call` at
859
1012
  * `outputIndex`, returning the completed item. An empty `input`
@@ -869,7 +1022,7 @@ function buildCustomToolCallOutputEvents(call, chunkSize, outputIndex) {
869
1022
  output_index: outputIndex,
870
1023
  item: customToolCallItem(call, ctcId, callId, "", "in_progress")
871
1024
  });
872
- for (const delta of surrogateSafeChunks(input, chunkSize)) events.push({
1025
+ for (const delta of require_helpers.surrogateSafeChunks(input, chunkSize)) events.push({
873
1026
  type: "response.custom_tool_call_input.delta",
874
1027
  item_id: ctcId,
875
1028
  output_index: outputIndex,
@@ -896,7 +1049,7 @@ function buildCustomToolCallOutputEvents(call, chunkSize, outputIndex) {
896
1049
  function buildToolCallOutputEvents(call, chunkSize, outputIndex) {
897
1050
  return isCustomEmittable(call) ? buildCustomToolCallOutputEvents(call, chunkSize, outputIndex) : buildFunctionCallOutputEvents(call, chunkSize, outputIndex);
898
1051
  }
899
- function buildOutputPrefix(content, model, reasoning, webSearches, emitEncryptedReasoning = false, synthesizeSummarylessReasoning = false, phase) {
1052
+ function buildOutputPrefix(content, model, reasoning, webSearches, emitEncryptedReasoning = false, synthesizeSummarylessReasoning = false, phase, citations, files) {
900
1053
  const output = [];
901
1054
  if (reasoning || shouldSynthesizeBlobOnlyReasoning(reasoning, model, synthesizeSummarylessReasoning)) output.push(buildReasoningOutputItem(require_helpers.generateId("rs"), {
902
1055
  summaryText: reasoning,
@@ -911,6 +1064,7 @@ function buildOutputPrefix(content, model, reasoning, webSearches, emitEncrypted
911
1064
  query
912
1065
  }
913
1066
  });
1067
+ output.push(...imageGenerationCallItems(files));
914
1068
  output.push({
915
1069
  type: "message",
916
1070
  id: itemId(),
@@ -920,7 +1074,7 @@ function buildOutputPrefix(content, model, reasoning, webSearches, emitEncrypted
920
1074
  content: [{
921
1075
  type: "output_text",
922
1076
  text: content,
923
- annotations: []
1077
+ annotations: buildAnnotations(content, citations)
924
1078
  }]
925
1079
  });
926
1080
  return output;
@@ -937,10 +1091,11 @@ function buildResponseEnvelope(model, output, overrides) {
937
1091
  };
938
1092
  }
939
1093
  function buildTextResponse(content, model, reasoning, webSearches, overrides, emitEncryptedReasoning = false, synthesizeSummarylessReasoning = false) {
940
- return buildResponseEnvelope(model, buildOutputPrefix(content, model, reasoning, webSearches, emitEncryptedReasoning, synthesizeSummarylessReasoning, overrides?.phase), overrides);
1094
+ return buildResponseEnvelope(model, buildOutputPrefix(content, model, reasoning, webSearches, emitEncryptedReasoning, synthesizeSummarylessReasoning, overrides?.phase, overrides?.citations, overrides?.files), overrides);
941
1095
  }
942
1096
  function buildToolCallResponse(toolCalls, model, reasoning, webSearches, overrides, emitEncryptedReasoning = false, synthesizeSummarylessReasoning = false) {
943
1097
  require_helpers.assertResponsesToolCalls(toolCalls);
1098
+ require_helpers.fixtureCitations("", overrides?.citations);
944
1099
  const output = [];
945
1100
  if (reasoning || shouldSynthesizeBlobOnlyReasoning(reasoning, model, synthesizeSummarylessReasoning)) output.push(buildReasoningOutputItem(require_helpers.generateId("rs"), {
946
1101
  summaryText: reasoning,
@@ -955,6 +1110,7 @@ function buildToolCallResponse(toolCalls, model, reasoning, webSearches, overrid
955
1110
  query
956
1111
  }
957
1112
  });
1113
+ output.push(...imageGenerationCallItems(overrides?.files));
958
1114
  for (const tc of toolCalls) output.push(buildToolCallOutputItem(tc));
959
1115
  return buildResponseEnvelope(model, output, overrides);
960
1116
  }
@@ -972,9 +1128,11 @@ function contentWithToolCallsStreamEvents(content, toolCalls, model, chunkSize,
972
1128
  const orderedOutputItems = [];
973
1129
  if (ordered && ordered.length > 0) {
974
1130
  let outputIndex = nextOutputIndex;
1131
+ const textAnnotations = orderedBlockAnnotations(ordered, overrides?.citations);
1132
+ let textPos = 0;
975
1133
  for (const block of ordered) {
976
1134
  if (block.type === "text") {
977
- const { events: msgEvents, msgItem } = buildMessageOutputEvents(block.text, chunkSize, outputIndex, overrides?.phase);
1135
+ const { events: msgEvents, msgItem } = buildMessageOutputEvents(block.text, chunkSize, outputIndex, overrides?.phase, textAnnotations[textPos++]);
978
1136
  events.push(...msgEvents);
979
1137
  orderedOutputItems.push(msgItem);
980
1138
  } else {
@@ -986,7 +1144,7 @@ function contentWithToolCallsStreamEvents(content, toolCalls, model, chunkSize,
986
1144
  }
987
1145
  } else {
988
1146
  require_helpers.assertResponsesToolCalls(toolCalls);
989
- const { events: msgEvents, msgItem } = buildMessageOutputEvents(content, chunkSize, nextOutputIndex, overrides?.phase);
1147
+ const { events: msgEvents, msgItem } = buildMessageOutputEvents(content, chunkSize, nextOutputIndex, overrides?.phase, buildAnnotations(content, overrides?.citations));
990
1148
  events.push(...msgEvents);
991
1149
  orderedOutputItems.push(msgItem);
992
1150
  for (let idx = 0; idx < toolCalls.length; idx++) {
@@ -1014,7 +1172,7 @@ function buildToolCallOutputItem(tc) {
1014
1172
  if (isCustomEmittable(tc)) return customToolCallItem(tc, customToolCallItemId(), tc.id || require_helpers.generateToolCallId(), requireFixtureToolInput(tc.input), "completed");
1015
1173
  return functionCallItem(tc, require_helpers.generateId("fc"), tc.id || require_helpers.generateToolCallId(), requireFixtureToolArguments(tc.arguments), "completed");
1016
1174
  }
1017
- function buildMessageOutputItem(content, phase) {
1175
+ function buildMessageOutputItem(content, phase, annotations) {
1018
1176
  return {
1019
1177
  type: "message",
1020
1178
  id: itemId(),
@@ -1024,7 +1182,7 @@ function buildMessageOutputItem(content, phase) {
1024
1182
  content: [{
1025
1183
  type: "output_text",
1026
1184
  text: content,
1027
- annotations: []
1185
+ annotations
1028
1186
  }]
1029
1187
  };
1030
1188
  }
@@ -1044,40 +1202,18 @@ function buildContentWithToolCallsResponse(content, toolCalls, model, reasoning,
1044
1202
  query
1045
1203
  }
1046
1204
  });
1047
- for (const block of ordered) if (block.type === "text") output.push(buildMessageOutputItem(block.text, overrides?.phase));
1205
+ output.push(...imageGenerationCallItems(overrides?.files));
1206
+ const textAnnotations = orderedBlockAnnotations(ordered, overrides?.citations);
1207
+ let textPos = 0;
1208
+ for (const block of ordered) if (block.type === "text") output.push(buildMessageOutputItem(block.text, overrides?.phase, textAnnotations[textPos++]));
1048
1209
  else output.push(buildToolCallOutputItem(block));
1049
1210
  return buildResponseEnvelope(model, output, overrides);
1050
1211
  }
1051
1212
  require_helpers.assertResponsesToolCalls(toolCalls);
1052
- const output = buildOutputPrefix(content, model, reasoning, webSearches, emitEncryptedReasoning, synthesizeSummarylessReasoning, overrides?.phase);
1213
+ const output = buildOutputPrefix(content, model, reasoning, webSearches, emitEncryptedReasoning, synthesizeSummarylessReasoning, overrides?.phase, overrides?.citations, overrides?.files);
1053
1214
  for (const tc of toolCalls) output.push(buildToolCallOutputItem(tc));
1054
1215
  return buildResponseEnvelope(model, output, overrides);
1055
1216
  }
1056
- async function writeResponsesSSEStream(res, events, optionsOrLatency) {
1057
- const opts = typeof optionsOrLatency === "number" ? { latency: optionsOrLatency } : optionsOrLatency ?? {};
1058
- const latency = opts.latency ?? 0;
1059
- const profile = opts.streamingProfile;
1060
- const { recordedTimings, replaySpeed } = opts;
1061
- const signal = opts.signal;
1062
- const onChunkSent = opts.onChunkSent;
1063
- if (res.writableEnded) return true;
1064
- res.setHeader("Content-Type", "text/event-stream");
1065
- res.setHeader("Cache-Control", "no-cache");
1066
- res.setHeader("Connection", "keep-alive");
1067
- let chunkIndex = 0;
1068
- for (const event of events) {
1069
- const chunkDelay = require_sse_writer.calculateDelay(chunkIndex, profile, latency, recordedTimings, replaySpeed);
1070
- if (chunkDelay > 0) await require_sse_writer.delay(chunkDelay, signal);
1071
- if (signal?.aborted) return false;
1072
- if (res.writableEnded) return true;
1073
- res.write(`event: ${event.type}\ndata: ${JSON.stringify(event)}\n\n`);
1074
- onChunkSent?.();
1075
- if (signal?.aborted) return false;
1076
- chunkIndex++;
1077
- }
1078
- if (!res.writableEnded) res.end();
1079
- return true;
1080
- }
1081
1217
  async function handleResponses(req, res, raw, fixtures, journal, defaults, setCorsHeaders) {
1082
1218
  setCorsHeaders(res);
1083
1219
  let responsesReq;
@@ -1159,7 +1295,10 @@ async function handleResponses(req, res, raw, fixtures, journal, defaults, setCo
1159
1295
  const emitEncryptedReasoning = requestWantsEncryptedReasoning(responsesReq);
1160
1296
  const synthesizeSummarylessReasoning = requestIncludesEncryptedReasoning(responsesReq);
1161
1297
  const testId = require_helpers.getTestId(req);
1162
- const { fixture, skippedBySequenceOrTurn } = require_router.matchFixtureDiagnostic(fixtures, completionReq, journal.getFixtureMatchCountsForTest(testId), defaults.requestTransform, require_router.recordMatchOptions(!!defaults.record, defaults.logger));
1298
+ const { fixture, skippedBySequenceOrTurn } = require_router.matchFixtureDiagnostic(fixtures, completionReq, journal.getFixtureMatchCountsForTest(testId), defaults.requestTransform, {
1299
+ ...require_router.recordMatchOptions(!!defaults.record, defaults.logger),
1300
+ wire: "openai-responses"
1301
+ });
1163
1302
  if (fixture) {
1164
1303
  defaults.logger.debug(`Responses fixture matched for ${req.method ?? "POST"} ${req.url ?? "/v1/responses"}`);
1165
1304
  journal.incrementFixtureMatchCount(fixture, fixtures, testId);
@@ -1194,7 +1333,7 @@ async function handleResponses(req, res, raw, fixtures, journal, defaults, setCo
1194
1333
  return;
1195
1334
  }
1196
1335
  if (defaults.record) {
1197
- const outcome = await require_recorder.proxyAndRecord(req, res, completionReq, "openai", req.url ?? "/v1/responses", fixtures, defaults, raw);
1336
+ const { outcome, clientHungUp } = await require_proxy.withHangUpDetection(req, res, () => require_recorder.proxyAndRecord(req, res, completionReq, "openai", req.url ?? "/v1/responses", fixtures, defaults, raw));
1198
1337
  if (outcome === "handled_by_hook") return;
1199
1338
  if (outcome !== "not_configured") {
1200
1339
  journal.add({
@@ -1205,7 +1344,8 @@ async function handleResponses(req, res, raw, fixtures, journal, defaults, setCo
1205
1344
  response: {
1206
1345
  status: res.statusCode ?? 200,
1207
1346
  fixture: null,
1208
- source: "proxy"
1347
+ source: "proxy",
1348
+ ...require_proxy.proxyInterruption(res, clientHungUp)
1209
1349
  }
1210
1350
  });
1211
1351
  return;
@@ -1270,9 +1410,23 @@ async function handleResponses(req, res, raw, fixtures, journal, defaults, setCo
1270
1410
  const chunkSize = Math.max(1, fixture.chunkSize ?? defaults.chunkSize);
1271
1411
  const fixtureTimings = fixture.recordedTimings;
1272
1412
  const effectiveReplaySpeed = fixture.replaySpeed ?? defaults.replaySpeed;
1413
+ const streamTiming = {
1414
+ latency,
1415
+ streamingProfile: fixture.streamingProfile,
1416
+ recordedTimings: fixtureTimings,
1417
+ replaySpeed: effectiveReplaySpeed
1418
+ };
1419
+ const timedJson = require_timing.nonStreamingTimingEnabled(fixture, "other");
1420
+ const jsonPayloadLength = (r, reasoning) => require_timing.responsesPayloadLength(r, reasoning) + require_timing.responsesOutputFrames(r) * chunkSize;
1421
+ const jsonOpts = (payloadLength, duplicateId = false) => ({
1422
+ surface: "other",
1423
+ payloadLength: timedJson ? payloadLength() : 0,
1424
+ chunkSize,
1425
+ recordedTimings: fixtureTimings,
1426
+ duplicateId,
1427
+ replaySpeed: effectiveReplaySpeed
1428
+ });
1273
1429
  if (misbehavior.kind === "applied") {
1274
- const prepared = prepareResponsesMisbehavior(misbehavior);
1275
- const output = buildResponsesMisbehavior(prepared, completionReq, chunkSize, emitEncryptedReasoning);
1276
1430
  const entry = journal.add({
1277
1431
  method: req.method ?? "POST",
1278
1432
  path: req.url ?? "/v1/responses",
@@ -1283,41 +1437,50 @@ async function handleResponses(req, res, raw, fixtures, journal, defaults, setCo
1283
1437
  fixture
1284
1438
  }
1285
1439
  });
1286
- require_misbehavior.recordMisbehaviorOutcome({
1440
+ const prepared = prepareResponsesMisbehavior(misbehavior);
1441
+ const build = () => buildResponsesMisbehavior(prepared, completionReq, chunkSize, emitEncryptedReasoning);
1442
+ const record = () => require_misbehavior.recordMisbehaviorOutcome({
1287
1443
  entry,
1288
1444
  summary: prepared.summary,
1289
1445
  defaults,
1290
1446
  testId
1291
1447
  });
1448
+ const output = timedJson && responsesReq.stream !== true ? void 0 : build();
1449
+ if (output) record();
1292
1450
  if (responsesReq.stream !== true) {
1293
- res.writeHead(200, { "Content-Type": "application/json" });
1294
- res.end(JSON.stringify(output.response));
1451
+ const servedReasoning = prepared.reasoning ?? ("reasoning" in prepared.response ? prepared.response.reasoning : void 0);
1452
+ await require_deliver.deliverJson(res, entry, fixture, () => {
1453
+ if (output) return output.response;
1454
+ const built = build();
1455
+ record();
1456
+ return built.response;
1457
+ }, jsonOpts(() => require_timing.misbehaviorPayloadLength(prepared, servedReasoning, jsonPayloadLength), !!prepared.duplicateId));
1295
1458
  } else {
1296
- const appliedTimings = prepared.duplicateId && fixtureTimings ? {
1297
- ...fixtureTimings,
1298
- interChunkDelaysMs: [...fixtureTimings.interChunkDelaysMs]
1299
- } : fixtureTimings;
1300
- if (prepared.duplicateId && appliedTimings) {
1301
- const lastGap = appliedTimings.interChunkDelaysMs.at(-1) ?? 0;
1302
- while (appliedTimings.interChunkDelaysMs.length < output.events.length - 1) appliedTimings.interChunkDelaysMs.push(lastGap);
1303
- }
1304
- const interruption = require_interruption.createInterruptionSignal(fixture);
1305
- if (!await writeResponsesSSEStream(res, output.events, {
1306
- latency,
1307
- streamingProfile: fixture.streamingProfile,
1308
- recordedTimings: appliedTimings,
1309
- replaySpeed: effectiveReplaySpeed,
1310
- signal: interruption?.signal,
1311
- onChunkSent: interruption?.tick
1312
- })) {
1313
- if (!res.writableEnded) res.destroy();
1314
- entry.response.interrupted = true;
1315
- entry.response.interruptReason = interruption?.reason();
1316
- }
1317
- interruption?.cleanup();
1459
+ const { events } = output ?? build();
1460
+ await require_deliver.deliverStream(res, entry, fixture, require_sse_writer.typedSseCodec(), events, {
1461
+ ...streamTiming,
1462
+ recordedTimings: require_timing.extendRecordedTimings(fixtureTimings, events.length, !!prepared.duplicateId)
1463
+ });
1318
1464
  }
1319
1465
  return;
1320
1466
  }
1467
+ if (require_helpers.isRawResponse(response)) {
1468
+ await require_raw.serveRawResponse({
1469
+ wire: "openai-responses",
1470
+ stream: responsesReq.stream === true,
1471
+ req,
1472
+ res,
1473
+ fixture,
1474
+ response,
1475
+ body: completionReq,
1476
+ journal,
1477
+ defaults,
1478
+ testId,
1479
+ misbehaviorSummary: misbehavior.summary,
1480
+ errorWire: "openai"
1481
+ });
1482
+ return;
1483
+ }
1321
1484
  if (require_helpers.isErrorResponse(response)) {
1322
1485
  const status = response.status ?? 500;
1323
1486
  require_misbehavior.recordMisbehaviorOutcome({
@@ -1361,26 +1524,10 @@ async function handleResponses(req, res, raw, fixtures, journal, defaults, setCo
1361
1524
  const ordered = require_helpers.resolveServedBlockOutcome(response)?.ordered;
1362
1525
  if (!ordered) require_helpers.assertCustomToolCalls(response);
1363
1526
  const toolCalls = require_helpers.servedToolCalls(response);
1364
- if (responsesReq.stream !== true) {
1365
- const body = buildContentWithToolCallsResponse(response.content ?? "", toolCalls, completionReq.model, effReasoning, response.webSearches, overrides, ordered, emitEncryptedReasoning, synthesizeSummarylessReasoning);
1366
- res.writeHead(200, { "Content-Type": "application/json" });
1367
- res.end(JSON.stringify(body));
1368
- } else {
1527
+ if (responsesReq.stream !== true) await require_deliver.deliverJson(res, journalEntry, fixture, () => buildContentWithToolCallsResponse(response.content ?? "", toolCalls, completionReq.model, effReasoning, response.webSearches, overrides, ordered, emitEncryptedReasoning, synthesizeSummarylessReasoning), jsonOpts(() => jsonPayloadLength(response, effReasoning)));
1528
+ else {
1369
1529
  const events = contentWithToolCallsStreamEvents(response.content ?? "", toolCalls, completionReq.model, chunkSize, effReasoning, response.webSearches, overrides, ordered, emitEncryptedReasoning, synthesizeSummarylessReasoning);
1370
- const interruption = require_interruption.createInterruptionSignal(fixture);
1371
- if (!await writeResponsesSSEStream(res, events, {
1372
- latency,
1373
- streamingProfile: fixture.streamingProfile,
1374
- recordedTimings: fixtureTimings,
1375
- replaySpeed: effectiveReplaySpeed,
1376
- signal: interruption?.signal,
1377
- onChunkSent: interruption?.tick
1378
- })) {
1379
- if (!res.writableEnded) res.destroy();
1380
- journalEntry.response.interrupted = true;
1381
- journalEntry.response.interruptReason = interruption?.reason();
1382
- }
1383
- interruption?.cleanup();
1530
+ await require_deliver.deliverStream(res, journalEntry, fixture, require_sse_writer.typedSseCodec(), events, streamTiming);
1384
1531
  }
1385
1532
  return;
1386
1533
  }
@@ -1404,26 +1551,10 @@ async function handleResponses(req, res, raw, fixtures, journal, defaults, setCo
1404
1551
  defaults,
1405
1552
  testId
1406
1553
  });
1407
- if (responsesReq.stream !== true) {
1408
- const body = buildTextResponse(response.content, completionReq.model, effReasoning, response.webSearches, overrides, emitEncryptedReasoning, synthesizeSummarylessReasoning);
1409
- res.writeHead(200, { "Content-Type": "application/json" });
1410
- res.end(JSON.stringify(body));
1411
- } else {
1554
+ if (responsesReq.stream !== true) await require_deliver.deliverJson(res, journalEntry, fixture, () => buildTextResponse(response.content, completionReq.model, effReasoning, response.webSearches, overrides, emitEncryptedReasoning, synthesizeSummarylessReasoning), jsonOpts(() => jsonPayloadLength(response, effReasoning)));
1555
+ else {
1412
1556
  const events = buildTextStreamEvents(response.content, completionReq.model, chunkSize, effReasoning, response.webSearches, overrides, emitEncryptedReasoning, synthesizeSummarylessReasoning);
1413
- const interruption = require_interruption.createInterruptionSignal(fixture);
1414
- if (!await writeResponsesSSEStream(res, events, {
1415
- latency,
1416
- streamingProfile: fixture.streamingProfile,
1417
- recordedTimings: fixtureTimings,
1418
- replaySpeed: effectiveReplaySpeed,
1419
- signal: interruption?.signal,
1420
- onChunkSent: interruption?.tick
1421
- })) {
1422
- if (!res.writableEnded) res.destroy();
1423
- journalEntry.response.interrupted = true;
1424
- journalEntry.response.interruptReason = interruption?.reason();
1425
- }
1426
- interruption?.cleanup();
1557
+ await require_deliver.deliverStream(res, journalEntry, fixture, require_sse_writer.typedSseCodec(), events, streamTiming);
1427
1558
  }
1428
1559
  return;
1429
1560
  }
@@ -1448,26 +1579,10 @@ async function handleResponses(req, res, raw, fixtures, journal, defaults, setCo
1448
1579
  testId
1449
1580
  });
1450
1581
  require_helpers.assertCustomToolCalls(response);
1451
- if (responsesReq.stream !== true) {
1452
- const body = buildToolCallResponse(require_helpers.servedToolCalls(response), completionReq.model, effReasoning, response.webSearches, overrides, emitEncryptedReasoning, synthesizeSummarylessReasoning);
1453
- res.writeHead(200, { "Content-Type": "application/json" });
1454
- res.end(JSON.stringify(body));
1455
- } else {
1582
+ if (responsesReq.stream !== true) await require_deliver.deliverJson(res, journalEntry, fixture, () => buildToolCallResponse(require_helpers.servedToolCalls(response), completionReq.model, effReasoning, response.webSearches, overrides, emitEncryptedReasoning, synthesizeSummarylessReasoning), jsonOpts(() => jsonPayloadLength(response, effReasoning)));
1583
+ else {
1456
1584
  const events = toolCallStreamEvents(require_helpers.servedToolCalls(response), completionReq.model, chunkSize, effReasoning, response.webSearches, overrides, emitEncryptedReasoning, synthesizeSummarylessReasoning);
1457
- const interruption = require_interruption.createInterruptionSignal(fixture);
1458
- if (!await writeResponsesSSEStream(res, events, {
1459
- latency,
1460
- streamingProfile: fixture.streamingProfile,
1461
- recordedTimings: fixtureTimings,
1462
- replaySpeed: effectiveReplaySpeed,
1463
- signal: interruption?.signal,
1464
- onChunkSent: interruption?.tick
1465
- })) {
1466
- if (!res.writableEnded) res.destroy();
1467
- journalEntry.response.interrupted = true;
1468
- journalEntry.response.interruptReason = interruption?.reason();
1469
- }
1470
- interruption?.cleanup();
1585
+ await require_deliver.deliverStream(res, journalEntry, fixture, require_sse_writer.typedSseCodec(), events, streamTiming);
1471
1586
  }
1472
1587
  return;
1473
1588
  }
@@ -1502,7 +1617,11 @@ function buildResponsesMisbehavior(plan, request, chunkSize, emitEncryptedReason
1502
1617
  const overrides = require_helpers.extractOverrides(response);
1503
1618
  const reasoning = plan.reasoning ?? ("reasoning" in response ? response.reasoning : void 0);
1504
1619
  const webSearches = "webSearches" in response ? response.webSearches : void 0;
1505
- const { respId, created, events, prefixOutputItems, nextOutputIndex } = buildResponsePreamble(request.model, chunkSize, reasoning, webSearches, overrides, emitEncryptedReasoning, false, plan.reasoning !== void 0);
1620
+ const outputWithheld = plan.refusal !== void 0 || plan.summary.fault === "empty-response" || plan.stop === "content_filter" || plan.reasoning !== void 0;
1621
+ const { respId, created, events, prefixOutputItems, nextOutputIndex } = buildResponsePreamble(request.model, chunkSize, reasoning, webSearches, outputWithheld ? {
1622
+ ...overrides,
1623
+ files: void 0
1624
+ } : overrides, emitEncryptedReasoning, false, plan.reasoning !== void 0);
1506
1625
  const output = [...prefixOutputItems];
1507
1626
  let outputIndex = nextOutputIndex;
1508
1627
  const incomplete = plan.stop === "length" || plan.stop === "content_filter";
@@ -1560,16 +1679,18 @@ function buildResponsesMisbehavior(plan, request, chunkSize, emitEncryptedReason
1560
1679
  item
1561
1680
  });
1562
1681
  output.push(item);
1563
- } else if (plan.summary.fault !== "empty-response" && plan.stop !== "content_filter" && plan.reasoning === void 0) {
1682
+ } else if (!outputWithheld) {
1564
1683
  const combined = require_helpers.isCombinedFixtureResponse(response);
1565
1684
  const blocks = (combined ? require_helpers.resolveServedBlockOutcome(response) : void 0)?.ordered ?? [..."content" in response && typeof response.content === "string" ? [{
1566
1685
  type: "text",
1567
1686
  text: response.content
1568
1687
  }] : [], ...combined || require_helpers.isToolCallResponse(response) ? require_helpers.servedToolCalls(response).map(require_helpers.toolCallFixtureBlock) : []];
1569
1688
  let callIndex = 0;
1689
+ const textAnnotations = orderedBlockAnnotations(blocks, overrides.citations);
1690
+ let textPos = 0;
1570
1691
  for (const block of blocks) {
1571
1692
  if (block.type === "text") {
1572
- const message = buildMessageOutputEvents(block.text, chunkSize, outputIndex, overrides.phase);
1693
+ const message = buildMessageOutputEvents(block.text, chunkSize, outputIndex, overrides.phase, textAnnotations[textPos++]);
1573
1694
  events.push(...message.events);
1574
1695
  output.push(message.msgItem);
1575
1696
  } else {