@opencode/ai 0.0.0-beta-19275

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 (252) hide show
  1. package/README.md +682 -0
  2. package/dist/cache-policy.d.ts +2 -0
  3. package/dist/cache-policy.js +129 -0
  4. package/dist/image-client.d.ts +19 -0
  5. package/dist/image-client.js +28 -0
  6. package/dist/image.d.ts +110 -0
  7. package/dist/image.js +103 -0
  8. package/dist/index.d.ts +18 -0
  9. package/dist/index.js +12 -0
  10. package/dist/llm.d.ts +252 -0
  11. package/dist/llm.js +74 -0
  12. package/dist/protocols/anthropic-messages.d.ts +957 -0
  13. package/dist/protocols/anthropic-messages.js +1414 -0
  14. package/dist/protocols/bedrock-converse.d.ts +499 -0
  15. package/dist/protocols/bedrock-converse.js +643 -0
  16. package/dist/protocols/bedrock-event-stream.d.ts +9 -0
  17. package/dist/protocols/bedrock-event-stream.js +97 -0
  18. package/dist/protocols/gemini.d.ts +285 -0
  19. package/dist/protocols/gemini.js +671 -0
  20. package/dist/protocols/google-images.d.ts +30 -0
  21. package/dist/protocols/google-images.js +181 -0
  22. package/dist/protocols/index.d.ts +12 -0
  23. package/dist/protocols/index.js +12 -0
  24. package/dist/protocols/meta-images.d.ts +26 -0
  25. package/dist/protocols/meta-images.js +89 -0
  26. package/dist/protocols/meta-messages.d.ts +350 -0
  27. package/dist/protocols/meta-messages.js +41 -0
  28. package/dist/protocols/meta-responses.d.ts +473 -0
  29. package/dist/protocols/meta-responses.js +173 -0
  30. package/dist/protocols/mistral-chat.d.ts +540 -0
  31. package/dist/protocols/mistral-chat.js +624 -0
  32. package/dist/protocols/open-responses-channel.d.ts +23 -0
  33. package/dist/protocols/open-responses-channel.js +141 -0
  34. package/dist/protocols/open-responses-continuation.d.ts +12 -0
  35. package/dist/protocols/open-responses-continuation.js +161 -0
  36. package/dist/protocols/open-responses.d.ts +1583 -0
  37. package/dist/protocols/open-responses.js +1206 -0
  38. package/dist/protocols/openai-chat.d.ts +860 -0
  39. package/dist/protocols/openai-chat.js +966 -0
  40. package/dist/protocols/openai-compatible-chat.d.ts +109 -0
  41. package/dist/protocols/openai-compatible-chat.js +19 -0
  42. package/dist/protocols/openai-compatible-responses.d.ts +172 -0
  43. package/dist/protocols/openai-compatible-responses.js +18 -0
  44. package/dist/protocols/openai-images.d.ts +32 -0
  45. package/dist/protocols/openai-images.js +183 -0
  46. package/dist/protocols/openai-responses.d.ts +1389 -0
  47. package/dist/protocols/openai-responses.js +260 -0
  48. package/dist/protocols/shared.d.ts +193 -0
  49. package/dist/protocols/shared.js +290 -0
  50. package/dist/protocols/utils/bedrock-auth.d.ts +60 -0
  51. package/dist/protocols/utils/bedrock-auth.js +85 -0
  52. package/dist/protocols/utils/bedrock-cache.d.ts +19 -0
  53. package/dist/protocols/utils/bedrock-cache.js +49 -0
  54. package/dist/protocols/utils/bedrock-media.d.ts +55 -0
  55. package/dist/protocols/utils/bedrock-media.js +73 -0
  56. package/dist/protocols/utils/cache.d.ts +6 -0
  57. package/dist/protocols/utils/cache.js +5 -0
  58. package/dist/protocols/utils/gemini-tool-schema.d.ts +2 -0
  59. package/dist/protocols/utils/gemini-tool-schema.js +103 -0
  60. package/dist/protocols/utils/image-input.d.ts +21 -0
  61. package/dist/protocols/utils/image-input.js +20 -0
  62. package/dist/protocols/utils/lifecycle.d.ts +24 -0
  63. package/dist/protocols/utils/lifecycle.js +72 -0
  64. package/dist/protocols/utils/meta-image.d.ts +2 -0
  65. package/dist/protocols/utils/meta-image.js +13 -0
  66. package/dist/protocols/utils/open-responses-options.d.ts +56 -0
  67. package/dist/protocols/utils/open-responses-options.js +61 -0
  68. package/dist/protocols/utils/openai-image.d.ts +5 -0
  69. package/dist/protocols/utils/openai-image.js +18 -0
  70. package/dist/protocols/utils/openai-options.d.ts +16 -0
  71. package/dist/protocols/utils/openai-options.js +14 -0
  72. package/dist/protocols/utils/partial-json-options.d.ts +62 -0
  73. package/dist/protocols/utils/partial-json-options.js +52 -0
  74. package/dist/protocols/utils/partial-json.d.ts +8 -0
  75. package/dist/protocols/utils/partial-json.js +279 -0
  76. package/dist/protocols/utils/responses-checkpoint.d.ts +4 -0
  77. package/dist/protocols/utils/responses-checkpoint.js +107 -0
  78. package/dist/protocols/utils/responses-compaction.d.ts +4 -0
  79. package/dist/protocols/utils/responses-compaction.js +129 -0
  80. package/dist/protocols/utils/responses-hosted-tools.d.ts +26 -0
  81. package/dist/protocols/utils/responses-hosted-tools.js +32 -0
  82. package/dist/protocols/utils/tool-schema.d.ts +8 -0
  83. package/dist/protocols/utils/tool-schema.js +54 -0
  84. package/dist/protocols/utils/tool-stream.d.ts +710 -0
  85. package/dist/protocols/utils/tool-stream.js +155 -0
  86. package/dist/protocols/xai-images.d.ts +26 -0
  87. package/dist/protocols/xai-images.js +106 -0
  88. package/dist/protocols/xai-responses.d.ts +250 -0
  89. package/dist/protocols/xai-responses.js +81 -0
  90. package/dist/protocols/zai-images.d.ts +22 -0
  91. package/dist/protocols/zai-images.js +79 -0
  92. package/dist/protocols.d.ts +1 -0
  93. package/dist/protocols.js +1 -0
  94. package/dist/provider-error.d.ts +15 -0
  95. package/dist/provider-error.js +125 -0
  96. package/dist/provider-package.d.ts +11 -0
  97. package/dist/provider-package.js +1 -0
  98. package/dist/provider.d.ts +23 -0
  99. package/dist/provider.js +2 -0
  100. package/dist/providers/amazon-bedrock/mantle/chat.d.ts +2 -0
  101. package/dist/providers/amazon-bedrock/mantle/chat.js +1 -0
  102. package/dist/providers/amazon-bedrock/mantle/responses.d.ts +2 -0
  103. package/dist/providers/amazon-bedrock/mantle/responses.js +1 -0
  104. package/dist/providers/amazon-bedrock/mantle.d.ts +2 -0
  105. package/dist/providers/amazon-bedrock/mantle.js +1 -0
  106. package/dist/providers/amazon-bedrock-mantle.d.ts +371 -0
  107. package/dist/providers/amazon-bedrock-mantle.js +76 -0
  108. package/dist/providers/amazon-bedrock.d.ts +173 -0
  109. package/dist/providers/amazon-bedrock.js +41 -0
  110. package/dist/providers/anthropic-compatible.d.ts +304 -0
  111. package/dist/providers/anthropic-compatible.js +44 -0
  112. package/dist/providers/anthropic.d.ts +302 -0
  113. package/dist/providers/anthropic.js +39 -0
  114. package/dist/providers/azure/chat.d.ts +2 -0
  115. package/dist/providers/azure/chat.js +1 -0
  116. package/dist/providers/azure/responses.d.ts +2 -0
  117. package/dist/providers/azure/responses.js +1 -0
  118. package/dist/providers/azure.d.ts +599 -0
  119. package/dist/providers/azure.js +115 -0
  120. package/dist/providers/baseten.d.ts +229 -0
  121. package/dist/providers/baseten.js +38 -0
  122. package/dist/providers/cerebras.d.ts +228 -0
  123. package/dist/providers/cerebras.js +40 -0
  124. package/dist/providers/cloudflare-ai-gateway.d.ts +238 -0
  125. package/dist/providers/cloudflare-ai-gateway.js +59 -0
  126. package/dist/providers/cloudflare-workers-ai.d.ts +232 -0
  127. package/dist/providers/cloudflare-workers-ai.js +45 -0
  128. package/dist/providers/deepinfra.d.ts +228 -0
  129. package/dist/providers/deepinfra.js +43 -0
  130. package/dist/providers/deepseek.d.ts +229 -0
  131. package/dist/providers/deepseek.js +41 -0
  132. package/dist/providers/fireworks.d.ts +229 -0
  133. package/dist/providers/fireworks.js +38 -0
  134. package/dist/providers/google-vertex/chat.d.ts +2 -0
  135. package/dist/providers/google-vertex/chat.js +1 -0
  136. package/dist/providers/google-vertex/gemini.d.ts +2 -0
  137. package/dist/providers/google-vertex/gemini.js +1 -0
  138. package/dist/providers/google-vertex/messages.d.ts +2 -0
  139. package/dist/providers/google-vertex/messages.js +1 -0
  140. package/dist/providers/google-vertex/responses.d.ts +2 -0
  141. package/dist/providers/google-vertex/responses.js +1 -0
  142. package/dist/providers/google-vertex-chat.d.ts +133 -0
  143. package/dist/providers/google-vertex-chat.js +55 -0
  144. package/dist/providers/google-vertex-messages.d.ts +301 -0
  145. package/dist/providers/google-vertex-messages.js +75 -0
  146. package/dist/providers/google-vertex-responses.d.ts +198 -0
  147. package/dist/providers/google-vertex-responses.js +56 -0
  148. package/dist/providers/google-vertex-shared.d.ts +22 -0
  149. package/dist/providers/google-vertex-shared.js +61 -0
  150. package/dist/providers/google-vertex.d.ts +120 -0
  151. package/dist/providers/google-vertex.js +92 -0
  152. package/dist/providers/google.d.ts +115 -0
  153. package/dist/providers/google.js +42 -0
  154. package/dist/providers/groq.d.ts +315 -0
  155. package/dist/providers/groq.js +81 -0
  156. package/dist/providers/index.d.ts +28 -0
  157. package/dist/providers/index.js +28 -0
  158. package/dist/providers/meta/chat.d.ts +2 -0
  159. package/dist/providers/meta/chat.js +1 -0
  160. package/dist/providers/meta/messages.d.ts +2 -0
  161. package/dist/providers/meta/messages.js +1 -0
  162. package/dist/providers/meta/responses.d.ts +2 -0
  163. package/dist/providers/meta/responses.js +1 -0
  164. package/dist/providers/meta.d.ts +631 -0
  165. package/dist/providers/meta.js +113 -0
  166. package/dist/providers/minimax/chat.d.ts +4 -0
  167. package/dist/providers/minimax/chat.js +8 -0
  168. package/dist/providers/minimax/messages.d.ts +1 -0
  169. package/dist/providers/minimax/messages.js +1 -0
  170. package/dist/providers/minimax/responses.d.ts +4 -0
  171. package/dist/providers/minimax/responses.js +8 -0
  172. package/dist/providers/minimax.d.ts +593 -0
  173. package/dist/providers/minimax.js +89 -0
  174. package/dist/providers/mistral.d.ts +208 -0
  175. package/dist/providers/mistral.js +28 -0
  176. package/dist/providers/open-responses-options.d.ts +6 -0
  177. package/dist/providers/open-responses-options.js +1 -0
  178. package/dist/providers/openai/chat.d.ts +2 -0
  179. package/dist/providers/openai/chat.js +1 -0
  180. package/dist/providers/openai/responses.d.ts +2 -0
  181. package/dist/providers/openai/responses.js +1 -0
  182. package/dist/providers/openai-compatible/responses.d.ts +1 -0
  183. package/dist/providers/openai-compatible/responses.js +1 -0
  184. package/dist/providers/openai-compatible-responses.d.ts +196 -0
  185. package/dist/providers/openai-compatible-responses.js +32 -0
  186. package/dist/providers/openai-compatible.d.ts +131 -0
  187. package/dist/providers/openai-compatible.js +33 -0
  188. package/dist/providers/openai-options.d.ts +26 -0
  189. package/dist/providers/openai-options.js +41 -0
  190. package/dist/providers/openai.d.ts +405 -0
  191. package/dist/providers/openai.js +82 -0
  192. package/dist/providers/openrouter.d.ts +564 -0
  193. package/dist/providers/openrouter.js +114 -0
  194. package/dist/providers/togetherai.d.ts +228 -0
  195. package/dist/providers/togetherai.js +40 -0
  196. package/dist/providers/xai.d.ts +164 -0
  197. package/dist/providers/xai.js +84 -0
  198. package/dist/providers/zai.d.ts +24 -0
  199. package/dist/providers/zai.js +21 -0
  200. package/dist/providers.d.ts +1 -0
  201. package/dist/providers.js +1 -0
  202. package/dist/route/auth-options.d.ts +34 -0
  203. package/dist/route/auth-options.js +14 -0
  204. package/dist/route/auth.d.ts +51 -0
  205. package/dist/route/auth.js +87 -0
  206. package/dist/route/client.d.ts +430 -0
  207. package/dist/route/client.js +304 -0
  208. package/dist/route/endpoint.d.ts +28 -0
  209. package/dist/route/endpoint.js +21 -0
  210. package/dist/route/executor.d.ts +27 -0
  211. package/dist/route/executor.js +183 -0
  212. package/dist/route/framing.d.ts +29 -0
  213. package/dist/route/framing.js +14 -0
  214. package/dist/route/index.d.ts +16 -0
  215. package/dist/route/index.js +9 -0
  216. package/dist/route/protocol.d.ts +76 -0
  217. package/dist/route/protocol.js +16 -0
  218. package/dist/route/transport/http.d.ts +34 -0
  219. package/dist/route/transport/http.js +68 -0
  220. package/dist/route/transport/index.d.ts +41 -0
  221. package/dist/route/transport/index.js +4 -0
  222. package/dist/route/transport/websocket-channel.d.ts +57 -0
  223. package/dist/route/transport/websocket-channel.js +1 -0
  224. package/dist/route/transport/websocket.d.ts +56 -0
  225. package/dist/route/transport/websocket.js +366 -0
  226. package/dist/route.d.ts +1 -0
  227. package/dist/route.js +1 -0
  228. package/dist/schema/errors.d.ts +158 -0
  229. package/dist/schema/errors.js +120 -0
  230. package/dist/schema/events.d.ts +8123 -0
  231. package/dist/schema/events.js +550 -0
  232. package/dist/schema/ids.d.ts +17 -0
  233. package/dist/schema/ids.js +10 -0
  234. package/dist/schema/index.d.ts +5 -0
  235. package/dist/schema/index.js +5 -0
  236. package/dist/schema/messages.d.ts +646 -0
  237. package/dist/schema/messages.js +301 -0
  238. package/dist/schema/options.d.ts +157 -0
  239. package/dist/schema/options.js +187 -0
  240. package/dist/testing.d.ts +1693 -0
  241. package/dist/testing.js +129 -0
  242. package/dist/tool-history.d.ts +2 -0
  243. package/dist/tool-history.js +84 -0
  244. package/dist/tool-runtime.d.ts +15 -0
  245. package/dist/tool-runtime.js +62 -0
  246. package/dist/tool.d.ts +136 -0
  247. package/dist/tool.js +66 -0
  248. package/dist/utils/record.d.ts +2 -0
  249. package/dist/utils/record.js +2 -0
  250. package/dist/utils/sanitize.d.ts +1 -0
  251. package/dist/utils/sanitize.js +12 -0
  252. package/package.json +48 -0
