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
@@ -0,0 +1,374 @@
1
+ import { OpenAIError } from '../../core/error';
2
+ import type { ResponseOutputSnapshot } from '../../internal/responses/canonical-output-text';
3
+ import { ensureCanonicalOutputText, getOutputText } from '../../internal/responses/canonical-output-text';
4
+ import {
5
+ accumulateWebSocketOutput,
6
+ cloneValidatedResponse,
7
+ createResponseContext,
8
+ isResponseOutputEvent,
9
+ } from '../../internal/responses/response-accumulator';
10
+ import { hasOwn } from '../../internal/utils';
11
+ import { isObj } from '../../internal/utils/values';
12
+ import type { Response, ResponseOutputItem } from '../../resources/responses/responses';
13
+ import type { ResponsesWebSocketEvent } from './responses-websocket-lane';
14
+
15
+ /** The discriminator is checked; other wire fields can be absent or not schema-valid yet. */
16
+ type ProvisionalOutputItem = {
17
+ // oxlint-disable-next-line anti-slop/no-unsafe-dictionary-type -- These are exactly the generated field names, but raw provisional values have deliberately not been schema-decoded.
18
+ [Type in ResponseOutputItem['type']]: { type: Type } & Partial<
19
+ Record<Exclude<keyof Extract<ResponseOutputItem, { type: Type }>, 'type'>, unknown>
20
+ >;
21
+ }[ResponseOutputItem['type']];
22
+ type PartSelector = { content_index: number; annotation_index?: number } | { summary_index: number };
23
+
24
+ // Keep the raw boundary aligned with the generated discriminants. Future wire
25
+ // items still belong to the raw lane and authoritative terminal event.
26
+ const outputItemTypes = {
27
+ message: true,
28
+ file_search_call: true,
29
+ function_call: true,
30
+ function_call_output: true,
31
+ web_search_call: true,
32
+ computer_call: true,
33
+ computer_call_output: true,
34
+ reasoning: true,
35
+ program: true,
36
+ program_output: true,
37
+ tool_search_call: true,
38
+ tool_search_output: true,
39
+ additional_tools: true,
40
+ compaction: true,
41
+ image_generation_call: true,
42
+ code_interpreter_call: true,
43
+ local_shell_call: true,
44
+ local_shell_call_output: true,
45
+ shell_call: true,
46
+ shell_call_output: true,
47
+ apply_patch_call: true,
48
+ apply_patch_call_output: true,
49
+ mcp_call: true,
50
+ mcp_list_tools: true,
51
+ mcp_approval_request: true,
52
+ mcp_approval_response: true,
53
+ custom_tool_call: true,
54
+ custom_tool_call_output: true,
55
+ } satisfies Record<ResponseOutputItem['type'], true>;
56
+
57
+ /** Provisional output is never substituted for a completed, failed, or incomplete response. */
58
+ export type ResponsesWebSocketAccumulatorState =
59
+ | {
60
+ phase: 'provisional';
61
+ // oxlint-disable-next-line anti-slop/no-unsafe-dictionary-type -- As with output items, only generated field names are typed: provisional wire metadata is not yet validated and must be narrowed by callers.
62
+ snapshot: { output: ProvisionalOutputItem[]; output_text: string } & {
63
+ [Field in Exclude<keyof Response, 'output' | 'output_text'>]?: unknown;
64
+ };
65
+ }
66
+ | { phase: 'unavailable'; error: Error }
67
+ | { phase: 'terminal'; event: ResponsesWebSocketEvent };
68
+
69
+ /**
70
+ * Optional, caller-fed reconstruction of one lane's provisional output.
71
+ *
72
+ * Feed the raw events returned by lane.receive(). Use a separate instance per
73
+ * lane. This helper never reads, sends, closes, or registers a listener on a
74
+ * socket; raw events remain in the caller's hands. Tools remain output data.
75
+ * Provisional response metadata is preserved as received and is not yet schema
76
+ * validated. Narrow its unknown fields before use. Omitted fields remain absent.
77
+ * Raw deltas give per-event progress. Use outputAt(event.output_index) when an
78
+ * item finishes, or pass its content_index/summary_index to read only a changed
79
+ * part. For citations use content_index and annotation_index together. A full
80
+ * current, item, or part read materializes its entire nested contents, so
81
+ * reserve those reads for when that complete snapshot is needed.
82
+ *
83
+ * A socket stream can omit the item/content scaffolding required for deltas.
84
+ * Such a response is marked unavailable until the next creation or terminal
85
+ * event. Terminal events, including failures and errors, are retained exactly as
86
+ * delivered, without filling omitted output from provisional data.
87
+ */
88
+ export class ResponsesWebSocketAccumulator {
89
+ #context = createResponseContext();
90
+ #current:
91
+ | { phase: 'provisional'; snapshot: ResponseOutputSnapshot }
92
+ | Exclude<ResponsesWebSocketAccumulatorState, { phase: 'provisional' }>
93
+ | undefined;
94
+
95
+ /** Materialize the full state. For per-item progress prefer outputAt(). */
96
+ get current(): ResponsesWebSocketAccumulatorState | undefined {
97
+ if (this.#current?.phase === 'provisional') {
98
+ if (this.#context.outputTextDirty) {
99
+ this.#current.snapshot.output_text = this.#current.snapshot.output
100
+ .map((output) => getOutputText(this.#context, output))
101
+ .join('');
102
+ this.#context.outputTextDirty = false;
103
+ }
104
+ return {
105
+ phase: 'provisional',
106
+ snapshot: ResponsesWebSocketAccumulator.#copyOutput(this.#current.snapshot),
107
+ };
108
+ }
109
+ return structuredClone(this.#current);
110
+ }
111
+
112
+ /**
113
+ * Read one provisional item by wire output_index, or just one message/
114
+ * reasoning part with its wire content_index/summary_index. Add annotation_index
115
+ * to content_index to read one raw citation. Full item and part reads copy all
116
+ * nested entries; prefer a selector for progress on a growing collection.
117
+ * Copies remain valid after further events, and cannot change the accumulator.
118
+ * Returns undefined for absent/mismatched parts or outside provisional output.
119
+ */
120
+ outputAt(outputIndex: number): ProvisionalOutputItem | undefined;
121
+ // oxlint-disable-next-line anti-slop/no-unknown-returns -- Raw socket parts and annotations are preserved, not schema-decoded. Requiring narrowing avoids an unproven public SDK contract.
122
+ outputAt(outputIndex: number, part: PartSelector): unknown;
123
+ // oxlint-disable-next-line anti-slop/no-unknown-returns -- Only the generated output discriminator is validated; every raw nested value must remain unknown to callers.
124
+ outputAt(outputIndex: number, part?: PartSelector): unknown {
125
+ if (this.#current?.phase !== 'provisional') {
126
+ return undefined;
127
+ }
128
+ const output = ResponsesWebSocketAccumulator.#at(this.#current.snapshot.output, outputIndex);
129
+ if (!part) {
130
+ return ResponsesWebSocketAccumulator.#copyOutput(output);
131
+ }
132
+ if ('content_index' in part) {
133
+ if (output?.type === 'message' || output?.type === 'reasoning') {
134
+ const content = ResponsesWebSocketAccumulator.#at(output.content, part.content_index);
135
+ if ('annotation_index' in part) {
136
+ return ResponsesWebSocketAccumulator.#copyOutput(
137
+ content?.type === 'output_text'
138
+ ? ResponsesWebSocketAccumulator.#at(content.annotations, part.annotation_index)
139
+ : undefined,
140
+ );
141
+ }
142
+ return ResponsesWebSocketAccumulator.#copyOutput(content);
143
+ }
144
+ } else if (output?.type === 'reasoning') {
145
+ return ResponsesWebSocketAccumulator.#copyOutput(
146
+ ResponsesWebSocketAccumulator.#at(output.summary, part.summary_index),
147
+ );
148
+ }
149
+ return undefined;
150
+ }
151
+
152
+ // Validate the same index rules at every selector depth before copying only
153
+ // the selected entry. The caller-supplied wire scaffold may not be an array.
154
+ static #at<T extends readonly unknown[]>(
155
+ collection: T | undefined,
156
+ index: number | undefined,
157
+ ): T[number] | undefined {
158
+ if (!Array.isArray(collection) || index === undefined || !Number.isSafeInteger(index) || index < 0) {
159
+ return undefined;
160
+ }
161
+ return collection[index];
162
+ }
163
+
164
+ // Provisional output is made of parsed JSON containers and immutable scalars.
165
+ // Detach containers without flattening and copying the accumulated strings at
166
+ // each read. Error and terminal snapshots use structuredClone as before.
167
+ static #copyOutput<T>(value: T): T {
168
+ if (Array.isArray(value)) {
169
+ // SAFETY: Copying the array preserves every element's type and order.
170
+ return value.map((item) => ResponsesWebSocketAccumulator.#copyOutput(item)) as T;
171
+ }
172
+ if (isObj(value)) {
173
+ // SAFETY: Copying own data properties preserves the snapshot's JSON shape.
174
+ // fromEntries defines "__proto__" as an own property instead of invoking a setter.
175
+ return Object.fromEntries(
176
+ Object.entries(value).map(([key, item]) => [key, ResponsesWebSocketAccumulator.#copyOutput(item)]),
177
+ ) as T;
178
+ }
179
+ return value;
180
+ }
181
+
182
+ /** Drop retained provisional and terminal state without affecting any lane. */
183
+ reset(): void {
184
+ this.#context = createResponseContext();
185
+ this.#current = undefined;
186
+ }
187
+
188
+ // Validate the discriminant and scaffolds that deltas and canonical text use. Raw
189
+ // WebSocket items have not been decoded through the generated response union.
190
+ // oxlint-disable-next-line anti-slop/no-unknown-parameters -- Output from a raw lane event has not passed a tool schema. This is its opt-in accumulator boundary.
191
+ static #validateOutputScaffold(item: unknown): void {
192
+ if (
193
+ !isObj(item) ||
194
+ !hasOwn(item, 'type') ||
195
+ // oxlint-disable-next-line anti-slop/no-runtime-typeof -- An unknown discriminator cannot satisfy the generated typed preview union.
196
+ typeof item['type'] !== 'string' ||
197
+ !hasOwn(outputItemTypes, item['type'])
198
+ ) {
199
+ throw new OpenAIError('Invalid Responses WebSocket output item');
200
+ }
201
+ if (item['type'] === 'message') {
202
+ if (!Array.isArray(item['content'])) {
203
+ throw new OpenAIError('Invalid Responses WebSocket message content');
204
+ }
205
+ for (const part of item['content']) {
206
+ ResponsesWebSocketAccumulator.#validateContentPart(part);
207
+ }
208
+ }
209
+ if (
210
+ item['type'] === 'function_call' ||
211
+ item['type'] === 'custom_tool_call' ||
212
+ item['type'] === 'mcp_call'
213
+ ) {
214
+ const field = item['type'] === 'custom_tool_call' ? 'input' : 'arguments';
215
+ if (
216
+ !hasOwn(item, 'name') ||
217
+ // oxlint-disable-next-line anti-slop/no-runtime-typeof -- The caller feeds raw socket events; validate own required tool fields before exposing a typed snapshot.
218
+ typeof item['name'] !== 'string' ||
219
+ !hasOwn(item, field) ||
220
+ // oxlint-disable-next-line anti-slop/no-runtime-typeof -- Missing and numeric tool data cannot be extended as text.
221
+ typeof item[field] !== 'string'
222
+ ) {
223
+ throw new OpenAIError('Invalid Responses WebSocket tool scaffold');
224
+ }
225
+ if (
226
+ item['type'] === 'mcp_call' &&
227
+ (!hasOwn(item, 'server_label') ||
228
+ // oxlint-disable-next-line anti-slop/no-runtime-typeof -- The server identity on an MCP scaffold is raw data, not a generated decoded string.
229
+ typeof item['server_label'] !== 'string')
230
+ ) {
231
+ throw new OpenAIError('Invalid Responses WebSocket MCP server label');
232
+ }
233
+ }
234
+ }
235
+
236
+ // Check only fields used by canonicalization; other provisional metadata stays raw.
237
+ // oxlint-disable-next-line anti-slop/no-unknown-parameters -- Parts from the WebSocket wire are not schema-decoded, including within the initial output.
238
+ static #validateContentPart(part: unknown): void {
239
+ if (
240
+ !isObj(part) ||
241
+ // oxlint-disable-next-line anti-slop/no-runtime-typeof -- Raw output_text must not be silently coerced during a later provisional read.
242
+ (part['type'] === 'output_text' && typeof part['text'] !== 'string')
243
+ ) {
244
+ throw new OpenAIError('Invalid Responses WebSocket content part');
245
+ }
246
+ }
247
+
248
+ static #validateOutputEvent(event: Parameters<typeof accumulateWebSocketOutput>[0]): void {
249
+ switch (event.type) {
250
+ case 'response.output_item.added':
251
+ case 'response.output_item.done': {
252
+ ResponsesWebSocketAccumulator.#validateOutputScaffold(event.item);
253
+ break;
254
+ }
255
+ case 'response.content_part.added':
256
+ case 'response.content_part.done': {
257
+ ResponsesWebSocketAccumulator.#validateContentPart(event.part);
258
+ break;
259
+ }
260
+ case 'response.function_call_arguments.delta':
261
+ case 'response.mcp_call_arguments.delta':
262
+ case 'response.custom_tool_call_input.delta':
263
+ case 'response.output_text.delta':
264
+ case 'response.refusal.delta': {
265
+ // oxlint-disable-next-line anti-slop/no-runtime-typeof -- The cloned raw event has not been schema-validated; do not coerce data into typed tool arguments.
266
+ if (typeof event.delta !== 'string') {
267
+ throw new OpenAIError('Invalid Responses WebSocket tool delta');
268
+ }
269
+ break;
270
+ }
271
+ case 'response.function_call_arguments.done':
272
+ case 'response.mcp_call_arguments.done': {
273
+ // oxlint-disable-next-line anti-slop/no-runtime-typeof -- A final raw argument payload must satisfy the field exposed by the provisional snapshot.
274
+ if (typeof event.arguments !== 'string') {
275
+ throw new OpenAIError('Invalid Responses WebSocket tool arguments');
276
+ }
277
+ break;
278
+ }
279
+ case 'response.custom_tool_call_input.done': {
280
+ // oxlint-disable-next-line anti-slop/no-runtime-typeof -- Do not substitute omitted, null or object input for the typed text field.
281
+ if (typeof event.input !== 'string') {
282
+ throw new OpenAIError('Invalid Responses WebSocket custom tool input');
283
+ }
284
+ break;
285
+ }
286
+ case 'response.output_text.done': {
287
+ // oxlint-disable-next-line anti-slop/no-runtime-typeof -- Only actual wire text can replace the accumulated message text.
288
+ if (typeof event.text !== 'string') {
289
+ throw new OpenAIError('Invalid Responses WebSocket completed text');
290
+ }
291
+ break;
292
+ }
293
+ case 'response.refusal.done': {
294
+ // oxlint-disable-next-line anti-slop/no-runtime-typeof -- Refusals use their own finalized wire text field.
295
+ if (typeof event.refusal !== 'string') {
296
+ throw new OpenAIError('Invalid Responses WebSocket completed refusal');
297
+ }
298
+ break;
299
+ }
300
+ default: {
301
+ break;
302
+ }
303
+ }
304
+ }
305
+
306
+ #start(event: { response?: unknown }): void {
307
+ this.reset();
308
+ if (!isObj(event.response)) {
309
+ throw new OpenAIError('Responses WebSocket created event must contain a response');
310
+ }
311
+ // Only output fields are normalized; omitted response metadata is never invented.
312
+ const { output, output_text: outputText } = event.response;
313
+ if (
314
+ (output !== undefined && !Array.isArray(output)) ||
315
+ // oxlint-disable-next-line anti-slop/no-runtime-typeof -- Validate optional text from an untyped WebSocket lifecycle before placing it in a typed provisional snapshot.
316
+ (outputText !== undefined && typeof outputText !== 'string')
317
+ ) {
318
+ throw new OpenAIError('Invalid Responses WebSocket initial output');
319
+ }
320
+ if (output !== undefined) {
321
+ for (const item of output) {
322
+ ResponsesWebSocketAccumulator.#validateOutputScaffold(item);
323
+ }
324
+ }
325
+ const snapshot = cloneValidatedResponse(this.#context, {
326
+ ...event.response,
327
+ output: output ?? [],
328
+ output_text: outputText ?? '',
329
+ });
330
+ if (outputText === undefined) {
331
+ ensureCanonicalOutputText(this.#context, snapshot);
332
+ }
333
+ this.#context.deferOutputText = true;
334
+ this.#current = { phase: 'provisional', snapshot };
335
+ }
336
+
337
+ /** Record one raw lane event. Unknown event types leave the current state unchanged. */
338
+ add(event: ResponsesWebSocketEvent): void {
339
+ if (
340
+ event.type === 'response.completed' ||
341
+ event.type === 'response.failed' ||
342
+ event.type === 'response.incomplete' ||
343
+ event.type === 'error'
344
+ ) {
345
+ this.#context = createResponseContext();
346
+ this.#current = { phase: 'terminal', event: structuredClone(event) };
347
+ return;
348
+ }
349
+ try {
350
+ if (event.type === 'response.created') {
351
+ this.#start({ response: event.response });
352
+ return;
353
+ }
354
+ if (
355
+ !isResponseOutputEvent(event) ||
356
+ this.#current?.phase === 'unavailable' ||
357
+ this.#current?.phase === 'terminal'
358
+ ) {
359
+ return;
360
+ }
361
+ if (this.#current?.phase !== 'provisional') {
362
+ throw new OpenAIError("Cannot reconstruct WebSocket output before 'response.created'");
363
+ }
364
+ const outputEvent = structuredClone(event);
365
+ ResponsesWebSocketAccumulator.#validateOutputEvent(outputEvent);
366
+ accumulateWebSocketOutput(outputEvent, this.#current.snapshot, this.#context);
367
+ } catch (error) {
368
+ this.#current = {
369
+ phase: 'unavailable',
370
+ error: error instanceof Error ? error : new OpenAIError('Invalid Responses WebSocket output event'),
371
+ };
372
+ }
373
+ }
374
+ }