openai 7.22.0 → 7.24.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 (235) hide show
  1. package/CHANGELOG.md +57 -0
  2. package/README.md +4 -1
  3. package/client.d.mts +1 -1
  4. package/client.d.mts.map +1 -1
  5. package/client.d.ts +1 -1
  6. package/client.d.ts.map +1 -1
  7. package/client.js +43 -5
  8. package/client.js.map +1 -1
  9. package/client.mjs +43 -5
  10. package/client.mjs.map +1 -1
  11. package/internal/responses/canonical-output-text.d.mts +8 -4
  12. package/internal/responses/canonical-output-text.d.mts.map +1 -1
  13. package/internal/responses/canonical-output-text.d.ts +8 -4
  14. package/internal/responses/canonical-output-text.d.ts.map +1 -1
  15. package/internal/responses/canonical-output-text.js +11 -0
  16. package/internal/responses/canonical-output-text.js.map +1 -1
  17. package/internal/responses/canonical-output-text.mjs +11 -0
  18. package/internal/responses/canonical-output-text.mjs.map +1 -1
  19. package/internal/responses/response-accumulator.d.mts +12 -1
  20. package/internal/responses/response-accumulator.d.mts.map +1 -1
  21. package/internal/responses/response-accumulator.d.ts +12 -1
  22. package/internal/responses/response-accumulator.d.ts.map +1 -1
  23. package/internal/responses/response-accumulator.js +99 -25
  24. package/internal/responses/response-accumulator.js.map +1 -1
  25. package/internal/responses/response-accumulator.mjs +97 -26
  26. package/internal/responses/response-accumulator.mjs.map +1 -1
  27. package/internal/ws.d.mts +6 -2
  28. package/internal/ws.d.mts.map +1 -1
  29. package/internal/ws.d.ts +6 -2
  30. package/internal/ws.d.ts.map +1 -1
  31. package/internal/ws.js +5 -3
  32. package/internal/ws.js.map +1 -1
  33. package/internal/ws.mjs +5 -3
  34. package/internal/ws.mjs.map +1 -1
  35. package/lib/live/transcript-grouping.d.mts.map +1 -1
  36. package/lib/live/transcript-grouping.d.ts.map +1 -1
  37. package/lib/live/transcript-grouping.js +6 -5
  38. package/lib/live/transcript-grouping.js.map +1 -1
  39. package/lib/live/transcript-grouping.mjs +6 -5
  40. package/lib/live/transcript-grouping.mjs.map +1 -1
  41. package/lib/responses/responses-websocket-accumulator.d.mts +72 -0
  42. package/lib/responses/responses-websocket-accumulator.d.mts.map +1 -0
  43. package/lib/responses/responses-websocket-accumulator.d.ts +72 -0
  44. package/lib/responses/responses-websocket-accumulator.d.ts.map +1 -0
  45. package/lib/responses/responses-websocket-accumulator.js +291 -0
  46. package/lib/responses/responses-websocket-accumulator.js.map +1 -0
  47. package/lib/responses/responses-websocket-accumulator.mjs +287 -0
  48. package/lib/responses/responses-websocket-accumulator.mjs.map +1 -0
  49. package/package.json +1 -1
  50. package/realtime/translations/ws.d.mts +84 -0
  51. package/realtime/translations/ws.d.mts.map +1 -0
  52. package/realtime/translations/ws.d.ts +84 -0
  53. package/realtime/translations/ws.d.ts.map +1 -0
  54. package/realtime/translations/ws.js +444 -0
  55. package/realtime/translations/ws.js.map +1 -0
  56. package/realtime/translations/ws.mjs +439 -0
  57. package/realtime/translations/ws.mjs.map +1 -0
  58. package/resources/admin/organization/audit-logs.d.mts +1 -1
  59. package/resources/admin/organization/audit-logs.d.mts.map +1 -1
  60. package/resources/admin/organization/audit-logs.d.ts +1 -1
  61. package/resources/admin/organization/audit-logs.d.ts.map +1 -1
  62. package/resources/admin/organization/external-storage.d.mts +19 -3
  63. package/resources/admin/organization/external-storage.d.mts.map +1 -1
  64. package/resources/admin/organization/external-storage.d.ts +19 -3
  65. package/resources/admin/organization/external-storage.d.ts.map +1 -1
  66. package/resources/admin/organization/index.d.mts +1 -1
  67. package/resources/admin/organization/index.d.mts.map +1 -1
  68. package/resources/admin/organization/index.d.ts +1 -1
  69. package/resources/admin/organization/index.d.ts.map +1 -1
  70. package/resources/admin/organization/index.js.map +1 -1
  71. package/resources/admin/organization/index.mjs.map +1 -1
  72. package/resources/admin/organization/organization.d.mts +2 -2
  73. package/resources/admin/organization/organization.d.mts.map +1 -1
  74. package/resources/admin/organization/organization.d.ts +2 -2
  75. package/resources/admin/organization/organization.d.ts.map +1 -1
  76. package/resources/admin/organization/organization.js.map +1 -1
  77. package/resources/admin/organization/organization.mjs.map +1 -1
  78. package/resources/audio/speech.d.mts +1 -1
  79. package/resources/audio/speech.d.mts.map +1 -1
  80. package/resources/audio/speech.d.ts +1 -1
  81. package/resources/audio/speech.d.ts.map +1 -1
  82. package/resources/audio/transcriptions.d.mts +2 -1
  83. package/resources/audio/transcriptions.d.mts.map +1 -1
  84. package/resources/audio/transcriptions.d.ts +2 -1
  85. package/resources/audio/transcriptions.d.ts.map +1 -1
  86. package/resources/audio/transcriptions.js.map +1 -1
  87. package/resources/audio/transcriptions.mjs.map +1 -1
  88. package/resources/beta/agents/agents.d.mts +14 -8
  89. package/resources/beta/agents/agents.d.mts.map +1 -1
  90. package/resources/beta/agents/agents.d.ts +14 -8
  91. package/resources/beta/agents/agents.d.ts.map +1 -1
  92. package/resources/beta/agents/agents.js.map +1 -1
  93. package/resources/beta/agents/agents.mjs.map +1 -1
  94. package/resources/beta/agents/environments/templates.d.mts +6 -6
  95. package/resources/beta/agents/environments/templates.d.ts +6 -6
  96. package/resources/beta/agents/sessions/sessions.d.mts +4 -2
  97. package/resources/beta/agents/sessions/sessions.d.mts.map +1 -1
  98. package/resources/beta/agents/sessions/sessions.d.ts +4 -2
  99. package/resources/beta/agents/sessions/sessions.d.ts.map +1 -1
  100. package/resources/beta/agents/sessions/sessions.js.map +1 -1
  101. package/resources/beta/agents/sessions/sessions.mjs.map +1 -1
  102. package/resources/beta/agents/vaults/credentials.d.mts +24 -7
  103. package/resources/beta/agents/vaults/credentials.d.mts.map +1 -1
  104. package/resources/beta/agents/vaults/credentials.d.ts +24 -7
  105. package/resources/beta/agents/vaults/credentials.d.ts.map +1 -1
  106. package/resources/beta/agents/vaults/credentials.js +2 -6
  107. package/resources/beta/agents/vaults/credentials.js.map +1 -1
  108. package/resources/beta/agents/vaults/credentials.mjs +2 -6
  109. package/resources/beta/agents/vaults/credentials.mjs.map +1 -1
  110. package/resources/beta/responses/responses.d.mts +69 -10
  111. package/resources/beta/responses/responses.d.mts.map +1 -1
  112. package/resources/beta/responses/responses.d.ts +69 -10
  113. package/resources/beta/responses/responses.d.ts.map +1 -1
  114. package/resources/beta/responses/responses.js.map +1 -1
  115. package/resources/beta/responses/responses.mjs.map +1 -1
  116. package/resources/files.d.mts +2 -2
  117. package/resources/files.d.mts.map +1 -1
  118. package/resources/files.d.ts +2 -2
  119. package/resources/files.d.ts.map +1 -1
  120. package/resources/index.d.mts +1 -1
  121. package/resources/index.d.mts.map +1 -1
  122. package/resources/index.d.ts +1 -1
  123. package/resources/index.d.ts.map +1 -1
  124. package/resources/live/forks/ws-base.js +1 -1
  125. package/resources/live/forks/ws-base.js.map +1 -1
  126. package/resources/live/forks/ws-base.mjs +1 -1
  127. package/resources/live/forks/ws-base.mjs.map +1 -1
  128. package/resources/live/sideband/ws-base.js +1 -1
  129. package/resources/live/sideband/ws-base.js.map +1 -1
  130. package/resources/live/sideband/ws-base.mjs +1 -1
  131. package/resources/live/sideband/ws-base.mjs.map +1 -1
  132. package/resources/live/ws-base.js +1 -1
  133. package/resources/live/ws-base.js.map +1 -1
  134. package/resources/live/ws-base.mjs +1 -1
  135. package/resources/live/ws-base.mjs.map +1 -1
  136. package/resources/realtime/index.d.mts +1 -0
  137. package/resources/realtime/index.d.mts.map +1 -1
  138. package/resources/realtime/index.d.ts +1 -0
  139. package/resources/realtime/index.d.ts.map +1 -1
  140. package/resources/realtime/index.js +3 -1
  141. package/resources/realtime/index.js.map +1 -1
  142. package/resources/realtime/index.mjs +1 -0
  143. package/resources/realtime/index.mjs.map +1 -1
  144. package/resources/realtime/realtime.d.mts +4 -0
  145. package/resources/realtime/realtime.d.mts.map +1 -1
  146. package/resources/realtime/realtime.d.ts +4 -0
  147. package/resources/realtime/realtime.d.ts.map +1 -1
  148. package/resources/realtime/realtime.js +4 -0
  149. package/resources/realtime/realtime.js.map +1 -1
  150. package/resources/realtime/realtime.mjs +4 -0
  151. package/resources/realtime/realtime.mjs.map +1 -1
  152. package/resources/realtime/translations/client-secrets.d.mts +67 -0
  153. package/resources/realtime/translations/client-secrets.d.mts.map +1 -0
  154. package/resources/realtime/translations/client-secrets.d.ts +67 -0
  155. package/resources/realtime/translations/client-secrets.d.ts.map +1 -0
  156. package/resources/realtime/translations/client-secrets.js +39 -0
  157. package/resources/realtime/translations/client-secrets.js.map +1 -0
  158. package/resources/realtime/translations/client-secrets.mjs +35 -0
  159. package/resources/realtime/translations/client-secrets.mjs.map +1 -0
  160. package/resources/realtime/translations/index.d.mts +3 -0
  161. package/resources/realtime/translations/index.d.mts.map +1 -0
  162. package/resources/realtime/translations/index.d.ts +3 -0
  163. package/resources/realtime/translations/index.d.ts.map +1 -0
  164. package/resources/realtime/translations/index.js +9 -0
  165. package/resources/realtime/translations/index.js.map +1 -0
  166. package/resources/realtime/translations/index.mjs +4 -0
  167. package/resources/realtime/translations/index.mjs.map +1 -0
  168. package/resources/realtime/translations/translations.d.mts +10 -0
  169. package/resources/realtime/translations/translations.d.mts.map +1 -0
  170. package/resources/realtime/translations/translations.d.ts +10 -0
  171. package/resources/realtime/translations/translations.d.ts.map +1 -0
  172. package/resources/realtime/translations/translations.js +17 -0
  173. package/resources/realtime/translations/translations.js.map +1 -0
  174. package/resources/realtime/translations/translations.mjs +12 -0
  175. package/resources/realtime/translations/translations.mjs.map +1 -0
  176. package/resources/realtime/translations.d.mts +2 -0
  177. package/resources/realtime/translations.d.mts.map +1 -0
  178. package/resources/realtime/translations.d.ts +2 -0
  179. package/resources/realtime/translations.d.ts.map +1 -0
  180. package/resources/realtime/translations.js +6 -0
  181. package/resources/realtime/translations.js.map +1 -0
  182. package/resources/realtime/translations.mjs +3 -0
  183. package/resources/realtime/translations.mjs.map +1 -0
  184. package/resources/responses/responses.d.mts +66 -7
  185. package/resources/responses/responses.d.mts.map +1 -1
  186. package/resources/responses/responses.d.ts +66 -7
  187. package/resources/responses/responses.d.ts.map +1 -1
  188. package/resources/responses/responses.js.map +1 -1
  189. package/resources/responses/responses.mjs.map +1 -1
  190. package/resources/shared.d.mts +3 -3
  191. package/resources/shared.d.mts.map +1 -1
  192. package/resources/shared.d.ts +3 -3
  193. package/resources/shared.d.ts.map +1 -1
  194. package/resources/videos.d.mts +7 -0
  195. package/resources/videos.d.mts.map +1 -1
  196. package/resources/videos.d.ts +7 -0
  197. package/resources/videos.d.ts.map +1 -1
  198. package/src/client.ts +42 -6
  199. package/src/internal/responses/canonical-output-text.ts +30 -5
  200. package/src/internal/responses/response-accumulator.ts +155 -56
  201. package/src/internal/ws.ts +5 -3
  202. package/src/lib/live/transcript-grouping.ts +6 -5
  203. package/src/lib/responses/responses-websocket-accumulator.ts +374 -0
  204. package/src/realtime/translations/ws.ts +539 -0
  205. package/src/resources/admin/organization/audit-logs.ts +2 -1
  206. package/src/resources/admin/organization/external-storage.ts +34 -2
  207. package/src/resources/admin/organization/index.ts +1 -0
  208. package/src/resources/admin/organization/organization.ts +2 -0
  209. package/src/resources/audio/speech.ts +3 -0
  210. package/src/resources/audio/transcriptions.ts +2 -1
  211. package/src/resources/beta/agents/agents.ts +15 -7
  212. package/src/resources/beta/agents/environments/templates.ts +6 -6
  213. package/src/resources/beta/agents/sessions/sessions.ts +4 -2
  214. package/src/resources/beta/agents/vaults/credentials.ts +21 -7
  215. package/src/resources/beta/responses/responses.ts +79 -6
  216. package/src/resources/files.ts +3 -3
  217. package/src/resources/index.ts +1 -1
  218. package/src/resources/live/forks/ws-base.ts +1 -1
  219. package/src/resources/live/sideband/ws-base.ts +1 -1
  220. package/src/resources/live/ws-base.ts +1 -1
  221. package/src/resources/realtime/api.md +8 -0
  222. package/src/resources/realtime/index.ts +1 -0
  223. package/src/resources/realtime/realtime.ts +6 -0
  224. package/src/resources/realtime/translations/client-secrets.ts +93 -0
  225. package/src/resources/realtime/translations/index.ts +4 -0
  226. package/src/resources/realtime/translations/translations.ts +15 -0
  227. package/src/resources/realtime/translations.ts +3 -0
  228. package/src/resources/responses/responses.ts +73 -6
  229. package/src/resources/shared.ts +5 -2
  230. package/src/resources/videos.ts +6 -0
  231. package/src/version.ts +1 -1
  232. package/version.d.mts +1 -1
  233. package/version.d.ts +1 -1
  234. package/version.js +1 -1
  235. package/version.mjs +1 -1
