@opencode-ai/ai 0.0.0-bootstrap.0 → 0.0.0-next-15617

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 (151) hide show
  1. package/README.md +176 -1
  2. package/dist/cache-policy.d.ts +2 -0
  3. package/dist/cache-policy.js +108 -0
  4. package/dist/index.d.ts +14 -0
  5. package/dist/index.js +9 -0
  6. package/dist/llm.d.ts +223 -0
  7. package/dist/llm.js +81 -0
  8. package/dist/protocols/anthropic-messages.d.ts +503 -0
  9. package/dist/protocols/anthropic-messages.js +745 -0
  10. package/dist/protocols/bedrock-converse.d.ts +476 -0
  11. package/dist/protocols/bedrock-converse.js +526 -0
  12. package/dist/protocols/bedrock-event-stream.d.ts +9 -0
  13. package/dist/protocols/bedrock-event-stream.js +64 -0
  14. package/dist/protocols/gemini.d.ts +220 -0
  15. package/dist/protocols/gemini.js +420 -0
  16. package/dist/protocols/google-vertex-anthropic.d.ts +494 -0
  17. package/dist/protocols/google-vertex-anthropic.js +33 -0
  18. package/dist/protocols/google-vertex-gemini.d.ts +59 -0
  19. package/dist/protocols/google-vertex-gemini.js +18 -0
  20. package/dist/protocols/index.d.ts +9 -0
  21. package/dist/protocols/index.js +9 -0
  22. package/dist/protocols/openai-chat.d.ts +391 -0
  23. package/dist/protocols/openai-chat.js +423 -0
  24. package/dist/protocols/openai-compatible-chat.d.ts +73 -0
  25. package/dist/protocols/openai-compatible-chat.js +20 -0
  26. package/dist/protocols/openai-compatible-responses.d.ts +85 -0
  27. package/dist/protocols/openai-compatible-responses.js +18 -0
  28. package/dist/protocols/openai-responses.d.ts +613 -0
  29. package/dist/protocols/openai-responses.js +828 -0
  30. package/dist/protocols/shared.d.ts +192 -0
  31. package/dist/protocols/shared.js +236 -0
  32. package/dist/protocols/utils/bedrock-auth.d.ts +18 -0
  33. package/dist/protocols/utils/bedrock-auth.js +41 -0
  34. package/dist/protocols/utils/bedrock-cache.d.ts +15 -0
  35. package/dist/protocols/utils/bedrock-cache.js +29 -0
  36. package/dist/protocols/utils/bedrock-media.d.ts +49 -0
  37. package/dist/protocols/utils/bedrock-media.js +68 -0
  38. package/dist/protocols/utils/cache.d.ts +6 -0
  39. package/dist/protocols/utils/cache.js +8 -0
  40. package/dist/protocols/utils/gemini-tool-schema.d.ts +2 -0
  41. package/dist/protocols/utils/gemini-tool-schema.js +83 -0
  42. package/dist/protocols/utils/lifecycle.d.ts +19 -0
  43. package/dist/protocols/utils/lifecycle.js +65 -0
  44. package/dist/protocols/utils/openai-options.d.ts +22 -0
  45. package/dist/protocols/utils/openai-options.js +64 -0
  46. package/dist/protocols/utils/tool-schema.d.ts +7 -0
  47. package/dist/protocols/utils/tool-schema.js +79 -0
  48. package/dist/protocols/utils/tool-stream.d.ts +155 -0
  49. package/dist/protocols/utils/tool-stream.js +136 -0
  50. package/dist/protocols.d.ts +1 -0
  51. package/dist/protocols.js +1 -0
  52. package/dist/provider-error.d.ts +13 -0
  53. package/dist/provider-error.js +122 -0
  54. package/dist/provider-package.d.ts +13 -0
  55. package/dist/provider-package.js +1 -0
  56. package/dist/provider.d.ts +23 -0
  57. package/dist/provider.js +2 -0
  58. package/dist/providers/amazon-bedrock.d.ts +150 -0
  59. package/dist/providers/amazon-bedrock.js +41 -0
  60. package/dist/providers/anthropic-compatible.d.ts +183 -0
  61. package/dist/providers/anthropic-compatible.js +44 -0
  62. package/dist/providers/anthropic.d.ts +181 -0
  63. package/dist/providers/anthropic.js +39 -0
  64. package/dist/providers/azure/chat.d.ts +2 -0
  65. package/dist/providers/azure/chat.js +1 -0
  66. package/dist/providers/azure/responses.d.ts +2 -0
  67. package/dist/providers/azure/responses.js +1 -0
  68. package/dist/providers/azure.d.ts +183 -0
  69. package/dist/providers/azure.js +89 -0
  70. package/dist/providers/cloudflare.d.ts +232 -0
  71. package/dist/providers/cloudflare.js +87 -0
  72. package/dist/providers/github-copilot.d.ts +167 -0
  73. package/dist/providers/github-copilot.js +47 -0
  74. package/dist/providers/google-vertex/anthropic.d.ts +2 -0
  75. package/dist/providers/google-vertex/anthropic.js +1 -0
  76. package/dist/providers/google-vertex-anthropic.d.ts +181 -0
  77. package/dist/providers/google-vertex-anthropic.js +46 -0
  78. package/dist/providers/google-vertex-shared.d.ts +22 -0
  79. package/dist/providers/google-vertex-shared.js +61 -0
  80. package/dist/providers/google-vertex.d.ts +93 -0
  81. package/dist/providers/google-vertex.js +49 -0
  82. package/dist/providers/google.d.ts +85 -0
  83. package/dist/providers/google.js +33 -0
  84. package/dist/providers/index.d.ts +15 -0
  85. package/dist/providers/index.js +15 -0
  86. package/dist/providers/openai/chat.d.ts +2 -0
  87. package/dist/providers/openai/chat.js +1 -0
  88. package/dist/providers/openai/responses.d.ts +2 -0
  89. package/dist/providers/openai/responses.js +1 -0
  90. package/dist/providers/openai-compatible/responses.d.ts +1 -0
  91. package/dist/providers/openai-compatible/responses.js +1 -0
  92. package/dist/providers/openai-compatible-profile.d.ts +43 -0
  93. package/dist/providers/openai-compatible-profile.js +12 -0
  94. package/dist/providers/openai-compatible-responses.d.ts +107 -0
  95. package/dist/providers/openai-compatible-responses.js +33 -0
  96. package/dist/providers/openai-compatible.d.ts +130 -0
  97. package/dist/providers/openai-compatible.js +55 -0
  98. package/dist/providers/openai-options.d.ts +30 -0
  99. package/dist/providers/openai-options.js +42 -0
  100. package/dist/providers/openai.d.ts +264 -0
  101. package/dist/providers/openai.js +62 -0
  102. package/dist/providers/openrouter.d.ts +321 -0
  103. package/dist/providers/openrouter.js +65 -0
  104. package/dist/providers/xai.d.ts +169 -0
  105. package/dist/providers/xai.js +43 -0
  106. package/dist/providers.d.ts +1 -0
  107. package/dist/providers.js +1 -0
  108. package/dist/route/auth-options.d.ts +34 -0
  109. package/dist/route/auth-options.js +14 -0
  110. package/dist/route/auth.d.ts +49 -0
  111. package/dist/route/auth.js +89 -0
  112. package/dist/route/client.d.ts +330 -0
  113. package/dist/route/client.js +211 -0
  114. package/dist/route/endpoint.d.ts +28 -0
  115. package/dist/route/endpoint.js +21 -0
  116. package/dist/route/executor.d.ts +12 -0
  117. package/dist/route/executor.js +229 -0
  118. package/dist/route/framing.d.ts +23 -0
  119. package/dist/route/framing.js +4 -0
  120. package/dist/route/index.d.ts +16 -0
  121. package/dist/route/index.js +9 -0
  122. package/dist/route/protocol.d.ts +76 -0
  123. package/dist/route/protocol.js +17 -0
  124. package/dist/route/transport/http.d.ts +33 -0
  125. package/dist/route/transport/http.js +98 -0
  126. package/dist/route/transport/index.d.ts +27 -0
  127. package/dist/route/transport/index.js +4 -0
  128. package/dist/route/transport/websocket.d.ts +56 -0
  129. package/dist/route/transport/websocket.js +181 -0
  130. package/dist/route.d.ts +1 -0
  131. package/dist/route.js +1 -0
  132. package/dist/schema/errors.d.ts +153 -0
  133. package/dist/schema/errors.js +150 -0
  134. package/dist/schema/events.d.ts +4292 -0
  135. package/dist/schema/events.js +482 -0
  136. package/dist/schema/ids.d.ts +30 -0
  137. package/dist/schema/ids.js +18 -0
  138. package/dist/schema/index.d.ts +5 -0
  139. package/dist/schema/index.js +5 -0
  140. package/dist/schema/messages.d.ts +385 -0
  141. package/dist/schema/messages.js +246 -0
  142. package/dist/schema/options.d.ts +143 -0
  143. package/dist/schema/options.js +195 -0
  144. package/dist/tool-runtime.d.ts +15 -0
  145. package/dist/tool-runtime.js +35 -0
  146. package/dist/tool.d.ts +134 -0
  147. package/dist/tool.js +66 -0
  148. package/dist/utils/record.d.ts +2 -0
  149. package/dist/utils/record.js +2 -0
  150. package/package.json +36 -9
  151. package/index.js +0 -1