@@ -0,0 +1,1206 @@
1
+ import { Effect, Option, Schema } from "effect";
2
+ import { HttpTransport } from "../route/transport/index.js";
3
+ import { Protocol } from "../route/protocol.js";
4
+ import { AIError, LLMEvent, ProviderInternalError, 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
+ export const PATH = "/responses";
14
+ // =============================================================================
15
+ // Request Body Schema
16
+ // =============================================================================
17
+ export const OpenResponsesInputText = Schema.Struct({
18
+ type: Schema.tag("input_text"),
19
+ text: Schema.String,
20
+ });
21
+ export const OpenResponsesInputImage = Schema.Struct({
22
+ type: Schema.tag("input_image"),
23
+ image_url: Schema.String,
24
+ detail: Schema.optional(Schema.String),
25
+ });
26
+ export const OpenResponsesInputFile = Schema.Struct({
27
+ type: Schema.tag("input_file"),
28
+ filename: Schema.String,
29
+ detail: Schema.optional(Schema.String),
30
+ file_data: Schema.optional(Schema.String),
31
+ file_url: Schema.optional(Schema.String),
32
+ });
33
+ const OpenResponsesInputVideo = Schema.Struct({
34
+ type: Schema.tag("input_video"),
35
+ video_url: Schema.String,
36
+ });
37
+ const MediaInput = Schema.Union([OpenResponsesInputImage, OpenResponsesInputFile]);
38
+ const OpenResponsesInputContent = Schema.Union([OpenResponsesInputText, MediaInput]);
39
+ export const OpenResponsesOutputText = Schema.Struct({
40
+ type: Schema.tag("output_text"),
41
+ text: Schema.String,
42
+ });
43
+ export const MessagePhase = Schema.NullOr(Schema.Literals(["commentary", "final_answer"]));
44
+ export const MessageMetadata = Schema.Struct({
45
+ itemId: Schema.optional(Schema.String),
46
+ type: Schema.optional(Schema.Literal("message")),
47
+ status: Schema.optional(Schema.String),
48
+ phase: Schema.optional(MessagePhase),
49
+ });
50
+ const messagePhase = (value) => {
51
+ if (value === null || value === "commentary" || value === "final_answer")
52
+ return value;
53
+ return undefined;
54
+ };
55
+ const OpenResponsesReasoningSummaryText = Schema.Struct({
56
+ type: Schema.tag("summary_text"),
57
+ text: Schema.String,
58
+ });
59
+ export const OpenResponsesReasoningItem = Schema.Struct({
60
+ type: Schema.tag("reasoning"),
61
+ id: Schema.optionalKey(Schema.String),
62
+ summary: Schema.Array(OpenResponsesReasoningSummaryText),
63
+ encrypted_content: optionalNull(Schema.String),
64
+ });
65
+ const OpenResponsesWebSearchCall = Schema.StructWithRest(Schema.Struct({
66
+ type: Schema.tag("web_search_call"),
67
+ id: Schema.String,
68
+ status: Schema.optional(Schema.String),
69
+ action: optionalNull(JsonObject),
70
+ }), [JsonObject]);
71
+ const OpenResponsesFileSearchCall = Schema.StructWithRest(Schema.Struct({
72
+ type: Schema.tag("file_search_call"),
73
+ id: Schema.String,
74
+ status: Schema.optional(Schema.String),
75
+ queries: Schema.optional(Schema.Array(Schema.String)),
76
+ results: optionalNull(Schema.Array(JsonObject)),
77
+ }), [JsonObject]);
78
+ const OpenResponsesCodeInterpreterCall = Schema.StructWithRest(Schema.Struct({
79
+ type: Schema.tag("code_interpreter_call"),
80
+ id: Schema.String,
81
+ status: Schema.optional(Schema.String),
82
+ code: optionalNull(Schema.String),
83
+ container_id: optionalNull(Schema.String),
84
+ outputs: optionalNull(Schema.Array(JsonObject)),
85
+ }), [JsonObject]);
86
+ const OpenResponsesMCPCall = Schema.StructWithRest(Schema.Struct({
87
+ type: Schema.tag("mcp_call"),
88
+ id: Schema.String,
89
+ status: Schema.optional(Schema.String),
90
+ server_label: Schema.optional(Schema.String),
91
+ name: Schema.optional(Schema.String),
92
+ arguments: Schema.optional(Schema.String),
93
+ output: optionalNull(Schema.String),
94
+ error: Schema.optional(Schema.Unknown),
95
+ }), [JsonObject]);
96
+ export const HostedToolItem = Schema.Union([
97
+ OpenResponsesWebSearchCall,
98
+ OpenResponsesFileSearchCall,
99
+ OpenResponsesCodeInterpreterCall,
100
+ OpenResponsesMCPCall,
101
+ ]);
102
+ // `function_call_output.output` accepts either a plain string or an ordered
103
+ // array of content items so tools can return images and files in addition to text.
104
+ // https://www.openresponses.org/reference
105
+ const OpenResponsesFunctionCallOutputContent = Schema.Union([
106
+ OpenResponsesInputText,
107
+ OpenResponsesInputImage,
108
+ OpenResponsesInputFile,
109
+ OpenResponsesInputVideo,
110
+ ]);
111
+ const OpenResponsesFunctionCallOutput = Schema.Union([
112
+ Schema.String,
113
+ Schema.Array(OpenResponsesFunctionCallOutputContent),
114
+ ]);
115
+ export const CompactionItem = Schema.Struct({
116
+ type: Schema.Literal("compaction"),
117
+ id: optionalNull(Schema.String),
118
+ encrypted_content: Schema.String,
119
+ });
120
+ export const InputItem = Schema.Union([
121
+ CompactionItem,
122
+ Schema.Struct({ role: Schema.tag("system"), content: Schema.String }),
123
+ Schema.Struct({ role: Schema.tag("developer"), content: Schema.String }),
124
+ Schema.Struct({
125
+ role: Schema.tag("user"),
126
+ content: Schema.Array(OpenResponsesInputContent),
127
+ type: Schema.optional(Schema.Literal("message")),
128
+ id: Schema.optional(Schema.String),
129
+ status: Schema.optional(Schema.String),
130
+ }),
131
+ Schema.Struct({
132
+ type: Schema.tag("message"),
133
+ id: Schema.optionalKey(Schema.String),
134
+ role: Schema.tag("assistant"),
135
+ content: Schema.Array(OpenResponsesOutputText),
136
+ phase: Schema.optionalKey(MessagePhase),
137
+ status: Schema.optional(Schema.String),
138
+ }),
139
+ OpenResponsesReasoningItem,
140
+ Schema.Struct({
141
+ type: Schema.tag("function_call"),
142
+ id: Schema.optionalKey(Schema.String),
143
+ call_id: Schema.String,
144
+ name: Schema.String,
145
+ namespace: Schema.optional(Schema.String),
146
+ arguments: Schema.String,
147
+ }),
148
+ Schema.Struct({
149
+ type: Schema.tag("function_call_output"),
150
+ call_id: Schema.String,
151
+ output: OpenResponsesFunctionCallOutput,
152
+ }),
153
+ HostedToolItem,
154
+ ]);
155
+ export const Tool = Schema.Struct({
156
+ type: Schema.tag("function"),
157
+ name: Schema.String,
158
+ description: Schema.String,
159
+ parameters: JsonObject,
160
+ strict: Schema.optional(Schema.Boolean),
161
+ });
162
+ export const ToolChoice = Schema.Union([
163
+ Schema.Literals(["auto", "none", "required"]),
164
+ Schema.Struct({ type: Schema.tag("function"), name: Schema.String }),
165
+ Schema.Struct({
166
+ type: Schema.tag("allowed_tools"),
167
+ mode: Schema.Literals(["auto", "none", "required"]),
168
+ tools: Schema.Array(Schema.Struct({ type: Schema.tag("function"), name: Schema.String })),
169
+ }),
170
+ ]);
171
+ // Fields shared between the HTTP body and the WebSocket `response.create`
172
+ // message. The HTTP body adds `stream: true`; the WebSocket message adds
173
+ // `type: "response.create"`. Defining the shared shape once keeps the two
174
+ // transports in sync without a destructure-and-strip dance.
175
+ export const coreFields = {
176
+ model: Schema.String,
177
+ input: Schema.Array(InputItem),
178
+ instructions: Schema.optional(Schema.String),
179
+ tools: optionalArray(Tool),
180
+ tool_choice: Schema.optional(ToolChoice),
181
+ store: Schema.optional(Schema.Boolean),
182
+ metadata: Schema.optional(Schema.Record(Schema.String, Schema.String)),
183
+ safety_identifier: Schema.optional(Schema.String),
184
+ stream_options: Schema.optional(Schema.Struct({
185
+ include_obfuscation: Schema.optional(Schema.Boolean),
186
+ })),
187
+ top_logprobs: Schema.optional(Schema.Int.check(Schema.isBetween({ minimum: 0, maximum: 20 }))),
188
+ truncation: Schema.optional(OpenResponsesOptions.TruncationSchema),
189
+ service_tier: Schema.optional(OpenResponsesOptions.ServiceTierSchema),
190
+ prompt_cache_key: Schema.optional(Schema.String),
191
+ include: optionalArray(OpenResponsesOptions.ResponseIncludableSchema),
192
+ reasoning: Schema.optional(Schema.Struct({
193
+ effort: Schema.optional(OpenResponsesOptions.ReasoningEffort),
194
+ summary: Schema.optional(Schema.Literals(["auto", "concise", "detailed"])),
195
+ })),
196
+ text: Schema.optional(Schema.Struct({
197
+ verbosity: Schema.optional(OpenResponsesOptions.TextVerbositySchema),
198
+ })),
199
+ max_output_tokens: Schema.optional(Schema.Number),
200
+ max_tool_calls: Schema.optional(Schema.Int),
201
+ parallel_tool_calls: Schema.optional(Schema.Boolean),
202
+ temperature: Schema.optional(Schema.Number),
203
+ top_p: Schema.optional(Schema.Number),
204
+ presence_penalty: Schema.optional(Schema.Number),
205
+ frequency_penalty: Schema.optional(Schema.Number),
206
+ };
207
+ const OpenResponsesBody = Schema.Struct({
208
+ ...coreFields,
209
+ stream: Schema.Literal(true),
210
+ });
211
+ export const OpenResponsesUsage = Schema.Struct({
212
+ input_tokens: Schema.optional(Schema.Number),
213
+ input_tokens_details: optionalNull(Schema.Struct({
214
+ cached_tokens: Schema.optional(Schema.Number),
215
+ cache_write_tokens: Schema.optional(Schema.Number),
216
+ })),
217
+ output_tokens: Schema.optional(Schema.Number),
218
+ output_tokens_details: optionalNull(Schema.Struct({ reasoning_tokens: Schema.optional(Schema.Number) })),
219
+ total_tokens: Schema.optional(Schema.Number),
220
+ });
221
+ // The spec requires `id` on every output item, but some gateways drop it from
222
+ // later item events (Bedrock Mantle renames it to `item_id` on
223
+ // `output_item.done` and `response.completed.output`). Decode it as optional
224
+ // and let `normalize` recover or mint it once before the parser runs.
225
+ // https://www.openresponses.org/specification#extending-items
226
+ export const StreamItem = Schema.StructWithRest(Schema.Struct({
227
+ type: Schema.String,
228
+ id: Schema.optional(Schema.String),
229
+ call_id: Schema.optional(Schema.String),
230
+ name: Schema.optional(Schema.String),
231
+ namespace: Schema.optional(Schema.String),
232
+ arguments: Schema.optional(Schema.String),
233
+ encrypted_content: optionalNull(Schema.String),
234
+ }), [Schema.Record(Schema.String, Schema.Unknown)]);
235
+ // The Responses schema puts streaming error details at the top level and
236
+ // response failures under `response.error`. WebSocket failures use an
237
+ // event-level `error` envelope, so accept all three shapes here.
238
+ // https://www.openresponses.org/specification
239
+ const OpenResponsesErrorPayload = Schema.Struct({
240
+ type: optionalNull(Schema.String),
241
+ code: optionalNull(Schema.String),
242
+ message: optionalNull(Schema.String),
243
+ param: optionalNull(Schema.String),
244
+ });
245
+ const WebSocketErrorHeader = Schema.Union([Schema.String, Schema.Number, Schema.Boolean]);
246
+ export const WebSocketErrorEvent = Schema.StructWithRest(Schema.Struct({
247
+ type: Schema.tag("error"),
248
+ status: Schema.optional(Schema.Number),
249
+ status_code: Schema.optional(Schema.Number),
250
+ code: optionalNull(Schema.String),
251
+ message: Schema.optional(Schema.String),
252
+ param: optionalNull(Schema.String),
253
+ error: optionalNull(OpenResponsesErrorPayload),
254
+ headers: Schema.optional(Schema.Record(Schema.String, WebSocketErrorHeader)),
255
+ }), [Schema.Record(Schema.String, Schema.Unknown)]);
256
+ const decodeWebSocketErrorEvent = Schema.decodeUnknownEffect(WebSocketErrorEvent);
257
+ export const decodeKnownErrorEvent = (event) => decodeWebSocketErrorEvent({
258
+ ...event,
259
+ status: typeof event.status === "number" ? event.status : undefined,
260
+ status_code: typeof event.status_code === "number" ? event.status_code : undefined,
261
+ headers: ProviderShared.isRecord(event.headers)
262
+ ? Object.fromEntries(Object.entries(event.headers).filter((entry) => typeof entry[1] === "string" || typeof entry[1] === "number" || typeof entry[1] === "boolean"))
263
+ : undefined,
264
+ });
265
+ export const Event = Schema.StructWithRest(Schema.Struct({
266
+ type: Schema.String,
267
+ delta: Schema.optional(Schema.String),
268
+ arguments: Schema.optional(Schema.String),
269
+ text: Schema.optional(Schema.String),
270
+ item_id: Schema.optional(Schema.String),
271
+ output_index: Schema.optional(Schema.Number),
272
+ summary_index: Schema.optional(Schema.Number),
273
+ // OutputItemAdded/Done permit a null item in the Open Responses OpenAPI schema.
274
+ item: optionalNull(StreamItem),
275
+ response: Schema.optional(Schema.StructWithRest(Schema.Struct({
276
+ id: Schema.optional(Schema.String),
277
+ service_tier: optionalNull(Schema.String),
278
+ incomplete_details: optionalNull(Schema.Struct({ reason: Schema.optional(Schema.String) })),
279
+ output: Schema.optional(Schema.Array(StreamItem)),
280
+ usage: optionalNull(OpenResponsesUsage),
281
+ error: optionalNull(OpenResponsesErrorPayload),
282
+ }), [Schema.Record(Schema.String, Schema.Unknown)])),
283
+ code: optionalNull(Schema.String),
284
+ message: Schema.optional(Schema.String),
285
+ param: optionalNull(Schema.String),
286
+ error: optionalNull(OpenResponsesErrorPayload),
287
+ status: Schema.optional(Schema.Unknown),
288
+ status_code: Schema.optional(Schema.Unknown),
289
+ headers: Schema.optional(Schema.Unknown),
290
+ }), [Schema.Record(Schema.String, Schema.Unknown)]);
291
+ const BASE_ADAPTER = { id: ADAPTER, name: NAME };
292
+ // =============================================================================
293
+ // Request Lowering
294
+ // =============================================================================
295
+ export const lowerTool = Effect.fn("OpenResponses.lowerTool")(function* (protocolName, tool, inputSchema) {
296
+ if (tool.native !== undefined)
297
+ return yield* ProviderShared.invalidRequest(`${protocolName} does not support provider-native tool ${tool.name}`);
298
+ return {
299
+ type: "function",
300
+ name: tool.name,
301
+ description: tool.description,
302
+ parameters: inputSchema,
303
+ // The common tool definition does not currently express Responses strict-schema policy.
304
+ strict: false,
305
+ };
306
+ });
307
+ export const lowerToolChoice = (protocolName, toolChoice) => ProviderShared.matchToolChoice(protocolName, toolChoice, {
308
+ auto: () => "auto",
309
+ none: () => "none",
310
+ required: () => "required",
311
+ tool: (toolName) => ({ type: "function", name: toolName }),
312
+ });
313
+ // Server-issued item ids need a nonempty prefix and suffix, but the prefix is
314
+ // provider-defined and does not necessarily identify the item's semantic type.
315
+ const itemID = (providerMetadata, providerMetadataKey) => {
316
+ const metadata = providerMetadata?.[providerMetadataKey];
317
+ if (!ProviderShared.isRecord(metadata) || typeof metadata.itemId !== "string")
318
+ return undefined;
319
+ const separator = metadata.itemId.indexOf("_");
320
+ return separator > 0 && separator < metadata.itemId.length - 1 ? metadata.itemId : undefined;
321
+ };
322
+ const lowerToolCall = (part, providerMetadataKey) => {
323
+ const id = itemID(part.providerMetadata, providerMetadataKey);
324
+ return {
325
+ type: "function_call",
326
+ ...(id === undefined ? {} : { id }),
327
+ call_id: part.id,
328
+ name: part.name,
329
+ namespace: part.namespace,
330
+ arguments: ProviderShared.encodeJson(part.input),
331
+ };
332
+ };
333
+ const lowerReasoning = (part, providerMetadataKey) => {
334
+ const metadata = part.providerMetadata?.[providerMetadataKey];
335
+ if (!ProviderShared.isRecord(metadata))
336
+ return undefined;
337
+ const id = itemID(part.providerMetadata, providerMetadataKey);
338
+ const encryptedContent = typeof metadata.reasoningEncryptedContent === "string" || metadata.reasoningEncryptedContent === null
339
+ ? metadata.reasoningEncryptedContent
340
+ : undefined;
341
+ return {
342
+ type: "reasoning",
343
+ ...(id === undefined ? {} : { id }),
344
+ summary: part.text.length > 0 ? [{ type: "summary_text", text: part.text }] : [],
345
+ encrypted_content: encryptedContent,
346
+ };
347
+ };
348
+ const lowerMedia = Effect.fn("OpenResponses.lowerMedia")(function* (part, request, adapter, target) {
349
+ const media = ProviderShared.normalizeMedia(part);
350
+ const providerMedia = adapter.lowerMedia?.({ part, media, request });
351
+ if (providerMedia)
352
+ return providerMedia;
353
+ const detail = yield* ProviderShared.validateWith(Schema.decodeUnknownEffect(OpenResponsesInputImage.fields.detail))(part.providerMetadata?.[metadataKey(request.model)]?.detail);
354
+ const url = typeof part.data === "string" && (part.data.startsWith("https://") || part.data.startsWith("http://"))
355
+ ? part.data
356
+ : undefined;
357
+ if (!media.mime.startsWith("image/")) {
358
+ if (target === "tool-result" && media.mime.startsWith("video/"))
359
+ return { type: "input_video", video_url: url ?? media.dataUrl };
360
+ return {
361
+ type: "input_file",
362
+ filename: part.filename ?? (media.mime === "application/pdf" ? "document.pdf" : "file"),
363
+ detail,
364
+ ...(url ? { file_url: url } : { file_data: media.dataUrl }),
365
+ };
366
+ }
367
+ return {
368
+ type: "input_image",
369
+ image_url: url ?? media.dataUrl,
370
+ detail,
371
+ };
372
+ });
373
+ const lowerUserContent = Effect.fnUntraced(function* (part, request, adapter) {
374
+ if (part.type === "text")
375
+ return { type: "input_text", text: part.text };
376
+ if (part.type === "media")
377
+ return yield* lowerMessageMedia(part, request, adapter);
378
+ return yield* ProviderShared.unsupportedContent(adapter.name, "user", ["text", "media"]);
379
+ });
380
+ const lowerMessageMedia = Effect.fnUntraced(function* (part, request, adapter) {
381
+ const lowered = yield* lowerMedia(part, request, adapter, "message");
382
+ if (lowered.type === "input_video")
383
+ return yield* ProviderShared.invalidRequest(`${adapter.name} user messages do not support input_video`);
384
+ return lowered;
385
+ });
386
+ // Tool results may carry structured text, images, and files. Keep media as provider-native
387
+ // content instead of JSON-stringifying base64 into a prompt string.
388
+ const lowerToolResultContentItem = Effect.fnUntraced(function* (item, request, adapter) {
389
+ if (item.type === "text")
390
+ return { type: "input_text", text: item.text };
391
+ return yield* lowerMedia({ type: "media", mediaType: item.mime, data: item.uri, filename: item.name }, request, adapter, "tool-result");
392
+ });
393
+ const lowerHostedToolResultContentItem = Effect.fnUntraced(function* (item, request, adapter) {
394
+ if (item.type === "text")
395
+ return { type: "input_text", text: item.text };
396
+ return yield* lowerMessageMedia({ type: "media", mediaType: item.mime, data: item.uri, filename: item.name }, request, adapter);
397
+ });
398
+ const lowerToolResultOutput = Effect.fnUntraced(function* (part, request, adapter) {
399
+ // Text/json/error results are encoded as a plain string for backward
400
+ // compatibility with existing cassettes and provider expectations.
401
+ if (part.result.type !== "content")
402
+ return ProviderShared.toolResultText(part);
403
+ // Preserve the narrowed array element type when compiled through a consumer package.
404
+ const content = part.result.value;
405
+ return yield* Effect.forEach(content, (item) => lowerToolResultContentItem(item, request, adapter));
406
+ });
407
+ const lowerMessages = Effect.fn("OpenResponses.lowerMessages")(function* (request, adapter) {
408
+ const input = [];
409
+ const providerMetadataKey = metadataKey(request.model);
410
+ for (const message of request.messages) {
411
+ const metadata = yield* ProviderShared.validateWith(Schema.decodeUnknownEffect(Schema.UndefinedOr(MessageMetadata)))(message.providerMetadata?.[providerMetadataKey]);
412
+ if (message.role === "system") {
413
+ input.push({
414
+ role: "developer",
415
+ content: ProviderShared.joinText(yield* ProviderShared.systemUpdateText(adapter.name, message)),
416
+ });
417
+ continue;
418
+ }
419
+ if (message.role === "user") {
420
+ const content = yield* Effect.forEach(message.content, (part) => lowerUserContent(part, request, adapter));
421
+ if (content.length > 0)
422
+ input.push({ role: "user", content, type: metadata?.type, id: metadata?.itemId, status: metadata?.status });
423
+ continue;
424
+ }
425
+ if (message.role === "assistant") {
426
+ const content = [];
427
+ const reasoningItems = {};
428
+ const hostedToolItems = new Set();
429
+ const flushText = () => {
430
+ if (content.length === 0)
431
+ return;
432
+ const groups = content.reduce((groups, part) => {
433
+ const partMetadata = part.providerMetadata?.[providerMetadataKey];
434
+ const id = itemID(part.providerMetadata, providerMetadataKey) ?? metadata?.itemId;
435
+ const partPhase = messagePhase(partMetadata?.phase);
436
+ const phase = partPhase === undefined ? metadata?.phase : partPhase;
437
+ const group = groups.at(-1);
438
+ if (group && group.id === id && group.phase === phase)
439
+ group.parts.push(part);
440
+ else
441
+ groups.push({ id, phase, parts: [part] });
442
+ return groups;
443
+ }, []);
444
+ input.push(...groups.map((group) => ({
445
+ type: "message",
446
+ ...(group.id === undefined ? {} : { id: group.id }),
447
+ role: "assistant",
448
+ status: metadata?.status,
449
+ content: group.parts.map((part) => ({ type: "output_text", text: part.text })),
450
+ ...(group.phase === undefined ? {} : { phase: group.phase }),
451
+ })));
452
+ content.splice(0, content.length);
453
+ };
454
+ for (const part of message.content) {
455
+ if (part.type === "compaction") {
456
+ flushText();
457
+ if (part.provider !== request.model.provider || part.encrypted === undefined)
458
+ return yield* ProviderShared.invalidRequest("Compaction state must be replayed to its originating provider and API");
459
+ input.push({ type: "compaction", id: part.id, encrypted_content: part.encrypted });
460
+ continue;
461
+ }
462
+ if (part.type === "text") {
463
+ content.push(part);
464
+ continue;
465
+ }
466
+ if (part.type === "reasoning") {
467
+ flushText();
468
+ const reasoning = lowerReasoning(part, providerMetadataKey);
469
+ if (!reasoning)
470
+ continue;
471
+ const existing = reasoning.id === undefined ? undefined : reasoningItems[reasoning.id];
472
+ if (existing) {
473
+ existing.summary.push(...reasoning.summary);
474
+ if (typeof reasoning.encrypted_content === "string")
475
+ existing.encrypted_content = reasoning.encrypted_content;
476
+ continue;
477
+ }
478
+ if (reasoning.id !== undefined)
479
+ reasoningItems[reasoning.id] = reasoning;
480
+ input.push(reasoning);
481
+ continue;
482
+ }
483
+ if (part.type === "tool-call") {
484
+ flushText();
485
+ if (part.providerExecuted === true)
486
+ continue;
487
+ input.push(lowerToolCall(part, providerMetadataKey));
488
+ continue;
489
+ }
490
+ if (part.type === "tool-result" && part.providerExecuted === true) {
491
+ flushText();
492
+ const id = itemID(part.providerMetadata, providerMetadataKey);
493
+ const hosted = part.result.type !== "json"
494
+ ? undefined
495
+ : Schema.is(HostedToolItem)(part.result.value)
496
+ ? part.result.value
497
+ : adapter.restoreHostedToolItem?.(part.result.value);
498
+ if (id !== undefined && hosted?.id === id) {
499
+ if (!hostedToolItems.has(id)) {
500
+ input.push(hosted);
501
+ hostedToolItems.add(id);
502
+ }
503
+ continue;
504
+ }
505
+ const content = part.result.type === "content"
506
+ ? part.result.value
507
+ : [{ type: "text", text: ProviderShared.toolResultText(part) }];
508
+ input.push({
509
+ role: "user",
510
+ content: yield* Effect.forEach(content, (item) => lowerHostedToolResultContentItem(item, request, adapter)),
511
+ });
512
+ continue;
513
+ }
514
+ return yield* ProviderShared.unsupportedContent(adapter.name, "assistant", [
515
+ "text",
516
+ "reasoning",
517
+ "tool-call",
518
+ "tool-result",
519
+ ]);
520
+ }
521
+ flushText();
522
+ continue;
523
+ }
524
+ for (const part of message.content) {
525
+ if (!ProviderShared.supportsContent(part, ["tool-result"]))
526
+ return yield* ProviderShared.unsupportedContent(adapter.name, "tool", ["tool-result"]);
527
+ input.push({
528
+ type: "function_call_output",
529
+ call_id: part.id,
530
+ output: yield* lowerToolResultOutput(part, request, adapter),
531
+ });
532
+ }
533
+ }
534
+ return input;
535
+ });
536
+ export const lowerConversation = Effect.fn("OpenResponses.lowerConversation")(function* (request, adapter) {
537
+ const instructions = ProviderShared.joinText(request.system);
538
+ return {
539
+ model: request.model.id,
540
+ input: yield* lowerMessages(request, adapter),
541
+ ...(instructions ? { instructions } : {}),
542
+ };
543
+ });
544
+ export const lowerGeneration = (request) => {
545
+ const options = OpenResponsesOptions.resolve(request);
546
+ const generation = request.generation;
547
+ const cacheKey = ProviderShared.promptCacheKey(request);
548
+ const parallelToolCalls = resolveParallelToolCalls(request);
549
+ return {
550
+ stream: true,
551
+ max_output_tokens: generation?.maxTokens,
552
+ temperature: generation?.temperature,
553
+ top_p: generation?.topP,
554
+ presence_penalty: generation?.presencePenalty,
555
+ frequency_penalty: generation?.frequencyPenalty,
556
+ ...(options.store !== undefined ? { store: options.store } : {}),
557
+ ...(options.metadata ? { metadata: options.metadata } : {}),
558
+ ...(options.safetyIdentifier ? { safety_identifier: options.safetyIdentifier } : {}),
559
+ ...(options.streamOptions?.includeObfuscation !== undefined
560
+ ? { stream_options: { include_obfuscation: options.streamOptions.includeObfuscation } }
561
+ : {}),
562
+ ...(options.topLogprobs !== undefined ? { top_logprobs: options.topLogprobs } : {}),
563
+ ...(cacheKey ? { prompt_cache_key: cacheKey } : {}),
564
+ ...(options.include ? { include: options.include } : {}),
565
+ ...(options.reasoningEffort || options.reasoningSummary
566
+ ? { reasoning: { effort: options.reasoningEffort, summary: options.reasoningSummary } }
567
+ : {}),
568
+ ...(options.textVerbosity ? { text: { verbosity: options.textVerbosity } } : {}),
569
+ ...(options.serviceTier ? { service_tier: options.serviceTier } : {}),
570
+ ...(options.maxToolCalls !== undefined ? { max_tool_calls: options.maxToolCalls } : {}),
571
+ ...(parallelToolCalls !== undefined ? { parallel_tool_calls: parallelToolCalls } : {}),
572
+ ...(options.truncation ? { truncation: options.truncation } : {}),
573
+ };
574
+ };
575
+ export const resolveParallelToolCalls = (request) => {
576
+ const configured = OpenResponsesOptions.resolve(request).parallelToolCalls;
577
+ if (configured !== undefined)
578
+ return configured;
579
+ const disabled = request.toolChoice?.disableParallelToolUse;
580
+ return disabled === undefined ? undefined : !disabled;
581
+ };
582
+ export const allowedToolChoice = (request) => {
583
+ const allowed = OpenResponsesOptions.resolve(request).allowedTools;
584
+ if (!allowed)
585
+ return undefined;
586
+ return {
587
+ type: "allowed_tools",
588
+ mode: allowed.mode,
589
+ tools: allowed.toolNames.map((name) => ({ type: "function", name })),
590
+ };
591
+ };
592
+ export const fromRequestWithAdapter = Effect.fn("OpenResponses.fromRequestWithAdapter")(function* (request, adapter) {
593
+ const projected = ProviderShared.flattenToolRequest(request);
594
+ const toolSchemaCompatibility = request.model.compatibility?.toolSchema;
595
+ return {
596
+ ...(yield* lowerConversation(projected.request, adapter)),
597
+ ...lowerGeneration(request),
598
+ tools: projected.tools.length === 0
599
+ ? undefined
600
+ : yield* Effect.forEach(projected.tools, (tool) => lowerTool(adapter.name, tool, ToolSchemaProjection.modelCompatibility(tool.inputSchema, toolSchemaCompatibility))),
601
+ tool_choice: allowedToolChoice(request) ??
602
+ (request.toolChoice ? yield* lowerToolChoice(adapter.name, request.toolChoice) : undefined),
603
+ };
604
+ });
605
+ const decodeBody = ProviderShared.validateWith(Schema.decodeUnknownEffect(OpenResponsesBody));
606
+ export const fromRequest = Effect.fn("OpenResponses.fromRequest")(function* (request) {
607
+ return yield* decodeBody(yield* fromRequestWithAdapter(request, BASE_ADAPTER));
608
+ });
609
+ // =============================================================================
610
+ // Stream Parsing
611
+ // =============================================================================
612
+ // Responses APIs report `input_tokens` (inclusive total) with a
613
+ // cached-read and cache-write subsets, and `output_tokens` (inclusive total)
614
+ // with a `reasoning_tokens` subset. Pass the totals through and derive the
615
+ // non-cached breakdown.
616
+ export const mapUsage = (usage, providerMetadataKey) => {
617
+ if (!usage)
618
+ return undefined;
619
+ const cached = usage.input_tokens_details?.cached_tokens;
620
+ const cacheWrite = usage.input_tokens_details?.cache_write_tokens;
621
+ const reasoning = usage.output_tokens_details?.reasoning_tokens;
622
+ const nonCached = ProviderShared.subtractTokens(usage.input_tokens, ProviderShared.sumTokens(cached, cacheWrite));
623
+ return new Usage({
624
+ inputTokens: usage.input_tokens,
625
+ outputTokens: usage.output_tokens,
626
+ nonCachedInputTokens: nonCached,
627
+ cacheReadInputTokens: cached,
628
+ cacheWriteInputTokens: cacheWrite,
629
+ reasoningTokens: reasoning,
630
+ totalTokens: ProviderShared.totalTokens(usage.input_tokens, usage.output_tokens, usage.total_tokens),
631
+ providerMetadata: { [providerMetadataKey]: usage },
632
+ });
633
+ };
634
+ const mapFinishReason = (event, hasFunctionCall) => {
635
+ const reason = event.response?.incomplete_details?.reason;
636
+ if (reason === undefined || reason === null) {
637
+ if (hasFunctionCall)
638
+ return "tool-calls";
639
+ if (event.type === "response.incomplete")
640
+ return "unknown";
641
+ return "stop";
642
+ }
643
+ if (reason === "max_output_tokens")
644
+ return "length";
645
+ if (reason === "content_filter")
646
+ return "content-filter";
647
+ return hasFunctionCall ? "tool-calls" : "unknown";
648
+ };
649
+ export const metadataKey = (model) => model.route.providerMetadataKey ?? "openresponses";
650
+ export const providerMetadata = (state, metadata) => ({
651
+ [state.providerMetadataKey]: metadata,
652
+ });
653
+ const NO_EVENTS = [];
654
+ // `response.completed` / `response.incomplete` are clean finishes that emit a
655
+ // `finish` event; `response.failed` and `error` are hard failures. All four end
656
+ // the stream, so keep this set aligned with `step` and the protocol's terminal predicate.
657
+ const TERMINAL_TYPES = new Set(["error", "response.completed", "response.incomplete", "response.failed"]);
658
+ export const terminal = (event) => TERMINAL_TYPES.has(event.type);
659
+ const onOutputTextDelta = (state, event, id) => {
660
+ if (!event.delta || state.message?.id !== id)
661
+ return [state, NO_EVENTS];
662
+ const events = [];
663
+ const phase = state.message.phase;
664
+ const metadata = providerMetadata(state, { itemId: id, ...(phase === undefined ? {} : { phase }) });
665
+ const lifecycle = Lifecycle.textStart(state.lifecycle, events, id, metadata);
666
+ return [{ ...state, lifecycle: Lifecycle.textDelta(lifecycle, events, id, event.delta) }, events];
667
+ };
668
+ const onOutputTextDone = (state, event, id) => {
669
+ if (state.message?.id === id) {
670
+ if (state.lifecycle.text.has(id) || event.text === undefined)
671
+ return [state, NO_EVENTS];
672
+ return onOutputTextDelta(state, { ...event, delta: event.text }, id);
673
+ }
674
+ const events = [];
675
+ return [{ ...state, lifecycle: Lifecycle.textEnd(state.lifecycle, events, id) }, events];
676
+ };
677
+ const decodeMessagePart = Schema.decodeUnknownOption(Schema.Union([OpenResponsesOutputText, Schema.Struct({ type: Schema.tag("refusal"), refusal: Schema.String })]));
678
+ const decodeSummaryPart = Schema.decodeUnknownOption(OpenResponsesReasoningSummaryText);
679
+ const decodeReasoningPart = Schema.decodeUnknownOption(Schema.Struct({ type: Schema.tag("reasoning_text"), text: Schema.String }));
680
+ const joinReasoningText = (parts) => {
681
+ if (!parts.some((part) => part !== undefined && part.length > 0))
682
+ return undefined;
683
+ return parts.filter((part) => part !== undefined).join("\n\n");
684
+ };
685
+ const outputItemID = (state, event) => event.output_index === undefined ? event.item_id : (state.outputItems[event.output_index] ?? event.item_id);
686
+ const ITEM_ID_PREFIX = {
687
+ message: "msg",
688
+ reasoning: "rs",
689
+ function_call: "fc",
690
+ compaction: "cmp",
691
+ };
692
+ // An item without an id adopts the id already open in its output slot,
693
+ // otherwise it gets a locally minted one.
694
+ const resolveItem = (state, item, index) => ({
695
+ ...item,
696
+ id: item.id ??
697
+ (index === undefined ? undefined : state.outputItems[index]) ??
698
+ `${ITEM_ID_PREFIX[item.type] ?? "item"}_${crypto.randomUUID().replaceAll("-", "")}`,
699
+ });
700
+ // Registered output slots are authoritative for `item_id` routing, and items
701
+ // are resolved here so everything downstream can rely on `item.id`.
702
+ export const normalize = (state, input) => ({
703
+ ...input,
704
+ item_id: input.item_id === undefined ? undefined : outputItemID(state, input),
705
+ item: input.item ? resolveItem(state, input.item, input.output_index) : input.item,
706
+ });
707
+ const startReasoningSummaryPart = (state, itemID, index) => {
708
+ const item = state.reasoningItems[itemID];
709
+ if (!item || index === 0 || item.summaryParts[index] !== undefined)
710
+ return [state, NO_EVENTS];
711
+ const events = [];
712
+ const lifecycle = Object.entries(item.summaryParts)
713
+ .filter((entry) => entry[1] !== "concluded")
714
+ .reduce((lifecycle, entry) => Lifecycle.reasoningEnd(lifecycle, events, `${itemID}:${entry[0]}`, providerMetadata(state, { itemId: itemID })), state.lifecycle);
715
+ return [
716
+ {
717
+ ...state,
718
+ lifecycle: Lifecycle.reasoningStart(lifecycle, events, `${itemID}:${index}`, providerMetadata(state, { itemId: itemID, reasoningEncryptedContent: item.encryptedContent ?? null })),
719
+ reasoningItems: {
720
+ ...state.reasoningItems,
721
+ [itemID]: {
722
+ ...item,
723
+ summaryParts: {
724
+ ...Object.fromEntries(Object.entries(item.summaryParts).map((entry) => entry[1] === "concluded" ? entry : [entry[0], "concluded"])),
725
+ [index]: "active",
726
+ },
727
+ },
728
+ },
729
+ },
730
+ events,
731
+ ];
732
+ };
733
+ export const onReasoningDelta = (state, event, itemID) => {
734
+ const item = state.reasoningItems[itemID];
735
+ if (!event.delta || !item)
736
+ return [state, NO_EVENTS];
737
+ const index = event.summary_index ?? 0;
738
+ if (item.summaryParts[index] === "concluded")
739
+ return [state, NO_EVENTS];
740
+ const [started, emitted] = startReasoningSummaryPart(state, itemID, index);
741
+ const current = started.reasoningItems[itemID];
742
+ if (!current)
743
+ return [started, emitted];
744
+ const events = [...emitted];
745
+ return [
746
+ {
747
+ ...started,
748
+ lifecycle: Lifecycle.reasoningDelta(started.lifecycle, events, `${itemID}:${index}`, event.delta),
749
+ reasoningItems: {
750
+ ...started.reasoningItems,
751
+ [itemID]: { ...current, deltaIndexes: new Set([...current.deltaIndexes, index]) },
752
+ },
753
+ },
754
+ events,
755
+ ];
756
+ };
757
+ // Some compatible gateways emit a reasoning final without streaming any
758
+ // deltas, mirroring `response.output_text.done`. Reconcile the complete text
759
+ // as a single delta unless that summary index already streamed one.
760
+ export const onReasoningDone = (state, event, itemID) => {
761
+ const item = state.reasoningItems[itemID];
762
+ if (!item || typeof event.text !== "string")
763
+ return [state, NO_EVENTS];
764
+ const index = event.summary_index ?? 0;
765
+ if (item.deltaIndexes.has(index))
766
+ return [state, NO_EVENTS];
767
+ return onReasoningDelta(state, { ...event, delta: event.text }, itemID);
768
+ };
769
+ const reasoningMetadata = (state, item) => providerMetadata(state, { itemId: item.id, reasoningEncryptedContent: item.encrypted_content ?? null });
770
+ // Responses APIs normally stream reasoning items in this order:
771
+ // `output_item.added` (reasoning) →
772
+ // `reasoning_summary_part.added` (index=0) →
773
+ // `reasoning_summary_text.delta` →
774
+ // `reasoning_summary_part.done` (index=0) →
775
+ // (repeat for index>0) →
776
+ // `output_item.done` (reasoning).
777
+ // `onOutputItemAdded` seeds the per-item entry, while each later part start is
778
+ // also an implicit boundary for the previous part. This keeps the common event
779
+ // lifecycle ordered when a compatible provider omits or delays a part-done event.
780
+ const onOutputItemAdded = (state, event) => {
781
+ const item = event.item;
782
+ if (!item)
783
+ return [state, NO_EVENTS];
784
+ if (item.type === "message") {
785
+ const phase = messagePhase(item.phase);
786
+ // A new message closes earlier messages, including ones that never streamed.
787
+ const events = [];
788
+ const lifecycle = [...state.lifecycle.text]
789
+ .filter((id) => id !== item.id)
790
+ .reduce((lifecycle, id) => {
791
+ const openPhase = state.message?.id === id ? state.message.phase : undefined;
792
+ return Lifecycle.textEnd(lifecycle, events, id, providerMetadata(state, { itemId: id, ...(openPhase === undefined ? {} : { phase: openPhase }) }));
793
+ }, state.lifecycle);
794
+ return [
795
+ {
796
+ ...state,
797
+ lifecycle,
798
+ message: {
799
+ id: item.id,
800
+ phase: phase === undefined && state.message?.id === item.id ? state.message.phase : phase,
801
+ },
802
+ },
803
+ events,
804
+ ];
805
+ }
806
+ if (item.type === "reasoning") {
807
+ if (state.reasoningItems[item.id] !== undefined)
808
+ return [state, NO_EVENTS];
809
+ const events = [];
810
+ return [
811
+ {
812
+ ...state,
813
+ lifecycle: Lifecycle.reasoningStart(state.lifecycle, events, `${item.id}:0`, reasoningMetadata(state, item)),
814
+ reasoningItems: {
815
+ ...state.reasoningItems,
816
+ [item.id]: {
817
+ encryptedContent: item.encrypted_content,
818
+ summaryParts: { 0: "active" },
819
+ deltaIndexes: new Set(),
820
+ },
821
+ },
822
+ },
823
+ events,
824
+ ];
825
+ }
826
+ if (item.type !== "function_call" || !item.call_id)
827
+ return [state, NO_EVENTS];
828
+ if (state.tools[item.id] !== undefined)
829
+ return [state, NO_EVENTS];
830
+ const metadata = providerMetadata(state, { itemId: item.id });
831
+ const events = [];
832
+ const lifecycle = Lifecycle.stepStart(state.lifecycle, events);
833
+ return [
834
+ {
835
+ ...state,
836
+ lifecycle,
837
+ tools: ToolStream.start(state.tools, item.id, {
838
+ id: item.call_id,
839
+ name: item.name ?? "",
840
+ namespace: item.namespace,
841
+ input: item.arguments ?? "",
842
+ providerMetadata: metadata,
843
+ }),
844
+ },
845
+ [
846
+ ...events,
847
+ LLMEvent.toolInputStart({
848
+ id: item.call_id,
849
+ name: item.name ?? "",
850
+ namespace: item.namespace,
851
+ providerMetadata: metadata,
852
+ }),
853
+ ],
854
+ ];
855
+ };
856
+ const onReasoningSummaryPartAdded = (state, event) => {
857
+ if (event.item_id === undefined || event.summary_index === undefined)
858
+ return [state, NO_EVENTS];
859
+ return startReasoningSummaryPart(state, event.item_id, event.summary_index);
860
+ };
861
+ const onReasoningSummaryPartDone = (state, event) => {
862
+ if (event.item_id === undefined || event.summary_index === undefined)
863
+ return [state, NO_EVENTS];
864
+ const item = state.reasoningItems[event.item_id];
865
+ if (!item)
866
+ return [state, NO_EVENTS];
867
+ if (item.summaryParts[event.summary_index] !== "active")
868
+ return [state, NO_EVENTS];
869
+ return [
870
+ {
871
+ ...state,
872
+ reasoningItems: {
873
+ ...state.reasoningItems,
874
+ [event.item_id]: {
875
+ ...item,
876
+ summaryParts: {
877
+ ...item.summaryParts,
878
+ [event.summary_index]: "can-conclude",
879
+ },
880
+ },
881
+ },
882
+ },
883
+ NO_EVENTS,
884
+ ];
885
+ };
886
+ const onFunctionCallArgumentsDelta = Effect.fn("OpenResponses.onFunctionCallArgumentsDelta")(function* (state, event) {
887
+ if (event.item_id === undefined)
888
+ return [state, NO_EVENTS];
889
+ const tool = state.tools[event.item_id];
890
+ if (!tool)
891
+ return [state, NO_EVENTS];
892
+ const final = event.type === "response.function_call_arguments.done" ? event.arguments : undefined;
893
+ if (event.type === "response.function_call_arguments.done" && final === undefined)
894
+ return [state, NO_EVENTS];
895
+ if (final !== undefined && !final.startsWith(tool.input))
896
+ return [
897
+ { ...state, tools: ToolStream.start(state.tools, event.item_id, { ...tool, input: final }) },
898
+ NO_EVENTS,
899
+ ];
900
+ const delta = final === undefined ? event.delta : final.slice(tool.input.length);
901
+ if (!delta)
902
+ return [state, NO_EVENTS];
903
+ const result = ToolStream.appendExisting(state.id, state.tools, event.item_id, delta, `${state.name} tool argument delta is missing its tool call`);
904
+ if (ToolStream.isError(result))
905
+ return yield* result;
906
+ const events = [];
907
+ const lifecycle = result.events.length ? Lifecycle.stepStart(state.lifecycle, events) : state.lifecycle;
908
+ events.push(...result.events);
909
+ return [{ ...state, lifecycle, tools: result.tools }, events];
910
+ });
911
+ const onOutputItemDone = Effect.fn("OpenResponses.onOutputItemDone")(function* (state, item) {
912
+ if (!item)
913
+ return [state, NO_EVENTS];
914
+ if (item.type === "compaction") {
915
+ if (typeof item.encrypted_content !== "string")
916
+ return yield* ProviderShared.eventError(state.id, "Compaction output is missing its encrypted content");
917
+ if (state.completedCompactions.has(item.id))
918
+ return [state, NO_EVENTS];
919
+ const events = [];
920
+ const lifecycle = Lifecycle.stepStart(state.lifecycle, events);
921
+ events.push(LLMEvent.compaction({
922
+ provider: state.provider,
923
+ id: item.id,
924
+ encrypted: item.encrypted_content,
925
+ }));
926
+ return [
927
+ { ...state, lifecycle, completedCompactions: new Set([...state.completedCompactions, item.id]) },
928
+ events,
929
+ ];
930
+ }
931
+ if (item.type === "message") {
932
+ const active = state.message?.id === item.id;
933
+ const itemPhase = messagePhase(item.phase);
934
+ const phase = itemPhase === undefined && active ? state.message?.phase : itemPhase;
935
+ const parts = Array.isArray(item.content) ? item.content : [];
936
+ const content = [];
937
+ for (const part of parts) {
938
+ const decoded = Option.getOrUndefined(decodeMessagePart(part));
939
+ if (!decoded)
940
+ continue;
941
+ content.push(decoded.type === "output_text" ? decoded.text : decoded.refusal);
942
+ }
943
+ const text = content.length > 0 ? content.join("") : undefined;
944
+ const metadata = providerMetadata(state, { itemId: item.id, ...(phase === undefined ? {} : { phase }) });
945
+ const events = [];
946
+ const lifecycle = text ? Lifecycle.textStart(state.lifecycle, events, item.id, metadata) : state.lifecycle;
947
+ return [
948
+ {
949
+ ...state,
950
+ lifecycle: Lifecycle.textEnd(lifecycle, events, item.id, metadata, text),
951
+ message: active ? undefined : state.message,
952
+ },
953
+ events,
954
+ ];
955
+ }
956
+ if (item.type === "function_call") {
957
+ if (!item.call_id || !item.name)
958
+ return [state, NO_EVENTS];
959
+ const metadata = providerMetadata(state, { itemId: item.id });
960
+ const registered = state.tools[item.id] !== undefined;
961
+ const tools = registered
962
+ ? state.tools
963
+ : ToolStream.start(state.tools, item.id, {
964
+ id: item.call_id,
965
+ name: item.name,
966
+ namespace: item.namespace,
967
+ providerMetadata: metadata,
968
+ });
969
+ const result = item.arguments === undefined
970
+ ? yield* ToolStream.finish(state.id, tools, item.id)
971
+ : yield* ToolStream.finishWithInput(state.id, tools, item.id, item.arguments);
972
+ const events = [];
973
+ const finished = result.events ?? [];
974
+ // A done-only call never streamed a start event, so open its lifecycle here.
975
+ const resultEvents = registered || finished.length === 0
976
+ ? finished
977
+ : [
978
+ LLMEvent.toolInputStart({
979
+ id: item.call_id,
980
+ name: item.name,
981
+ namespace: item.namespace,
982
+ providerMetadata: metadata,
983
+ }),
984
+ ...finished,
985
+ ];
986
+ const lifecycle = resultEvents.length ? Lifecycle.stepStart(state.lifecycle, events) : state.lifecycle;
987
+ events.push(...resultEvents);
988
+ return [
989
+ {
990
+ ...state,
991
+ lifecycle,
992
+ hasFunctionCall: resultEvents.some((event) => LLMEvent.is.toolCall(event) || LLMEvent.is.toolInputError(event)) ||
993
+ state.hasFunctionCall,
994
+ tools: result.tools,
995
+ },
996
+ events,
997
+ ];
998
+ }
999
+ if (item.type === "reasoning") {
1000
+ const metadata = reasoningMetadata(state, item);
1001
+ const summaryParts = Array.isArray(item.summary) ? item.summary : [];
1002
+ const summary = [];
1003
+ for (const part of summaryParts) {
1004
+ const decoded = Option.getOrUndefined(decodeSummaryPart(part));
1005
+ // Keep missing entries so the array still matches the provider's summary indexes.
1006
+ summary.push(decoded?.text);
1007
+ }
1008
+ const reasoningParts = Array.isArray(item.content) ? item.content : [];
1009
+ const content = [];
1010
+ for (const part of reasoningParts) {
1011
+ const decoded = Option.getOrUndefined(decodeReasoningPart(part));
1012
+ if (decoded)
1013
+ content.push(decoded.text);
1014
+ }
1015
+ const itemText = joinReasoningText(summary) ?? joinReasoningText(content);
1016
+ const events = [];
1017
+ const reasoningItem = state.reasoningItems[item.id];
1018
+ if (reasoningItem) {
1019
+ const fragments = Object.entries(reasoningItem.summaryParts);
1020
+ let lifecycle = state.lifecycle;
1021
+ for (const [index, status] of fragments) {
1022
+ if (status === "concluded")
1023
+ continue;
1024
+ // Do not repeat earlier summaries that were already emitted as separate fragments.
1025
+ const finalText = fragments.length === 1 ? itemText : summary[Number(index)];
1026
+ lifecycle = Lifecycle.reasoningEnd(lifecycle, events, `${item.id}:${index}`, metadata, finalText || undefined);
1027
+ }
1028
+ const reasoningItems = { ...state.reasoningItems };
1029
+ delete reasoningItems[item.id];
1030
+ return [{ ...state, lifecycle, reasoningItems }, events];
1031
+ }
1032
+ const lifecycle = Lifecycle.stepStart(state.lifecycle, events);
1033
+ events.push(LLMEvent.reasoningStart({ id: item.id, providerMetadata: metadata }));
1034
+ events.push(LLMEvent.reasoningEnd({ id: item.id, providerMetadata: metadata, text: itemText }));
1035
+ return [{ ...state, lifecycle }, events];
1036
+ }
1037
+ return [state, NO_EVENTS];
1038
+ });
1039
+ const onResponseFinish = Effect.fn("OpenResponses.onResponseFinish")(function* (state, event) {
1040
+ let current = state;
1041
+ const events = [];
1042
+ if (event.type === "response.completed") {
1043
+ // An output item's array position is its output index.
1044
+ for (const item of (event.response?.output ?? []).map((item, index) => resolveItem(state, item, index))) {
1045
+ // Terminal recovery cannot insert a checkpoint before already-emitted content.
1046
+ if (item.type === "compaction" && state.lifecycle.stepStarted && !state.completedCompactions.has(item.id))
1047
+ return yield* ProviderShared.eventError(state.id, "Cannot recover a compaction checkpoint after output has been emitted");
1048
+ const recoverable = item.type === "compaction" || (item.type === "function_call" && current.tools[item.id] !== undefined);
1049
+ if (!recoverable)
1050
+ continue;
1051
+ const [next, emitted] = yield* onOutputItemDone(current, item);
1052
+ current = next;
1053
+ events.push(...emitted);
1054
+ }
1055
+ // Some compatible providers omit output_item.done even after completing the response.
1056
+ const pending = yield* ToolStream.finishAll(current.id, current.tools);
1057
+ current = {
1058
+ ...current,
1059
+ tools: pending.tools,
1060
+ hasFunctionCall: current.hasFunctionCall ||
1061
+ pending.events.some((event) => LLMEvent.is.toolCall(event) || LLMEvent.is.toolInputError(event)),
1062
+ };
1063
+ events.push(...pending.events);
1064
+ }
1065
+ const lifecycle = Lifecycle.finish(current.lifecycle, events, {
1066
+ reason: {
1067
+ normalized: mapFinishReason(event, current.hasFunctionCall),
1068
+ raw: event.response?.incomplete_details?.reason,
1069
+ },
1070
+ usage: mapUsage(event.response?.usage, current.providerMetadataKey),
1071
+ providerMetadata: event.response?.id || event.response?.service_tier
1072
+ ? providerMetadata(current, {
1073
+ responseId: event.response.id,
1074
+ serviceTier: event.response.service_tier,
1075
+ })
1076
+ : undefined,
1077
+ });
1078
+ return [{ ...current, lifecycle }, events];
1079
+ });
1080
+ // Build the prettiest summary available from whatever the provider supplied.
1081
+ // When both code and message are present, prefix the code so consumers see
1082
+ // the failure mode (e.g. `rate_limit_exceeded: Slow down`) instead of just
1083
+ // the bare message — production rate limits and context-length failures used
1084
+ // to be indistinguishable from generic stream drops. Returns undefined when
1085
+ // the payload carries no usable summary.
1086
+ const providerErrorMessage = (event, nested) => {
1087
+ const message = event.message || nested?.message || undefined;
1088
+ const code = event.code || nested?.code || undefined;
1089
+ if (message && code)
1090
+ return `${code}: ${message}`;
1091
+ return message || code;
1092
+ };
1093
+ export const providerFailure = (event, fallback, body = ProviderShared.encodeJson(event)) => {
1094
+ const nested = event.error ?? event.response?.error ?? undefined;
1095
+ const summary = providerErrorMessage(event, nested);
1096
+ const message = summary ?? (body === "{}" ? fallback : body);
1097
+ const status = typeof event.status === "number"
1098
+ ? event.status
1099
+ : typeof event.status_code === "number"
1100
+ ? event.status_code
1101
+ : undefined;
1102
+ const reason = event.type === "error" &&
1103
+ event.error === undefined &&
1104
+ event.response === undefined &&
1105
+ summary === undefined &&
1106
+ status === undefined
1107
+ ? new ProviderInternalError({ message, body })
1108
+ : classifyProviderFailure({ message, status, rawBody: body });
1109
+ return new AIError({ reason });
1110
+ };
1111
+ // Callers must pass events through `normalize` first. The OpenAPI requires
1112
+ // string IDs but imposes no minLength; empty is not missing.
1113
+ export const step = (state, event) => {
1114
+ if (event.type === "response.output_text.delta" || event.type === "response.output_text.done") {
1115
+ if (event.item_id === undefined)
1116
+ return ProviderShared.eventError(state.id, `${event.type} is missing item_id`);
1117
+ return Effect.succeed(event.type === "response.output_text.delta"
1118
+ ? onOutputTextDelta(state, event, event.item_id)
1119
+ : onOutputTextDone(state, event, event.item_id));
1120
+ }
1121
+ if (event.type === "response.refusal.delta" || event.type === "response.refusal.done") {
1122
+ const value = event.type === "response.refusal.delta" ? event.delta : event.refusal;
1123
+ if (event.item_id === undefined || typeof value !== "string")
1124
+ return ProviderShared.eventError(state.id, `${event.type} is malformed`);
1125
+ return Effect.succeed(event.type === "response.refusal.delta"
1126
+ ? onOutputTextDelta(state, event, event.item_id)
1127
+ : onOutputTextDone(state, { ...event, text: value }, event.item_id));
1128
+ }
1129
+ if (event.type === "response.reasoning.delta" || event.type === "response.reasoning_summary_text.delta") {
1130
+ if (event.item_id === undefined)
1131
+ return ProviderShared.eventError(state.id, `${event.type} is missing item_id`);
1132
+ return Effect.succeed(onReasoningDelta(state, event, event.item_id));
1133
+ }
1134
+ if (event.type === "response.reasoning.done" ||
1135
+ event.type === "response.reasoning_summary_text.done" ||
1136
+ event.type === "response.reasoning_text.done") {
1137
+ if (event.item_id === undefined)
1138
+ return ProviderShared.eventError(state.id, `${event.type} is missing item_id`);
1139
+ return Effect.succeed(onReasoningDone(state, event, event.item_id));
1140
+ }
1141
+ if (event.type === "response.reasoning_summary_part.added")
1142
+ return event.item_id !== undefined
1143
+ ? Effect.succeed(onReasoningSummaryPartAdded(state, event))
1144
+ : ProviderShared.eventError(state.id, `${event.type} is missing item_id`);
1145
+ if (event.type === "response.reasoning_summary_part.done")
1146
+ return event.item_id !== undefined
1147
+ ? Effect.succeed(onReasoningSummaryPartDone(state, event))
1148
+ : ProviderShared.eventError(state.id, `${event.type} is missing item_id`);
1149
+ if (event.type === "response.output_item.added") {
1150
+ if (event.item?.type === "reasoning" &&
1151
+ state.reasoningItems[event.item.id] === undefined &&
1152
+ state.lifecycle.reasoning.size > 0)
1153
+ return ProviderShared.eventError(state.id, `${event.type} started reasoning before the previous item ended`);
1154
+ return Effect.succeed(onOutputItemAdded(event.output_index !== undefined && event.item
1155
+ ? { ...state, outputItems: { ...state.outputItems, [event.output_index]: event.item.id } }
1156
+ : state, event));
1157
+ }
1158
+ if (event.type === "response.function_call_arguments.delta" || event.type === "response.function_call_arguments.done")
1159
+ return event.item_id !== undefined
1160
+ ? onFunctionCallArgumentsDelta(state, event)
1161
+ : ProviderShared.eventError(state.id, `${event.type} is missing item_id`);
1162
+ if (event.type === "response.output_item.done")
1163
+ return onOutputItemDone(state, event.item);
1164
+ if (event.type === "response.completed" || event.type === "response.incomplete")
1165
+ return onResponseFinish(state, event);
1166
+ if (event.type === "response.failed")
1167
+ return providerFailure(event, `${state.name} response failed`);
1168
+ if (event.type === "error")
1169
+ return decodeKnownErrorEvent(event).pipe(Effect.mapError((cause) => ProviderShared.eventError(state.id, `${state.name} returned a malformed error event`, ProviderShared.encodeJson(event), cause)), Effect.flatMap(() => providerFailure(event, `${state.name} stream error`)));
1170
+ return Effect.succeed([state, NO_EVENTS]);
1171
+ };
1172
+ // =============================================================================
1173
+ // Protocol
1174
+ // =============================================================================
1175
+ /**
1176
+ * The provider-neutral Open Responses protocol. Provider-specific Responses
1177
+ * implementations compose this baseline with their own tools and event variants.
1178
+ */
1179
+ export const initial = (request, adapter = BASE_ADAPTER) => ({
1180
+ provider: request.model.provider,
1181
+ completedCompactions: new Set(),
1182
+ id: adapter.id,
1183
+ name: adapter.name,
1184
+ providerMetadataKey: metadataKey(request.model),
1185
+ hasFunctionCall: false,
1186
+ tools: ToolStream.empty(),
1187
+ lifecycle: Lifecycle.initial(),
1188
+ outputItems: {},
1189
+ message: undefined,
1190
+ reasoningItems: {},
1191
+ });
1192
+ export const protocol = Protocol.make({
1193
+ id: ADAPTER,
1194
+ body: {
1195
+ schema: OpenResponsesBody,
1196
+ from: fromRequest,
1197
+ },
1198
+ stream: {
1199
+ event: Protocol.jsonEvent(Event),
1200
+ initial,
1201
+ step: (state, event) => step(state, normalize(state, event)),
1202
+ terminal,
1203
+ },
1204
+ });
1205
+ export const httpTransport = HttpTransport.sseJson.with();
1206
+ export * as OpenResponses from "./open-responses.js";