@opencode-ai/ai 0.0.0-beta-17492

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