@@ -0,0 +1,828 @@
1
+ import { Effect, Schema } from "effect";
2
+ import { Route } from "../route/client";
3
+ import { Auth } from "../route/auth";
4
+ import { Endpoint } from "../route/endpoint";
5
+ import { HttpTransport, WebSocketTransport } from "../route/transport";
6
+ import { Protocol } from "../route/protocol";
7
+ import { LLMError, LLMEvent, Usage, } from "../schema";
8
+ import { JsonObject, optionalArray, optionalNull, ProviderShared } from "./shared";
9
+ import { classifyProviderFailure } from "../provider-error";
10
+ import { OpenAIOptions } from "./utils/openai-options";
11
+ import { Lifecycle } from "./utils/lifecycle";
12
+ import { ToolSchemaProjection } from "./utils/tool-schema";
13
+ import { ToolStream } from "./utils/tool-stream";
14
+ const ADAPTER = "openai-responses";
15
+ export const DEFAULT_BASE_URL = "https://api.openai.com/v1";
16
+ export const PATH = "/responses";
17
+ // =============================================================================
18
+ // Request Body Schema
19
+ // =============================================================================
20
+ const OpenAIResponsesInputText = Schema.Struct({
21
+ type: Schema.tag("input_text"),
22
+ text: Schema.String,
23
+ });
24
+ const OpenAIResponsesInputImage = Schema.Struct({
25
+ type: Schema.tag("input_image"),
26
+ image_url: Schema.String,
27
+ });
28
+ const OpenAIResponsesInputContent = Schema.Union([OpenAIResponsesInputText, OpenAIResponsesInputImage]);
29
+ const OpenAIResponsesOutputText = Schema.Struct({
30
+ type: Schema.tag("output_text"),
31
+ text: Schema.String,
32
+ });
33
+ const OpenAIResponsesReasoningSummaryText = Schema.Struct({
34
+ type: Schema.tag("summary_text"),
35
+ text: Schema.String,
36
+ });
37
+ const OpenAIResponsesReasoningItem = Schema.Struct({
38
+ type: Schema.tag("reasoning"),
39
+ id: Schema.optionalKey(Schema.String),
40
+ summary: Schema.Array(OpenAIResponsesReasoningSummaryText),
41
+ encrypted_content: optionalNull(Schema.String),
42
+ });
43
+ const OpenAIResponsesItemReference = Schema.Struct({
44
+ type: Schema.tag("item_reference"),
45
+ id: Schema.String,
46
+ });
47
+ // `function_call_output.output` accepts either a plain string or an ordered
48
+ // array of content items so tools can return images in addition to text.
49
+ // https://platform.openai.com/docs/api-reference/responses/object
50
+ const OpenAIResponsesFunctionCallOutputContent = Schema.Union([OpenAIResponsesInputText, OpenAIResponsesInputImage]);
51
+ const OpenAIResponsesFunctionCallOutput = Schema.Union([
52
+ Schema.String,
53
+ Schema.Array(OpenAIResponsesFunctionCallOutputContent),
54
+ ]);
55
+ const OpenAIResponsesInputItem = Schema.Union([
56
+ Schema.Struct({ role: Schema.tag("system"), content: Schema.String }),
57
+ Schema.Struct({ role: Schema.tag("user"), content: Schema.Array(OpenAIResponsesInputContent) }),
58
+ Schema.Struct({ role: Schema.tag("assistant"), content: Schema.Array(OpenAIResponsesOutputText) }),
59
+ OpenAIResponsesReasoningItem,
60
+ OpenAIResponsesItemReference,
61
+ Schema.Struct({
62
+ type: Schema.tag("function_call"),
63
+ call_id: Schema.String,
64
+ name: Schema.String,
65
+ arguments: Schema.String,
66
+ }),
67
+ Schema.Struct({
68
+ type: Schema.tag("function_call_output"),
69
+ call_id: Schema.String,
70
+ output: OpenAIResponsesFunctionCallOutput,
71
+ }),
72
+ ]);
73
+ const OpenAIResponsesTool = Schema.Struct({
74
+ type: Schema.tag("function"),
75
+ name: Schema.String,
76
+ description: Schema.String,
77
+ parameters: JsonObject,
78
+ strict: Schema.optional(Schema.Boolean),
79
+ });
80
+ const OpenAIResponsesToolChoice = Schema.Union([
81
+ Schema.Literals(["auto", "none", "required"]),
82
+ Schema.Struct({ type: Schema.tag("function"), name: Schema.String }),
83
+ ]);
84
+ // Fields shared between the HTTP body and the WebSocket `response.create`
85
+ // message. The HTTP body adds `stream: true`; the WebSocket message adds
86
+ // `type: "response.create"`. Defining the shared shape once keeps the two
87
+ // transports in sync without a destructure-and-strip dance.
88
+ const OpenAIResponsesCoreFields = {
89
+ model: Schema.String,
90
+ input: Schema.Array(OpenAIResponsesInputItem),
91
+ instructions: Schema.optional(Schema.String),
92
+ tools: optionalArray(OpenAIResponsesTool),
93
+ tool_choice: Schema.optional(OpenAIResponsesToolChoice),
94
+ store: Schema.optional(Schema.Boolean),
95
+ service_tier: Schema.optional(OpenAIOptions.OpenAIServiceTier),
96
+ prompt_cache_key: Schema.optional(Schema.String),
97
+ include: optionalArray(OpenAIOptions.OpenAIResponseIncludable),
98
+ reasoning: Schema.optional(Schema.Struct({
99
+ effort: Schema.optional(OpenAIOptions.OpenAIReasoningEffort),
100
+ summary: Schema.optional(Schema.Literal("auto")),
101
+ })),
102
+ text: Schema.optional(Schema.Struct({
103
+ verbosity: Schema.optional(OpenAIOptions.OpenAITextVerbosity),
104
+ })),
105
+ max_output_tokens: Schema.optional(Schema.Number),
106
+ temperature: Schema.optional(Schema.Number),
107
+ top_p: Schema.optional(Schema.Number),
108
+ };
109
+ const OpenAIResponsesBody = Schema.Struct({
110
+ ...OpenAIResponsesCoreFields,
111
+ stream: Schema.Literal(true),
112
+ });
113
+ const OpenAIResponsesWebSocketMessage = Schema.StructWithRest(Schema.Struct({
114
+ type: Schema.tag("response.create"),
115
+ ...OpenAIResponsesCoreFields,
116
+ }), [Schema.Record(Schema.String, Schema.Unknown)]);
117
+ const encodeWebSocketMessage = Schema.encodeSync(Schema.fromJsonString(OpenAIResponsesWebSocketMessage));
118
+ const OpenAIResponsesUsage = Schema.Struct({
119
+ input_tokens: Schema.optional(Schema.Number),
120
+ input_tokens_details: optionalNull(Schema.Struct({ cached_tokens: Schema.optional(Schema.Number) })),
121
+ output_tokens: Schema.optional(Schema.Number),
122
+ output_tokens_details: optionalNull(Schema.Struct({ reasoning_tokens: Schema.optional(Schema.Number) })),
123
+ total_tokens: Schema.optional(Schema.Number),
124
+ });
125
+ const OpenAIResponsesStreamItem = Schema.Struct({
126
+ type: Schema.String,
127
+ id: Schema.optional(Schema.String),
128
+ call_id: Schema.optional(Schema.String),
129
+ name: Schema.optional(Schema.String),
130
+ arguments: Schema.optional(Schema.String),
131
+ // Hosted (provider-executed) tool fields. Each hosted tool item carries its
132
+ // own subset of these — we capture them generically so we can surface the
133
+ // call's typed input portion and round-trip the full result payload without
134
+ // hand-rolling a per-tool schema.
135
+ status: Schema.optional(Schema.String),
136
+ action: Schema.optional(Schema.Unknown),
137
+ queries: Schema.optional(Schema.Unknown),
138
+ results: Schema.optional(Schema.Unknown),
139
+ code: Schema.optional(Schema.String),
140
+ container_id: Schema.optional(Schema.String),
141
+ outputs: Schema.optional(Schema.Unknown),
142
+ server_label: Schema.optional(Schema.String),
143
+ output: Schema.optional(Schema.Unknown),
144
+ error: Schema.optional(Schema.Unknown),
145
+ encrypted_content: optionalNull(Schema.String),
146
+ });
147
+ // The Responses schema puts streaming error details at the top level and
148
+ // response failures under `response.error`. The official SDK also recognizes
149
+ // an event-level HTTP-style `error` envelope, so accept all three shapes here.
150
+ // https://github.com/openai/openai-openapi/blob/5162af98d3147432c14680df789e8e12d4891e6b/openapi.yaml#L67234-L67382
151
+ // https://github.com/openai/openai-node/blob/61539248cbe04665de68a71e6fd878127ae4db87/src/core/streaming.ts#L58-L85
152
+ const OpenAIResponsesErrorPayload = Schema.Struct({
153
+ code: optionalNull(Schema.String),
154
+ message: optionalNull(Schema.String),
155
+ param: optionalNull(Schema.String),
156
+ });
157
+ const OpenAIResponsesEvent = Schema.Struct({
158
+ type: Schema.String,
159
+ delta: Schema.optional(Schema.String),
160
+ item_id: Schema.optional(Schema.String),
161
+ summary_index: Schema.optional(Schema.Number),
162
+ item: Schema.optional(OpenAIResponsesStreamItem),
163
+ response: Schema.optional(Schema.StructWithRest(Schema.Struct({
164
+ id: Schema.optional(Schema.String),
165
+ service_tier: optionalNull(Schema.String),
166
+ incomplete_details: optionalNull(Schema.Struct({ reason: Schema.String })),
167
+ usage: optionalNull(OpenAIResponsesUsage),
168
+ error: optionalNull(OpenAIResponsesErrorPayload),
169
+ }), [Schema.Record(Schema.String, Schema.Unknown)])),
170
+ code: optionalNull(Schema.String),
171
+ message: Schema.optional(Schema.String),
172
+ param: optionalNull(Schema.String),
173
+ error: optionalNull(OpenAIResponsesErrorPayload),
174
+ });
175
+ const invalid = ProviderShared.invalidRequest;
176
+ // =============================================================================
177
+ // Request Lowering
178
+ // =============================================================================
179
+ const lowerTool = (tool, inputSchema) => ({
180
+ type: "function",
181
+ name: tool.name,
182
+ description: tool.description,
183
+ parameters: ToolSchemaProjection.openAI(inputSchema),
184
+ // TODO: Read this from OpenAI-specific tool options so direct LLM callers can opt into strict schemas.
185
+ strict: false,
186
+ });
187
+ const lowerToolChoice = (toolChoice) => ProviderShared.matchToolChoice("OpenAI Responses", toolChoice, {
188
+ auto: () => "auto",
189
+ none: () => "none",
190
+ required: () => "required",
191
+ tool: (name) => ({ type: "function", name }),
192
+ });
193
+ const lowerToolCall = (part) => ({
194
+ type: "function_call",
195
+ call_id: part.id,
196
+ name: part.name,
197
+ arguments: ProviderShared.encodeJson(part.input),
198
+ });
199
+ const lowerReasoning = (part) => {
200
+ const openai = part.providerMetadata?.openai;
201
+ if (!ProviderShared.isRecord(openai) || typeof openai.itemId !== "string" || openai.itemId.length === 0)
202
+ return undefined;
203
+ const encryptedContent = typeof openai.reasoningEncryptedContent === "string"
204
+ ? openai.reasoningEncryptedContent
205
+ : openai.reasoningEncryptedContent === null
206
+ ? null
207
+ : undefined;
208
+ return {
209
+ type: "reasoning",
210
+ id: openai.itemId,
211
+ summary: part.text.length > 0 ? [{ type: "summary_text", text: part.text }] : [],
212
+ encrypted_content: encryptedContent,
213
+ };
214
+ };
215
+ const hostedToolItemID = (part) => {
216
+ const openai = part.providerMetadata?.openai;
217
+ return ProviderShared.isRecord(openai) && typeof openai.itemId === "string" && openai.itemId.length > 0
218
+ ? openai.itemId
219
+ : undefined;
220
+ };
221
+ const lowerUserContent = Effect.fn("OpenAIResponses.lowerUserContent")(function* (part) {
222
+ if (part.type === "text")
223
+ return { type: "input_text", text: part.text };
224
+ if (part.type === "media") {
225
+ const media = yield* ProviderShared.validateMedia("OpenAI Responses", part, new Set(ProviderShared.IMAGE_MIMES));
226
+ return { type: "input_image", image_url: media.dataUrl };
227
+ }
228
+ return yield* ProviderShared.unsupportedContent("OpenAI Responses", "user", ["text", "media"]);
229
+ });
230
+ // Tool results may carry structured text/images. Keep media as provider-native
231
+ // content instead of JSON-stringifying base64 into a prompt string.
232
+ const lowerToolResultContentItem = Effect.fn("OpenAIResponses.lowerToolResultContentItem")(function* (item) {
233
+ if (item.type === "text")
234
+ return { type: "input_text", text: item.text };
235
+ const media = yield* ProviderShared.validateToolFile("OpenAI Responses", item, new Set(ProviderShared.IMAGE_MIMES));
236
+ return { type: "input_image", image_url: media.dataUrl };
237
+ });
238
+ const lowerToolResultOutput = Effect.fn("OpenAIResponses.lowerToolResultOutput")(function* (part) {
239
+ // Text/json/error results are encoded as a plain string for backward
240
+ // compatibility with existing cassettes and provider expectations.
241
+ if (part.result.type !== "content")
242
+ return ProviderShared.toolResultText(part);
243
+ // Preserve the narrowed array element type when compiled through a consumer package.
244
+ const content = part.result.value;
245
+ return yield* Effect.forEach(content, lowerToolResultContentItem);
246
+ });
247
+ const lowerMessages = Effect.fn("OpenAIResponses.lowerMessages")(function* (request) {
248
+ const system = request.system.length === 0 ? [] : [{ role: "system", content: ProviderShared.joinText(request.system) }];
249
+ const input = [...system];
250
+ const store = OpenAIOptions.store(request);
251
+ for (const message of request.messages) {
252
+ if (message.role === "system") {
253
+ const part = yield* ProviderShared.wrappedSystemUpdate("OpenAI Responses", message);
254
+ const previous = input.at(-1);
255
+ if (previous && "role" in previous && previous.role === "user")
256
+ input[input.length - 1] = {
257
+ role: "user",
258
+ content: [...previous.content, { type: "input_text", text: part.text }],
259
+ };
260
+ else
261
+ input.push({ role: "user", content: [{ type: "input_text", text: part.text }] });
262
+ continue;
263
+ }
264
+ if (message.role === "user") {
265
+ input.push({ role: "user", content: yield* Effect.forEach(message.content, lowerUserContent) });
266
+ continue;
267
+ }
268
+ if (message.role === "assistant") {
269
+ const content = [];
270
+ const reasoningItems = {};
271
+ const reasoningReferences = new Set();
272
+ const hostedToolReferences = new Set();
273
+ const flushText = () => {
274
+ if (content.length === 0)
275
+ return;
276
+ input.push({ role: "assistant", content: content.map((part) => ({ type: "output_text", text: part.text })) });
277
+ content.splice(0, content.length);
278
+ };
279
+ for (const part of message.content) {
280
+ if (part.type === "text") {
281
+ content.push(part);
282
+ continue;
283
+ }
284
+ if (part.type === "reasoning") {
285
+ flushText();
286
+ const reasoning = lowerReasoning(part);
287
+ if (!reasoning)
288
+ continue;
289
+ if (store !== false) {
290
+ if (!reasoningReferences.has(reasoning.id))
291
+ input.push({ type: "item_reference", id: reasoning.id });
292
+ reasoningReferences.add(reasoning.id);
293
+ continue;
294
+ }
295
+ const existing = reasoningItems[reasoning.id];
296
+ if (existing) {
297
+ existing.summary.push(...reasoning.summary);
298
+ if (typeof reasoning.encrypted_content === "string")
299
+ existing.encrypted_content = reasoning.encrypted_content;
300
+ continue;
301
+ }
302
+ const replay = {
303
+ type: reasoning.type,
304
+ summary: reasoning.summary,
305
+ encrypted_content: reasoning.encrypted_content,
306
+ };
307
+ reasoningItems[reasoning.id] = replay;
308
+ input.push(replay);
309
+ continue;
310
+ }
311
+ if (part.type === "tool-call") {
312
+ flushText();
313
+ if (part.providerExecuted === true)
314
+ continue;
315
+ input.push(lowerToolCall(part));
316
+ continue;
317
+ }
318
+ if (part.type === "tool-result" && part.providerExecuted === true) {
319
+ flushText();
320
+ const itemID = hostedToolItemID(part);
321
+ if (store !== false && itemID && !hostedToolReferences.has(itemID))
322
+ input.push({ type: "item_reference", id: itemID });
323
+ if (itemID)
324
+ hostedToolReferences.add(itemID);
325
+ continue;
326
+ }
327
+ return yield* ProviderShared.unsupportedContent("OpenAI Responses", "assistant", [
328
+ "text",
329
+ "reasoning",
330
+ "tool-call",
331
+ "tool-result",
332
+ ]);
333
+ }
334
+ flushText();
335
+ continue;
336
+ }
337
+ for (const part of message.content) {
338
+ if (!ProviderShared.supportsContent(part, ["tool-result"]))
339
+ return yield* ProviderShared.unsupportedContent("OpenAI Responses", "tool", ["tool-result"]);
340
+ input.push({
341
+ type: "function_call_output",
342
+ call_id: part.id,
343
+ output: yield* lowerToolResultOutput(part),
344
+ });
345
+ }
346
+ }
347
+ // With store:false, OpenAI only accepts previous reasoning items when the
348
+ // complete item has encrypted state. Summary blocks for one item may carry
349
+ // that state only on the last block, so filter after they have been joined.
350
+ return store === false
351
+ ? input.filter((item) => !("type" in item) || item.type !== "reasoning" || typeof item.encrypted_content === "string")
352
+ : input;
353
+ });
354
+ const lowerOptions = Effect.fn("OpenAIResponses.lowerOptions")(function* (request) {
355
+ const store = OpenAIOptions.store(request);
356
+ const promptCacheKey = OpenAIOptions.promptCacheKey(request);
357
+ const effort = OpenAIOptions.reasoningEffort(request);
358
+ const summary = OpenAIOptions.reasoningSummary(request);
359
+ const include = OpenAIOptions.include(request);
360
+ const verbosity = OpenAIOptions.textVerbosity(request);
361
+ const instructions = OpenAIOptions.instructions(request);
362
+ const serviceTier = OpenAIOptions.serviceTier(request);
363
+ return {
364
+ ...(instructions ? { instructions } : {}),
365
+ ...(store !== undefined ? { store } : {}),
366
+ ...(promptCacheKey ? { prompt_cache_key: promptCacheKey } : {}),
367
+ ...(include ? { include } : {}),
368
+ ...(effort || summary ? { reasoning: { effort, summary } } : {}),
369
+ ...(verbosity ? { text: { verbosity } } : {}),
370
+ ...(serviceTier ? { service_tier: serviceTier } : {}),
371
+ };
372
+ });
373
+ const fromRequest = Effect.fn("OpenAIResponses.fromRequest")(function* (request) {
374
+ const generation = request.generation;
375
+ const options = yield* lowerOptions(request);
376
+ const toolSchemaCompatibility = request.model.compatibility?.toolSchema;
377
+ return {
378
+ model: request.model.id,
379
+ input: yield* lowerMessages(request),
380
+ tools: request.tools.length === 0
381
+ ? undefined
382
+ : request.tools.map((tool) => lowerTool(tool, ToolSchemaProjection.modelCompatibility(tool.inputSchema, toolSchemaCompatibility))),
383
+ tool_choice: request.toolChoice ? yield* lowerToolChoice(request.toolChoice) : undefined,
384
+ stream: true,
385
+ max_output_tokens: generation?.maxTokens,
386
+ temperature: generation?.temperature,
387
+ top_p: generation?.topP,
388
+ ...options,
389
+ };
390
+ });
391
+ // =============================================================================
392
+ // Stream Parsing
393
+ // =============================================================================
394
+ // OpenAI Responses reports `input_tokens` (inclusive total) with a
395
+ // `cached_tokens` subset, and `output_tokens` (inclusive total) with a
396
+ // `reasoning_tokens` subset. Pass the totals through and derive the
397
+ // non-cached breakdown.
398
+ const mapUsage = (usage) => {
399
+ if (!usage)
400
+ return undefined;
401
+ const cached = usage.input_tokens_details?.cached_tokens;
402
+ const reasoning = usage.output_tokens_details?.reasoning_tokens;
403
+ const nonCached = ProviderShared.subtractTokens(usage.input_tokens, cached);
404
+ return new Usage({
405
+ inputTokens: usage.input_tokens,
406
+ outputTokens: usage.output_tokens,
407
+ nonCachedInputTokens: nonCached,
408
+ cacheReadInputTokens: cached,
409
+ reasoningTokens: reasoning,
410
+ totalTokens: ProviderShared.totalTokens(usage.input_tokens, usage.output_tokens, usage.total_tokens),
411
+ providerMetadata: { openai: usage },
412
+ });
413
+ };
414
+ const mapFinishReason = (event, hasFunctionCall) => {
415
+ const reason = event.response?.incomplete_details?.reason;
416
+ if (reason === undefined || reason === null)
417
+ return hasFunctionCall ? "tool-calls" : "stop";
418
+ if (reason === "max_output_tokens")
419
+ return "length";
420
+ if (reason === "content_filter")
421
+ return "content-filter";
422
+ return hasFunctionCall ? "tool-calls" : "unknown";
423
+ };
424
+ const openaiMetadata = (metadata) => ({ openai: metadata });
425
+ // Hosted tool items (provider-executed) ship their typed input + status +
426
+ // result fields all in one item. We expose them as a `tool-call` +
427
+ // `tool-result` pair so consumers can treat them uniformly with client tools,
428
+ // only differentiated by `providerExecuted: true`.
429
+ //
430
+ // One record per OpenAI Responses item type that represents a hosted
431
+ // (provider-executed) tool call: the common name we surface, plus an `input`
432
+ // extractor that picks the fields the model actually populated for that tool.
433
+ // Falling back to `{}` when an entry isn't fully typed keeps unknown tools
434
+ // observable without rolling a per-tool schema.
435
+ const HOSTED_TOOLS = {
436
+ web_search_call: { name: "web_search", input: (item) => item.action ?? {} },
437
+ web_search_preview_call: { name: "web_search_preview", input: (item) => item.action ?? {} },
438
+ file_search_call: { name: "file_search", input: (item) => ({ queries: item.queries ?? [] }) },
439
+ code_interpreter_call: {
440
+ name: "code_interpreter",
441
+ input: (item) => ({ code: item.code, container_id: item.container_id }),
442
+ },
443
+ computer_use_call: { name: "computer_use", input: (item) => item.action ?? {} },
444
+ image_generation_call: { name: "image_generation", input: () => ({}) },
445
+ mcp_call: {
446
+ name: "mcp",
447
+ input: (item) => ({ server_label: item.server_label, name: item.name, arguments: item.arguments }),
448
+ },
449
+ local_shell_call: { name: "local_shell", input: (item) => item.action ?? {} },
450
+ };
451
+ const isHostedToolItem = (item) => item.type in HOSTED_TOOLS && typeof item.id === "string" && item.id.length > 0;
452
+ const isReasoningItem = (item) => item.type === "reasoning" && typeof item.id === "string" && item.id.length > 0;
453
+ // Round-trip the full item as the structured result so consumers can extract
454
+ // outputs / sources / status without re-decoding.
455
+ const hostedToolResult = (item) => {
456
+ const isError = typeof item.error !== "undefined" && item.error !== null;
457
+ return isError ? { type: "error", value: item.error } : { type: "json", value: item };
458
+ };
459
+ const hostedToolEvents = (item) => {
460
+ const tool = HOSTED_TOOLS[item.type];
461
+ const providerMetadata = openaiMetadata({ itemId: item.id });
462
+ return [
463
+ LLMEvent.toolCall({
464
+ id: item.id,
465
+ name: tool.name,
466
+ input: tool.input(item),
467
+ providerExecuted: true,
468
+ providerMetadata,
469
+ }),
470
+ LLMEvent.toolResult({
471
+ id: item.id,
472
+ name: tool.name,
473
+ result: hostedToolResult(item),
474
+ providerExecuted: true,
475
+ providerMetadata,
476
+ }),
477
+ ];
478
+ };
479
+ const NO_EVENTS = [];
480
+ // `response.completed` / `response.incomplete` are clean finishes that emit a
481
+ // `finish` event; `response.failed` is a hard failure. All three end the stream,
482
+ // so keep this set aligned with `step` and the protocol's terminal predicate.
483
+ const TERMINAL_TYPES = new Set(["response.completed", "response.incomplete", "response.failed"]);
484
+ const onOutputTextDelta = (state, event) => {
485
+ if (!event.delta)
486
+ return [state, NO_EVENTS];
487
+ const events = [];
488
+ return [
489
+ { ...state, lifecycle: Lifecycle.textDelta(state.lifecycle, events, event.item_id ?? "text-0", event.delta) },
490
+ events,
491
+ ];
492
+ };
493
+ const onOutputTextDone = (state, event) => {
494
+ const events = [];
495
+ return [{ ...state, lifecycle: Lifecycle.textEnd(state.lifecycle, events, event.item_id ?? "text-0") }, events];
496
+ };
497
+ const onReasoningDelta = (state, event) => {
498
+ if (!event.delta)
499
+ return [state, NO_EVENTS];
500
+ const events = [];
501
+ const itemID = event.item_id ?? "reasoning-0";
502
+ const id = event.summary_index !== undefined || state.reasoningItems[itemID] ? `${itemID}:${event.summary_index ?? 0}` : itemID;
503
+ return [
504
+ {
505
+ ...state,
506
+ lifecycle: Lifecycle.reasoningDelta(state.lifecycle, events, id, event.delta),
507
+ },
508
+ events,
509
+ ];
510
+ };
511
+ const onReasoningDone = (state, _event) => [state, NO_EVENTS];
512
+ const reasoningMetadata = (item) => openaiMetadata({ itemId: item.id, reasoningEncryptedContent: item.encrypted_content ?? null });
513
+ // OpenAI Responses streams reasoning items in a stable order:
514
+ // `output_item.added` (reasoning) →
515
+ // `reasoning_summary_part.added` (index=0) →
516
+ // `reasoning_summary_text.delta` →
517
+ // `reasoning_summary_part.done` (index=0) →
518
+ // (repeat for index>0) →
519
+ // `output_item.done` (reasoning).
520
+ // The handlers below rely on this ordering: `onOutputItemAdded` seeds the
521
+ // per-item entry, `onReasoningSummaryPartAdded` for `summary_index === 0`
522
+ // short-circuits when the entry already exists, and higher-index handlers
523
+ // fold against the same entry. Behaviour for out-of-order events is
524
+ // best-effort, not guaranteed.
525
+ const onOutputItemAdded = (state, event) => {
526
+ const item = event.item;
527
+ if (item && isReasoningItem(item)) {
528
+ const events = [];
529
+ return [
530
+ {
531
+ ...state,
532
+ lifecycle: Lifecycle.reasoningStart(state.lifecycle, events, `${item.id}:0`, reasoningMetadata(item)),
533
+ reasoningItems: {
534
+ ...state.reasoningItems,
535
+ [item.id]: { encryptedContent: item.encrypted_content, summaryParts: { 0: "active" } },
536
+ },
537
+ },
538
+ events,
539
+ ];
540
+ }
541
+ if (item?.type !== "function_call" || !item.id)
542
+ return [state, NO_EVENTS];
543
+ const providerMetadata = openaiMetadata({ itemId: item.id });
544
+ const events = [];
545
+ const lifecycle = Lifecycle.stepStart(state.lifecycle, events);
546
+ return [
547
+ {
548
+ ...state,
549
+ lifecycle,
550
+ hasFunctionCall: state.hasFunctionCall,
551
+ tools: ToolStream.start(state.tools, item.id, {
552
+ id: item.call_id ?? item.id,
553
+ name: item.name ?? "",
554
+ input: item.arguments ?? "",
555
+ providerMetadata,
556
+ }),
557
+ },
558
+ [...events, LLMEvent.toolInputStart({ id: item.call_id ?? item.id, name: item.name ?? "", providerMetadata })],
559
+ ];
560
+ };
561
+ const onReasoningSummaryPartAdded = (state, event) => {
562
+ if (!event.item_id || event.summary_index === undefined)
563
+ return [state, NO_EVENTS];
564
+ const item = state.reasoningItems[event.item_id] ?? { encryptedContent: undefined, summaryParts: {} };
565
+ if (event.summary_index === 0) {
566
+ if (state.reasoningItems[event.item_id])
567
+ return [state, NO_EVENTS];
568
+ const events = [];
569
+ return [
570
+ {
571
+ ...state,
572
+ lifecycle: Lifecycle.reasoningStart(state.lifecycle, events, `${event.item_id}:0`, openaiMetadata({ itemId: event.item_id, reasoningEncryptedContent: null })),
573
+ reasoningItems: {
574
+ ...state.reasoningItems,
575
+ [event.item_id]: { ...item, summaryParts: { 0: "active" } },
576
+ },
577
+ },
578
+ events,
579
+ ];
580
+ }
581
+ const events = [];
582
+ const closed = Object.entries(item.summaryParts)
583
+ .filter((entry) => entry[1] === "can-conclude")
584
+ .reduce((lifecycle, entry) => Lifecycle.reasoningEnd(lifecycle, events, `${event.item_id}:${entry[0]}`, openaiMetadata({ itemId: event.item_id })), state.lifecycle);
585
+ return [
586
+ {
587
+ ...state,
588
+ lifecycle: Lifecycle.reasoningStart(closed, events, `${event.item_id}:${event.summary_index}`, openaiMetadata({ itemId: event.item_id, reasoningEncryptedContent: item.encryptedContent ?? null })),
589
+ reasoningItems: {
590
+ ...state.reasoningItems,
591
+ [event.item_id]: {
592
+ ...item,
593
+ summaryParts: {
594
+ ...Object.fromEntries(Object.entries(item.summaryParts).map((entry) => entry[1] === "can-conclude" ? [entry[0], "concluded"] : entry)),
595
+ [event.summary_index]: "active",
596
+ },
597
+ },
598
+ },
599
+ },
600
+ events,
601
+ ];
602
+ };
603
+ const onReasoningSummaryPartDone = (state, event) => {
604
+ if (!event.item_id || event.summary_index === undefined)
605
+ return [state, NO_EVENTS];
606
+ const item = state.reasoningItems[event.item_id];
607
+ if (!item)
608
+ return [state, NO_EVENTS];
609
+ const events = [];
610
+ return [
611
+ {
612
+ ...state,
613
+ lifecycle: state.store !== false
614
+ ? Lifecycle.reasoningEnd(state.lifecycle, events, `${event.item_id}:${event.summary_index}`, openaiMetadata({ itemId: event.item_id }))
615
+ : state.lifecycle,
616
+ reasoningItems: {
617
+ ...state.reasoningItems,
618
+ [event.item_id]: {
619
+ ...item,
620
+ summaryParts: {
621
+ ...item.summaryParts,
622
+ [event.summary_index]: state.store !== false ? "concluded" : "can-conclude",
623
+ },
624
+ },
625
+ },
626
+ },
627
+ events,
628
+ ];
629
+ };
630
+ const onFunctionCallArgumentsDelta = Effect.fn("OpenAIResponses.onFunctionCallArgumentsDelta")(function* (state, event) {
631
+ if (!event.item_id || !event.delta)
632
+ return [state, NO_EVENTS];
633
+ const result = ToolStream.appendExisting(ADAPTER, state.tools, event.item_id, event.delta, "OpenAI Responses tool argument delta is missing its tool call");
634
+ if (ToolStream.isError(result))
635
+ return yield* result;
636
+ const events = [];
637
+ const lifecycle = result.events.length ? Lifecycle.stepStart(state.lifecycle, events) : state.lifecycle;
638
+ events.push(...result.events);
639
+ return [{ ...state, lifecycle, tools: result.tools }, events];
640
+ });
641
+ const onOutputItemDone = Effect.fn("OpenAIResponses.onOutputItemDone")(function* (state, event) {
642
+ const item = event.item;
643
+ if (!item)
644
+ return [state, NO_EVENTS];
645
+ if (item.type === "message" && item.id)
646
+ return onOutputTextDone(state, { ...event, item_id: item.id });
647
+ if (item.type === "function_call") {
648
+ if (!item.id || !item.call_id || !item.name)
649
+ return [state, NO_EVENTS];
650
+ const tools = state.tools[item.id]
651
+ ? state.tools
652
+ : ToolStream.start(state.tools, item.id, { id: item.call_id, name: item.name });
653
+ const result = item.arguments === undefined
654
+ ? yield* ToolStream.finish(ADAPTER, tools, item.id)
655
+ : yield* ToolStream.finishWithInput(ADAPTER, tools, item.id, item.arguments);
656
+ const events = [];
657
+ const resultEvents = result.events ?? [];
658
+ const lifecycle = resultEvents.length ? Lifecycle.stepStart(state.lifecycle, events) : state.lifecycle;
659
+ events.push(...resultEvents);
660
+ return [
661
+ {
662
+ ...state,
663
+ lifecycle,
664
+ hasFunctionCall: resultEvents.some(LLMEvent.is.toolCall) ? true : state.hasFunctionCall,
665
+ tools: result.tools,
666
+ },
667
+ events,
668
+ ];
669
+ }
670
+ if (isHostedToolItem(item)) {
671
+ const events = [];
672
+ const lifecycle = Lifecycle.stepStart(state.lifecycle, events);
673
+ events.push(...hostedToolEvents(item));
674
+ return [{ ...state, lifecycle }, events];
675
+ }
676
+ if (isReasoningItem(item)) {
677
+ const events = [];
678
+ const providerMetadata = reasoningMetadata(item);
679
+ const reasoningItem = state.reasoningItems[item.id];
680
+ if (reasoningItem) {
681
+ const lifecycle = Object.entries(reasoningItem.summaryParts)
682
+ .filter((entry) => entry[1] === "active" || entry[1] === "can-conclude")
683
+ .reduce((lifecycle, entry) => Lifecycle.reasoningEnd(lifecycle, events, `${item.id}:${entry[0]}`, providerMetadata), state.lifecycle);
684
+ const { [item.id]: _removed, ...reasoningItems } = state.reasoningItems;
685
+ return [{ ...state, lifecycle, reasoningItems }, events];
686
+ }
687
+ if (!state.lifecycle.reasoning.has(item.id)) {
688
+ const lifecycle = Lifecycle.stepStart(state.lifecycle, events);
689
+ events.push(LLMEvent.reasoningStart({ id: item.id, providerMetadata }));
690
+ events.push(LLMEvent.reasoningEnd({ id: item.id, providerMetadata }));
691
+ return [{ ...state, lifecycle }, events];
692
+ }
693
+ return [
694
+ { ...state, lifecycle: Lifecycle.reasoningEnd(state.lifecycle, events, item.id, providerMetadata) },
695
+ events,
696
+ ];
697
+ }
698
+ return [state, NO_EVENTS];
699
+ });
700
+ const onResponseFinish = (state, event) => {
701
+ const events = [];
702
+ const lifecycle = Lifecycle.finish(state.lifecycle, events, {
703
+ reason: mapFinishReason(event, state.hasFunctionCall),
704
+ usage: mapUsage(event.response?.usage),
705
+ providerMetadata: event.response?.id || event.response?.service_tier
706
+ ? openaiMetadata({
707
+ responseId: event.response.id,
708
+ serviceTier: event.response.service_tier,
709
+ })
710
+ : undefined,
711
+ });
712
+ return [{ ...state, lifecycle }, events];
713
+ };
714
+ // Build a single human-readable message from whatever the provider supplied.
715
+ // When both code and message are present, prefix the code so consumers see
716
+ // the failure mode (e.g. `rate_limit_exceeded: Slow down`) instead of just
717
+ // the bare message — production rate limits and context-length failures used
718
+ // to be indistinguishable from generic stream drops.
719
+ const providerErrorMessage = (event, fallback) => {
720
+ const nested = event.error ?? event.response?.error ?? undefined;
721
+ const message = event.message || nested?.message || undefined;
722
+ const code = event.code || nested?.code || undefined;
723
+ if (message && code)
724
+ return `${code}: ${message}`;
725
+ return message || code || fallback;
726
+ };
727
+ const providerError = (event, fallback) => {
728
+ const code = event.code || event.error?.code || event.response?.error?.code || undefined;
729
+ const message = providerErrorMessage(event, fallback);
730
+ return new LLMError({
731
+ module: ADAPTER,
732
+ method: "stream",
733
+ reason: classifyProviderFailure({ message, code }),
734
+ });
735
+ };
736
+ const step = (state, event) => {
737
+ if (event.type === "response.output_text.delta")
738
+ return Effect.succeed(onOutputTextDelta(state, event));
739
+ if (event.type === "response.output_text.done")
740
+ return Effect.succeed(onOutputTextDone(state, event));
741
+ if (event.type === "response.reasoning_text.delta" ||
742
+ event.type === "response.reasoning_summary.delta" ||
743
+ event.type === "response.reasoning_summary_text.delta")
744
+ return Effect.succeed(onReasoningDelta(state, event));
745
+ if (event.type === "response.reasoning_text.done" ||
746
+ event.type === "response.reasoning_summary.done" ||
747
+ event.type === "response.reasoning_summary_text.done")
748
+ return Effect.succeed(onReasoningDone(state, event));
749
+ if (event.type === "response.reasoning_summary_part.added")
750
+ return Effect.succeed(onReasoningSummaryPartAdded(state, event));
751
+ if (event.type === "response.reasoning_summary_part.done")
752
+ return Effect.succeed(onReasoningSummaryPartDone(state, event));
753
+ if (event.type === "response.output_item.added")
754
+ return Effect.succeed(onOutputItemAdded(state, event));
755
+ if (event.type === "response.function_call_arguments.delta")
756
+ return onFunctionCallArgumentsDelta(state, event);
757
+ if (event.type === "response.output_item.done")
758
+ return onOutputItemDone(state, event);
759
+ if (event.type === "response.completed" || event.type === "response.incomplete")
760
+ return Effect.succeed(onResponseFinish(state, event));
761
+ if (event.type === "response.failed")
762
+ return providerError(event, "OpenAI Responses response failed");
763
+ if (event.type === "error")
764
+ return providerError(event, "OpenAI Responses stream error");
765
+ return Effect.succeed([state, NO_EVENTS]);
766
+ };
767
+ // =============================================================================
768
+ // Protocol And OpenAI Route
769
+ // =============================================================================
770
+ /**
771
+ * The OpenAI Responses protocol — request body construction, body schema, and
772
+ * the streaming-event state machine. Used by native OpenAI and (once
773
+ * registered) Azure OpenAI Responses.
774
+ */
775
+ export const protocol = Protocol.make({
776
+ id: ADAPTER,
777
+ body: {
778
+ schema: OpenAIResponsesBody,
779
+ from: fromRequest,
780
+ },
781
+ stream: {
782
+ event: Protocol.jsonEvent(OpenAIResponsesEvent),
783
+ initial: (request) => ({
784
+ hasFunctionCall: false,
785
+ tools: ToolStream.empty(),
786
+ lifecycle: Lifecycle.initial(),
787
+ reasoningItems: {},
788
+ store: OpenAIOptions.store(request),
789
+ }),
790
+ step,
791
+ terminal: (event) => TERMINAL_TYPES.has(event.type),
792
+ },
793
+ });
794
+ const endpoint = Endpoint.path(PATH, { baseURL: DEFAULT_BASE_URL });
795
+ const auth = Auth.none;
796
+ export const httpTransport = HttpTransport.sseJson.with();
797
+ export const route = Route.make({
798
+ id: ADAPTER,
799
+ provider: "openai",
800
+ providerMetadataKey: "openai",
801
+ protocol,
802
+ endpoint,
803
+ auth,
804
+ transport: httpTransport,
805
+ defaults: { providerOptions: { openai: { store: false } } },
806
+ });
807
+ const decodeWebSocketMessage = ProviderShared.validateWith(Schema.decodeUnknownEffect(OpenAIResponsesWebSocketMessage));
808
+ const webSocketMessage = (body) => Effect.gen(function* () {
809
+ if (!ProviderShared.isRecord(body))
810
+ return yield* ProviderShared.invalidRequest("OpenAI Responses WebSocket body must be a JSON object");
811
+ const { stream: _stream, ...message } = body;
812
+ return yield* decodeWebSocketMessage({ ...message, type: "response.create" });
813
+ });
814
+ export const webSocketTransport = WebSocketTransport.jsonTransport.with({
815
+ toMessage: webSocketMessage,
816
+ encodeMessage: encodeWebSocketMessage,
817
+ });
818
+ export const webSocketRoute = Route.make({
819
+ id: `${ADAPTER}-websocket`,
820
+ provider: "openai",
821
+ providerMetadataKey: "openai",
822
+ protocol,
823
+ endpoint,
824
+ auth,
825
+ transport: webSocketTransport,
826
+ defaults: { providerOptions: { openai: { store: false } } },
827
+ });
828
+ export * as OpenAIResponses from "./openai-responses";