@@ -1,7 +1,8 @@
1
1
  import type { Response, ResponseOutputText, ResponseStreamEvent } from '../../resources/responses/responses';
2
- import type { ResponseAccumulatorContext } from './canonical-output-text';
2
+ import type { ResponseAccumulatorContext, ResponseOutputSnapshot } from './canonical-output-text';
3
3
  import { OpenAIError } from '../../error';
4
4
  import { hasOwn } from '../utils';
5
+ import { isObj } from '../utils/values';
5
6
  import {
6
7
  cloneResponse,
7
8
  createCanonicalResponseContext,
@@ -139,7 +140,7 @@ type ResponseIgnoredEvent = Extract<
139
140
  >;
140
141
 
141
142
  interface ResponseOutputIdentityIndex {
142
- snapshot: Response;
143
+ snapshot: ResponseOutputSnapshot;
143
144
  output: Response['output'];
144
145
  length: number;
145
146
  identities: Set<string>;
@@ -175,7 +176,7 @@ function validateArrayAppend(
175
176
  validateArrayIndex(collection, index, kind, true);
176
177
  }
177
178
 
178
- function getOutput(snapshot: Response, outputIndex: number): Response['output'][number] {
179
+ function getOutput(snapshot: ResponseOutputSnapshot, outputIndex: number): Response['output'][number] {
179
180
  validateArrayIndex(snapshot.output, outputIndex, 'output');
180
181
  const output = snapshot.output[outputIndex];
181
182
  if (!output) {
@@ -243,7 +244,7 @@ function addOutputItemIdentities(identities: Set<string>, keys: readonly string[
243
244
  }
244
245
  }
245
246
 
246
- function createResponseOutputIdentityIndex(snapshot: Response): ResponseOutputIdentityIndex {
247
+ function createResponseOutputIdentityIndex(snapshot: ResponseOutputSnapshot): ResponseOutputIdentityIndex {
247
248
  const identityIndex: ResponseOutputIdentityIndex = {
248
249
  snapshot,
249
250
  output: snapshot.output,
@@ -261,7 +262,7 @@ function createResponseOutputIdentityIndex(snapshot: Response): ResponseOutputId
261
262
 
262
263
  function getResponseOutputIdentityIndex(
263
264
  context: ResponseAccumulatorContext,
264
- snapshot: Response,
265
+ snapshot: ResponseOutputSnapshot,
265
266
  ): ResponseOutputIdentityIndex {
266
267
  const cached = responseOutputIdentityIndexes.get(context);
267
268
  if (
@@ -278,7 +279,10 @@ function getResponseOutputIdentityIndex(
278
279
  return identityIndex;
279
280
  }
280
281
 
281
- function cloneValidatedResponse(context: ResponseAccumulatorContext, response: Response): Response {
282
+ export function cloneValidatedResponse<T extends ResponseOutputSnapshot>(
283
+ context: ResponseAccumulatorContext,
284
+ response: T,
285
+ ): T {
282
286
  const nextContext = createCanonicalResponseContext();
283
287
  const snapshot = cloneResponse(nextContext, response);
284
288
  const identityIndex = createResponseOutputIdentityIndex(snapshot);
@@ -348,7 +352,7 @@ function getExpectedOutputItemType(event: ResponseItemScopedEvent): Response['ou
348
352
 
349
353
  function validateCompletedOutputItemIdentity(
350
354
  event: Extract<ResponseOutputItemEvent, { type: 'response.output_item.done' }>,
351
- snapshot: Response,
355
+ snapshot: ResponseOutputSnapshot,
352
356
  ): void {
353
357
  const output = getOutput(snapshot, event.output_index);
354
358
  const replacement = event.item;
@@ -376,7 +380,7 @@ function validateCompletedOutputItemIdentity(
376
380
 
377
381
  function validateOutputItemIdentity(
378
382
  event: ResponseAccumulatorEvent,
379
- snapshot: Response,
383
+ snapshot: ResponseOutputSnapshot,
380
384
  rejectInvalidShellTargets: boolean,
381
385
  ): void {
382
386
  if (event.type === 'response.output_item.done') {
@@ -434,7 +438,7 @@ function getContent<T>(content: T[], contentIndex: number): T {
434
438
  }
435
439
 
436
440
  function getShellOutputContent(
437
- snapshot: Response,
441
+ snapshot: ResponseOutputSnapshot,
438
442
  output: Extract<Response['output'][number], { type: 'shell_call_output' }>,
439
443
  commandIndex: number,
440
444
  ): (typeof output.output)[number] {
@@ -616,7 +620,7 @@ function sanitizeResponseEvent(
616
620
 
617
621
  function accumulateOutputItemEvent(
618
622
  event: ResponseAccumulatorEvent,
619
- snapshot: Response,
623
+ snapshot: ResponseOutputSnapshot,
620
624
  context: ResponseAccumulatorContext,
621
625
  ): event is ResponseOutputItemEvent {
622
626
  switch (event.type) {
@@ -637,7 +641,11 @@ function accumulateOutputItemEvent(
637
641
  context.outputTextIndex.append(text.length);
638
642
  }
639
643
  if (text) {
640
- snapshot.output_text += text;
644
+ if (context.deferOutputText) {
645
+ context.outputTextDirty = true;
646
+ } else {
647
+ snapshot.output_text += text;
648
+ }
641
649
  }
642
650
  return true;
643
651
  }
@@ -664,7 +672,7 @@ function accumulateOutputItemEvent(
664
672
 
665
673
  function accumulateContentPartAddedEvent(
666
674
  event: ResponseAccumulatorEvent,
667
- snapshot: Response,
675
+ snapshot: ResponseOutputSnapshot,
668
676
  context: ResponseAccumulatorContext,
669
677
  ): event is ResponseContentPartAddedEvent {
670
678
  switch (event.type) {
@@ -701,7 +709,7 @@ function accumulateContentPartAddedEvent(
701
709
 
702
710
  function accumulateContentPartDoneEvent(
703
711
  event: ResponseAccumulatorEvent,
704
- snapshot: Response,
712
+ snapshot: ResponseOutputSnapshot,
705
713
  context: ResponseAccumulatorContext,
706
714
  ): event is ResponseContentPartDoneEvent {
707
715
  switch (event.type) {
@@ -735,9 +743,57 @@ function accumulateContentPartDoneEvent(
735
743
  }
736
744
  }
737
745
 
746
+ // Streamed logprobs have a looser type than final output: bytes and even the
747
+ // top token fields may be missing. Do not fabricate them in typed SSE snapshots.
748
+ function isLogprobWithBytes(value: unknown): value is ResponseOutputText.Logprob.TopLogprob {
749
+ return (
750
+ isObj(value) &&
751
+ typeof value['token'] === 'string' &&
752
+ typeof value['logprob'] === 'number' &&
753
+ Array.isArray(value['bytes']) &&
754
+ // oxlint-disable-next-line anti-slop/no-runtime-typeof -- This boundary checks each byte against the generated final-output contract before exposing a typed SSE snapshot.
755
+ value['bytes'].every((byte) => typeof byte === 'number')
756
+ );
757
+ }
758
+
759
+ function isOutputLogprobs(value: unknown): value is ResponseOutputText.Logprob[] {
760
+ return (
761
+ Array.isArray(value) &&
762
+ value.every(
763
+ (entry) =>
764
+ isLogprobWithBytes(entry) &&
765
+ 'top_logprobs' in entry &&
766
+ Array.isArray(entry.top_logprobs) &&
767
+ entry.top_logprobs.every(isLogprobWithBytes),
768
+ )
769
+ );
770
+ }
771
+
772
+ function accumulateOutputLogprobs(
773
+ content: ResponseOutputText,
774
+ event: Extract<ResponseOutputTextEvent, { logprobs: unknown }>,
775
+ ): void {
776
+ const logprobs = structuredClone(event.logprobs);
777
+ if (!isOutputLogprobs(logprobs)) {
778
+ return;
779
+ }
780
+ if (event.type === 'response.output_text.done') {
781
+ if (logprobs.length > 0 || (Array.isArray(content.logprobs) && content.logprobs.length > 0)) {
782
+ content.logprobs = logprobs;
783
+ }
784
+ } else if (logprobs.length > 0) {
785
+ if (!Array.isArray(content.logprobs)) {
786
+ content.logprobs = [];
787
+ }
788
+ for (const logprob of logprobs) {
789
+ content.logprobs.push(logprob);
790
+ }
791
+ }
792
+ }
793
+
738
794
  function accumulateOutputTextEvent(
739
795
  event: ResponseAccumulatorEvent,
740
- snapshot: Response,
796
+ snapshot: ResponseOutputSnapshot,
741
797
  context: ResponseAccumulatorContext,
742
798
  ): event is ResponseOutputTextEvent {
743
799
  switch (event.type) {
@@ -748,6 +804,7 @@ function accumulateOutputTextEvent(
748
804
  if (content.type !== 'output_text') {
749
805
  throw new OpenAIError(`expected content to be 'output_text', got ${content.type}`);
750
806
  }
807
+ accumulateOutputLogprobs(content, event);
751
808
  const previousText = content.text;
752
809
  ensureCanonicalOutputText(context, snapshot);
753
810
  content.text = previousText + event.delta;
@@ -756,7 +813,13 @@ function accumulateOutputTextEvent(
756
813
  event.output_index === snapshot.output.length - 1 &&
757
814
  event.content_index === output.content.length - 1
758
815
  ) {
759
- snapshot.output_text += event.delta;
816
+ if (context.deferOutputText) {
817
+ if (event.delta !== '') {
818
+ context.outputTextDirty = true;
819
+ }
820
+ } else {
821
+ snapshot.output_text += event.delta;
822
+ }
760
823
  } else {
761
824
  updateOutputText(
762
825
  context,
@@ -777,6 +840,7 @@ function accumulateOutputTextEvent(
777
840
  if (content.type !== 'output_text') {
778
841
  throw new OpenAIError(`expected content to be 'output_text', got ${content.type}`);
779
842
  }
843
+ accumulateOutputLogprobs(content, event);
780
844
  const previousText = content.text;
781
845
  ensureCanonicalOutputText(context, snapshot);
782
846
  content.text = event.text;
@@ -815,7 +879,7 @@ function accumulateOutputTextEvent(
815
879
 
816
880
  function accumulateRefusalAndArgumentsEvent(
817
881
  event: ResponseAccumulatorEvent,
818
- snapshot: Response,
882
+ snapshot: ResponseOutputSnapshot,
819
883
  ): event is ResponseRefusalAndArgumentsEvent {
820
884
  switch (event.type) {
821
885
  case 'response.refusal.delta': {
@@ -890,7 +954,7 @@ function accumulateRefusalAndArgumentsEvent(
890
954
 
891
955
  function accumulateShellEvent(
892
956
  event: ResponseAccumulatorEvent,
893
- snapshot: Response,
957
+ snapshot: ResponseOutputSnapshot,
894
958
  ): event is ResponseShellEvent {
895
959
  switch (event.type) {
896
960
  case 'response.shell_call_command.added':
@@ -937,7 +1001,7 @@ function accumulateShellEvent(
937
1001
 
938
1002
  function accumulateReasoningEvent(
939
1003
  event: ResponseAccumulatorEvent,
940
- snapshot: Response,
1004
+ snapshot: ResponseOutputSnapshot,
941
1005
  ): event is ResponseReasoningEvent {
942
1006
  switch (event.type) {
943
1007
  case 'response.reasoning_text.delta': {
@@ -1008,7 +1072,7 @@ function accumulateReasoningEvent(
1008
1072
 
1009
1073
  function accumulateCodeInterpreterEvent(
1010
1074
  event: ResponseAccumulatorEvent,
1011
- snapshot: Response,
1075
+ snapshot: ResponseOutputSnapshot,
1012
1076
  ): event is ResponseCodeInterpreterEvent {
1013
1077
  switch (event.type) {
1014
1078
  case 'response.code_interpreter_call_code.delta': {
@@ -1054,7 +1118,7 @@ function accumulateCodeInterpreterEvent(
1054
1118
 
1055
1119
  function accumulateSearchStatusEvent(
1056
1120
  event: ResponseAccumulatorEvent,
1057
- snapshot: Response,
1121
+ snapshot: ResponseOutputSnapshot,
1058
1122
  ): event is ResponseSearchStatusEvent {
1059
1123
  switch (event.type) {
1060
1124
  case 'response.file_search_call.in_progress': {
@@ -1107,7 +1171,7 @@ function accumulateSearchStatusEvent(
1107
1171
 
1108
1172
  function accumulateImageAndMcpStatusEvent(
1109
1173
  event: ResponseAccumulatorEvent,
1110
- snapshot: Response,
1174
+ snapshot: ResponseOutputSnapshot,
1111
1175
  ): event is ResponseImageAndMcpStatusEvent {
1112
1176
  switch (event.type) {
1113
1177
  case 'response.image_generation_call.in_progress': {
@@ -1158,7 +1222,7 @@ function accumulateImageAndMcpStatusEvent(
1158
1222
  }
1159
1223
  }
1160
1224
 
1161
- function isResponseLifecycleEvent(event: ResponseAccumulatorEvent): event is ResponseLifecycleEvent {
1225
+ function isResponseLifecycleEvent(event: { type: string }): event is ResponseLifecycleEvent {
1162
1226
  switch (event.type) {
1163
1227
  case 'response.created':
1164
1228
  case 'response.queued':
@@ -1174,7 +1238,7 @@ function isResponseLifecycleEvent(event: ResponseAccumulatorEvent): event is Res
1174
1238
  }
1175
1239
  }
1176
1240
 
1177
- function isIgnoredResponseEvent(event: ResponseAccumulatorEvent): event is ResponseIgnoredEvent {
1241
+ function isIgnoredResponseEvent(event: { type: string }): event is ResponseIgnoredEvent {
1178
1242
  switch (event.type) {
1179
1243
  case 'response.audio.delta':
1180
1244
  case 'response.audio.done':
@@ -1199,65 +1263,100 @@ export function createResponseContext(): ResponseAccumulatorContext {
1199
1263
  return createCanonicalResponseContext();
1200
1264
  }
1201
1265
 
1202
- export function accumulateResponseWithContext(
1203
- event: ResponseAccumulatorEvent,
1204
- snapshot: Response | undefined,
1205
- context: ResponseAccumulatorContext,
1206
- rejectInvalidShellTargets = false,
1207
- onSanitizedEvent?: (event: ResponseStreamEvent) => void,
1208
- ): Response {
1209
- const dispatchEvent = sanitizeResponseEvent(event);
1210
- if (onSanitizedEvent && dispatchEvent.type !== 'keepalive') {
1211
- onSanitizedEvent(dispatchEvent);
1212
- }
1213
-
1214
- if (!snapshot) {
1215
- if (dispatchEvent.type !== 'response.created') {
1216
- throw new OpenAIError(
1217
- `When snapshot hasn't been set yet, expected 'response.created' event, got ${dispatchEvent.type}`,
1218
- );
1219
- }
1220
- return cloneValidatedResponse(context, dispatchEvent.response);
1221
- }
1266
+ type ResponseOutputEvent = Exclude<ResponseAccumulatorEvent, ResponseLifecycleEvent>;
1222
1267
 
1268
+ function accumulateResponseOutput(
1269
+ dispatchEvent: ResponseAccumulatorEvent,
1270
+ snapshot: ResponseOutputSnapshot,
1271
+ context: ResponseAccumulatorContext,
1272
+ rejectInvalidShellTargets: boolean,
1273
+ ): dispatchEvent is ResponseOutputEvent {
1223
1274
  validateOutputItemIdentity(dispatchEvent, snapshot, rejectInvalidShellTargets);
1224
-
1225
1275
  if (accumulateOutputItemEvent(dispatchEvent, snapshot, context)) {
1226
- return snapshot;
1276
+ return true;
1227
1277
  }
1228
1278
  if (accumulateContentPartAddedEvent(dispatchEvent, snapshot, context)) {
1229
- return snapshot;
1279
+ return true;
1230
1280
  }
1231
1281
  if (accumulateContentPartDoneEvent(dispatchEvent, snapshot, context)) {
1232
- return snapshot;
1282
+ return true;
1233
1283
  }
1234
1284
  if (accumulateOutputTextEvent(dispatchEvent, snapshot, context)) {
1235
- return snapshot;
1285
+ return true;
1236
1286
  }
1237
1287
  if (accumulateRefusalAndArgumentsEvent(dispatchEvent, snapshot)) {
1238
- return snapshot;
1288
+ return true;
1239
1289
  }
1240
1290
  if (accumulateShellEvent(dispatchEvent, snapshot)) {
1241
- return snapshot;
1291
+ return true;
1242
1292
  }
1243
1293
  if (accumulateReasoningEvent(dispatchEvent, snapshot)) {
1244
- return snapshot;
1294
+ return true;
1245
1295
  }
1246
1296
  if (accumulateCodeInterpreterEvent(dispatchEvent, snapshot)) {
1247
- return snapshot;
1297
+ return true;
1248
1298
  }
1249
1299
  if (accumulateSearchStatusEvent(dispatchEvent, snapshot)) {
1250
- return snapshot;
1300
+ return true;
1251
1301
  }
1252
1302
  if (accumulateImageAndMcpStatusEvent(dispatchEvent, snapshot)) {
1253
- return snapshot;
1303
+ return true;
1254
1304
  }
1255
1305
 
1256
- if (isResponseLifecycleEvent(dispatchEvent)) {
1306
+ if (isIgnoredResponseEvent(dispatchEvent)) {
1307
+ return true;
1308
+ }
1309
+ return false;
1310
+ }
1311
+
1312
+ /** Matches shared events that can change output. Validation still occurs before mutation. */
1313
+ export function isResponseOutputEvent(event: { type: string }): event is ResponseOutputEvent {
1314
+ // SAFETY: Membership of the existing SSE tag set is checked before using shared output validators.
1315
+ return (
1316
+ supportedResponseEventTypes.has(event.type as ResponseAccumulatorEvent['type']) &&
1317
+ !isResponseLifecycleEvent(event) &&
1318
+ !isIgnoredResponseEvent(event)
1319
+ );
1320
+ }
1321
+
1322
+ /** Applies the same strict output validation and mutations without requiring response metadata. */
1323
+ export function accumulateWebSocketOutput(
1324
+ event: ResponseOutputEvent,
1325
+ snapshot: ResponseOutputSnapshot,
1326
+ context: ResponseAccumulatorContext,
1327
+ ): void {
1328
+ const dispatchEvent = sanitizeResponseEvent(event);
1329
+ if (!accumulateResponseOutput(dispatchEvent, snapshot, context, true)) {
1330
+ throw new OpenAIError('Unsupported WebSocket output event');
1331
+ }
1332
+ }
1333
+
1334
+ export function accumulateResponseWithContext(
1335
+ event: ResponseAccumulatorEvent,
1336
+ snapshot: Response | undefined,
1337
+ context: ResponseAccumulatorContext,
1338
+ rejectInvalidShellTargets = false,
1339
+ onSanitizedEvent?: (event: ResponseStreamEvent) => void,
1340
+ ): Response {
1341
+ const dispatchEvent = sanitizeResponseEvent(event);
1342
+ if (onSanitizedEvent && dispatchEvent.type !== 'keepalive') {
1343
+ onSanitizedEvent(dispatchEvent);
1344
+ }
1345
+
1346
+ if (!snapshot) {
1347
+ if (dispatchEvent.type !== 'response.created') {
1348
+ throw new OpenAIError(
1349
+ `When snapshot hasn't been set yet, expected 'response.created' event, got ${dispatchEvent.type}`,
1350
+ );
1351
+ }
1257
1352
  return cloneValidatedResponse(context, dispatchEvent.response);
1258
1353
  }
1259
- if (isIgnoredResponseEvent(dispatchEvent)) {
1354
+
1355
+ if (accumulateResponseOutput(dispatchEvent, snapshot, context, rejectInvalidShellTargets)) {
1260
1356
  return snapshot;
1261
1357
  }
1358
+ if (isResponseLifecycleEvent(dispatchEvent)) {
1359
+ return cloneValidatedResponse(context, dispatchEvent.response);
1360
+ }
1262
1361
  return assertNever(dispatchEvent);
1263
1362
  }
@@ -299,16 +299,18 @@ export class SendQueue<T = unknown> {
299
299
  /**
300
300
  * Send every queued message via `send`. If `send` throws, the failing
301
301
  * message and all subsequent messages are re-queued and the error is
302
- * re-thrown so the caller can report it.
302
+ * re-thrown so the caller can report it. Endpoints that cannot safely replay
303
+ * an attempted write use `requeueFailed: false`. Never-attempted messages
304
+ * remain queued, including messages enqueued during the failed send.
303
305
  */
304
- flush(send: (data: RawWebSocketData) => void): void {
306
+ flush(send: (data: RawWebSocketData) => void, options?: { requeueFailed?: boolean }): void {
305
307
  const pending = this._queue.splice(0);
306
308
  this._bytes = 0;
307
309
  for (let i = 0; i < pending.length; i++) {
308
310
  try {
309
311
  send(pending[i]!.data);
310
312
  } catch (err) {
311
- const remaining = pending.slice(i);
313
+ const remaining = pending.slice(options?.requeueFailed === false ? i + 1 : i);
312
314
  this._queue = [...remaining, ...this._queue];
313
315
  this._bytes = this._queue.reduce((sum, item) => sum + item.byteLength, 0);
314
316
  throw err;
@@ -124,10 +124,11 @@ export class TranscriptGrouping {
124
124
  }
125
125
 
126
126
  advance(timeMs: number, hasIncomingUser = false): GroupingUpdate[] {
127
+ // Compare against deadline()'s sums so fractional deadlines remain reachable.
127
128
  if (
128
129
  this.current &&
129
130
  this.buffered &&
130
- timeMs - this.current.endMs >= this.options.minTurnSeparationMs &&
131
+ timeMs >= this.current.endMs + this.options.minTurnSeparationMs &&
131
132
  !this.keepBackchannel(timeMs)
132
133
  ) {
133
134
  this.buffered = this.maybeDropBackchannel(timeMs);
@@ -138,7 +139,7 @@ export class TranscriptGrouping {
138
139
  if (
139
140
  this.current?.speaker === 'assistant' &&
140
141
  !hasIncomingUser &&
141
- timeMs - this.current.endMs >= this.options.assistantSilenceMs
142
+ timeMs >= this.current.endMs + this.options.assistantSilenceMs
142
143
  ) {
143
144
  return this.finishCurrent('inactivity');
144
145
  }
@@ -339,7 +340,7 @@ export class TranscriptGrouping {
339
340
  this.buffered?.canDropAsBackchannel === true &&
340
341
  !this.userContinued() &&
341
342
  !this.recentAssistant() &&
342
- timeMs - this.buffered.endMs < this.options.backchannelIsolationMs
343
+ timeMs < this.buffered.endMs + this.options.backchannelIsolationMs
343
344
  );
344
345
  }
345
346
 
@@ -353,12 +354,12 @@ export class TranscriptGrouping {
353
354
  if (this.recentAssistant()) {
354
355
  return this.buffered;
355
356
  }
356
- if (next && next.startMs - this.buffered.endMs < this.options.backchannelIsolationMs) {
357
+ if (next && next.startMs < this.buffered.endMs + this.options.backchannelIsolationMs) {
357
358
  return this.buffered;
358
359
  }
359
360
  if (
360
361
  !next &&
361
- (timeMs === undefined || timeMs - this.buffered.endMs < this.options.backchannelIsolationMs)
362
+ (timeMs === undefined || timeMs < this.buffered.endMs + this.options.backchannelIsolationMs)
362
363
  ) {
363
364
  return this.buffered;
364
365
  }