@opencode/ai 0.0.0-reserved → 2.0.1

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