@draht/ai 2026.6.11 → 2026.7.7-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 (557) hide show
  1. package/dist/api/anthropic-messages.d.ts +71 -0
  2. package/dist/api/anthropic-messages.d.ts.map +1 -0
  3. package/dist/api/anthropic-messages.js +972 -0
  4. package/dist/api/anthropic-messages.js.map +1 -0
  5. package/dist/api/anthropic-messages.lazy.d.ts +3 -0
  6. package/dist/api/anthropic-messages.lazy.d.ts.map +1 -0
  7. package/dist/api/anthropic-messages.lazy.js +3 -0
  8. package/dist/api/anthropic-messages.lazy.js.map +1 -0
  9. package/dist/api/azure-openai-responses.d.ts +15 -0
  10. package/dist/api/azure-openai-responses.d.ts.map +1 -0
  11. package/dist/api/azure-openai-responses.js +222 -0
  12. package/dist/api/azure-openai-responses.js.map +1 -0
  13. package/dist/api/azure-openai-responses.lazy.d.ts +3 -0
  14. package/dist/api/azure-openai-responses.lazy.d.ts.map +1 -0
  15. package/dist/api/azure-openai-responses.lazy.js +3 -0
  16. package/dist/api/azure-openai-responses.lazy.js.map +1 -0
  17. package/dist/api/bedrock-converse-stream.d.ts +38 -0
  18. package/dist/api/bedrock-converse-stream.d.ts.map +1 -0
  19. package/dist/api/bedrock-converse-stream.js +876 -0
  20. package/dist/api/bedrock-converse-stream.js.map +1 -0
  21. package/dist/api/bedrock-converse-stream.lazy.d.ts +9 -0
  22. package/dist/api/bedrock-converse-stream.lazy.d.ts.map +1 -0
  23. package/dist/api/bedrock-converse-stream.lazy.js +30 -0
  24. package/dist/api/bedrock-converse-stream.lazy.js.map +1 -0
  25. package/dist/{providers → api}/cloudflare.d.ts +0 -4
  26. package/dist/api/cloudflare.d.ts.map +1 -0
  27. package/dist/{providers → api}/cloudflare.js +0 -17
  28. package/dist/api/cloudflare.js.map +1 -0
  29. package/dist/api/github-copilot-headers.d.ts.map +1 -0
  30. package/dist/api/github-copilot-headers.js.map +1 -0
  31. package/dist/api/google-generative-ai.d.ts +13 -0
  32. package/dist/api/google-generative-ai.d.ts.map +1 -0
  33. package/dist/api/google-generative-ai.js +407 -0
  34. package/dist/api/google-generative-ai.js.map +1 -0
  35. package/dist/api/google-generative-ai.lazy.d.ts +3 -0
  36. package/dist/api/google-generative-ai.lazy.d.ts.map +1 -0
  37. package/dist/api/google-generative-ai.lazy.js +3 -0
  38. package/dist/api/google-generative-ai.lazy.js.map +1 -0
  39. package/dist/api/google-shared.d.ts.map +1 -0
  40. package/dist/api/google-shared.js.map +1 -0
  41. package/dist/api/google-vertex.d.ts +15 -0
  42. package/dist/api/google-vertex.d.ts.map +1 -0
  43. package/dist/api/google-vertex.js +456 -0
  44. package/dist/api/google-vertex.js.map +1 -0
  45. package/dist/api/google-vertex.lazy.d.ts +3 -0
  46. package/dist/api/google-vertex.lazy.d.ts.map +1 -0
  47. package/dist/api/google-vertex.lazy.js +3 -0
  48. package/dist/api/google-vertex.lazy.js.map +1 -0
  49. package/dist/api/lazy.d.ts +15 -0
  50. package/dist/api/lazy.d.ts.map +1 -0
  51. package/dist/api/lazy.js +59 -0
  52. package/dist/api/lazy.js.map +1 -0
  53. package/dist/api/mistral-conversations.d.ts +25 -0
  54. package/dist/api/mistral-conversations.d.ts.map +1 -0
  55. package/dist/api/mistral-conversations.js +555 -0
  56. package/dist/api/mistral-conversations.js.map +1 -0
  57. package/dist/api/mistral-conversations.lazy.d.ts +3 -0
  58. package/dist/api/mistral-conversations.lazy.d.ts.map +1 -0
  59. package/dist/api/mistral-conversations.lazy.js +3 -0
  60. package/dist/api/mistral-conversations.lazy.js.map +1 -0
  61. package/dist/{providers → api}/openai-codex-responses.d.ts +2 -2
  62. package/dist/api/openai-codex-responses.d.ts.map +1 -0
  63. package/dist/{providers → api}/openai-codex-responses.js +149 -93
  64. package/dist/api/openai-codex-responses.js.map +1 -0
  65. package/dist/api/openai-codex-responses.lazy.d.ts +3 -0
  66. package/dist/api/openai-codex-responses.lazy.d.ts.map +1 -0
  67. package/dist/api/openai-codex-responses.lazy.js +3 -0
  68. package/dist/api/openai-codex-responses.lazy.js.map +1 -0
  69. package/dist/{providers → api}/openai-completions.d.ts +2 -2
  70. package/dist/api/openai-completions.d.ts.map +1 -0
  71. package/dist/{providers → api}/openai-completions.js +155 -72
  72. package/dist/api/openai-completions.js.map +1 -0
  73. package/dist/api/openai-completions.lazy.d.ts +3 -0
  74. package/dist/api/openai-completions.lazy.d.ts.map +1 -0
  75. package/dist/api/openai-completions.lazy.js +3 -0
  76. package/dist/api/openai-completions.lazy.js.map +1 -0
  77. package/dist/api/openai-prompt-cache.d.ts.map +1 -0
  78. package/dist/api/openai-prompt-cache.js.map +1 -0
  79. package/dist/api/openai-responses-shared.d.ts.map +1 -0
  80. package/dist/{providers → api}/openai-responses-shared.js +190 -204
  81. package/dist/api/openai-responses-shared.js.map +1 -0
  82. package/dist/{providers → api}/openai-responses.d.ts +2 -2
  83. package/dist/api/openai-responses.d.ts.map +1 -0
  84. package/dist/{providers → api}/openai-responses.js +35 -41
  85. package/dist/api/openai-responses.js.map +1 -0
  86. package/dist/api/openai-responses.lazy.d.ts +3 -0
  87. package/dist/api/openai-responses.lazy.d.ts.map +1 -0
  88. package/dist/api/openai-responses.lazy.js +3 -0
  89. package/dist/api/openai-responses.lazy.js.map +1 -0
  90. package/dist/api/openrouter-images.d.ts +3 -0
  91. package/dist/api/openrouter-images.d.ts.map +1 -0
  92. package/dist/{providers/images/openrouter.js → api/openrouter-images.js} +7 -9
  93. package/dist/api/openrouter-images.js.map +1 -0
  94. package/dist/api/openrouter-images.lazy.d.ts +3 -0
  95. package/dist/api/openrouter-images.lazy.d.ts.map +1 -0
  96. package/dist/api/openrouter-images.lazy.js +4 -0
  97. package/dist/api/openrouter-images.lazy.js.map +1 -0
  98. package/dist/{providers → api}/simple-options.d.ts +3 -2
  99. package/dist/api/simple-options.d.ts.map +1 -0
  100. package/dist/{providers → api}/simple-options.js +12 -2
  101. package/dist/api/simple-options.js.map +1 -0
  102. package/dist/api/transform-messages.d.ts.map +1 -0
  103. package/dist/{providers → api}/transform-messages.js +4 -1
  104. package/dist/api/transform-messages.js.map +1 -0
  105. package/dist/auth/context.d.ts +7 -0
  106. package/dist/auth/context.d.ts.map +1 -0
  107. package/dist/auth/context.js +42 -0
  108. package/dist/auth/context.js.map +1 -0
  109. package/dist/auth/credential-store.d.ts +16 -0
  110. package/dist/auth/credential-store.d.ts.map +1 -0
  111. package/dist/auth/credential-store.js +37 -0
  112. package/dist/auth/credential-store.js.map +1 -0
  113. package/dist/auth/helpers.d.ts +20 -0
  114. package/dist/auth/helpers.d.ts.map +1 -0
  115. package/dist/auth/helpers.js +46 -0
  116. package/dist/auth/helpers.js.map +1 -0
  117. package/dist/auth/resolve.d.ts +26 -0
  118. package/dist/auth/resolve.d.ts.map +1 -0
  119. package/dist/auth/resolve.js +101 -0
  120. package/dist/auth/resolve.js.map +1 -0
  121. package/dist/auth/types.d.ts +180 -0
  122. package/dist/auth/types.d.ts.map +1 -0
  123. package/dist/auth/types.js +2 -0
  124. package/dist/auth/types.js.map +1 -0
  125. package/dist/bedrock-provider.d.ts +2 -2
  126. package/dist/bedrock-provider.d.ts.map +1 -1
  127. package/dist/bedrock-provider.js +3 -3
  128. package/dist/bedrock-provider.js.map +1 -1
  129. package/dist/compat.d.ts +65 -0
  130. package/dist/compat.d.ts.map +1 -0
  131. package/dist/compat.js +182 -0
  132. package/dist/compat.js.map +1 -0
  133. package/dist/env-api-keys.d.ts +5 -5
  134. package/dist/env-api-keys.d.ts.map +1 -1
  135. package/dist/env-api-keys.js +21 -57
  136. package/dist/env-api-keys.js.map +1 -1
  137. package/dist/image-models.generated.d.ts +64 -19
  138. package/dist/image-models.generated.d.ts.map +1 -1
  139. package/dist/image-models.generated.js +90 -45
  140. package/dist/image-models.generated.js.map +1 -1
  141. package/dist/images-models.d.ts +93 -0
  142. package/dist/images-models.d.ts.map +1 -0
  143. package/dist/images-models.js +141 -0
  144. package/dist/images-models.js.map +1 -0
  145. package/dist/index.d.ts +17 -18
  146. package/dist/index.d.ts.map +1 -1
  147. package/dist/index.js +7 -8
  148. package/dist/index.js.map +1 -1
  149. package/dist/legacy-api-aliases.d.ts +42 -0
  150. package/dist/legacy-api-aliases.d.ts.map +1 -0
  151. package/dist/legacy-api-aliases.js +49 -0
  152. package/dist/legacy-api-aliases.js.map +1 -0
  153. package/dist/models.d.ts +133 -9
  154. package/dist/models.d.ts.map +1 -1
  155. package/dist/models.generated.d.ts +3425 -669
  156. package/dist/models.generated.d.ts.map +1 -1
  157. package/dist/models.generated.js +70 -16932
  158. package/dist/models.generated.js.map +1 -1
  159. package/dist/models.js +185 -18
  160. package/dist/models.js.map +1 -1
  161. package/dist/providers/all.d.ts +21 -0
  162. package/dist/providers/all.d.ts.map +1 -0
  163. package/dist/providers/all.js +114 -0
  164. package/dist/providers/all.js.map +1 -0
  165. package/dist/providers/amazon-bedrock.d.ts +2 -37
  166. package/dist/providers/amazon-bedrock.d.ts.map +1 -1
  167. package/dist/providers/amazon-bedrock.js +35 -840
  168. package/dist/providers/amazon-bedrock.js.map +1 -1
  169. package/dist/providers/amazon-bedrock.models.d.ts +1875 -0
  170. package/dist/providers/amazon-bedrock.models.d.ts.map +1 -0
  171. package/dist/providers/amazon-bedrock.models.js +1829 -0
  172. package/dist/providers/amazon-bedrock.models.js.map +1 -0
  173. package/dist/providers/ant-ling.d.ts +3 -0
  174. package/dist/providers/ant-ling.d.ts.map +1 -0
  175. package/dist/providers/ant-ling.js +15 -0
  176. package/dist/providers/ant-ling.js.map +1 -0
  177. package/dist/providers/ant-ling.models.d.ts +86 -0
  178. package/dist/providers/ant-ling.models.d.ts.map +1 -0
  179. package/dist/providers/ant-ling.models.js +60 -0
  180. package/dist/providers/ant-ling.models.js.map +1 -0
  181. package/dist/providers/anthropic.d.ts +2 -70
  182. package/dist/providers/anthropic.d.ts.map +1 -1
  183. package/dist/providers/anthropic.js +17 -957
  184. package/dist/providers/anthropic.js.map +1 -1
  185. package/dist/providers/anthropic.models.d.ts +274 -0
  186. package/dist/providers/anthropic.models.d.ts.map +1 -0
  187. package/dist/providers/anthropic.models.js +253 -0
  188. package/dist/providers/anthropic.models.js.map +1 -0
  189. package/dist/providers/azure-openai-responses.d.ts +2 -14
  190. package/dist/providers/azure-openai-responses.d.ts.map +1 -1
  191. package/dist/providers/azure-openai-responses.js +11 -220
  192. package/dist/providers/azure-openai-responses.js.map +1 -1
  193. package/dist/providers/azure-openai-responses.models.d.ts +804 -0
  194. package/dist/providers/azure-openai-responses.models.d.ts.map +1 -0
  195. package/dist/providers/azure-openai-responses.models.js +743 -0
  196. package/dist/providers/azure-openai-responses.models.js.map +1 -0
  197. package/dist/providers/cerebras.d.ts +3 -0
  198. package/dist/providers/cerebras.d.ts.map +1 -0
  199. package/dist/providers/cerebras.js +15 -0
  200. package/dist/providers/cerebras.js.map +1 -0
  201. package/dist/providers/cerebras.models.d.ts +66 -0
  202. package/dist/providers/cerebras.models.d.ts.map +1 -0
  203. package/dist/providers/cerebras.models.js +59 -0
  204. package/dist/providers/cerebras.models.js.map +1 -0
  205. package/dist/providers/cloudflare-ai-gateway.d.ts +3 -0
  206. package/dist/providers/cloudflare-ai-gateway.d.ts.map +1 -0
  207. package/dist/providers/cloudflare-ai-gateway.js +20 -0
  208. package/dist/providers/cloudflare-ai-gateway.js.map +1 -0
  209. package/dist/providers/cloudflare-ai-gateway.models.d.ts +786 -0
  210. package/dist/providers/cloudflare-ai-gateway.models.d.ts.map +1 -0
  211. package/dist/providers/cloudflare-ai-gateway.models.js +684 -0
  212. package/dist/providers/cloudflare-ai-gateway.models.js.map +1 -0
  213. package/dist/providers/cloudflare-auth.d.ts +4 -0
  214. package/dist/providers/cloudflare-auth.d.ts.map +1 -0
  215. package/dist/providers/cloudflare-auth.js +85 -0
  216. package/dist/providers/cloudflare-auth.js.map +1 -0
  217. package/dist/providers/cloudflare-workers-ai.d.ts +3 -0
  218. package/dist/providers/cloudflare-workers-ai.d.ts.map +1 -0
  219. package/dist/providers/cloudflare-workers-ai.js +14 -0
  220. package/dist/providers/cloudflare-workers-ai.js.map +1 -0
  221. package/dist/providers/cloudflare-workers-ai.models.d.ts +302 -0
  222. package/dist/providers/cloudflare-workers-ai.models.d.ts.map +1 -0
  223. package/dist/providers/cloudflare-workers-ai.models.js +239 -0
  224. package/dist/providers/cloudflare-workers-ai.models.js.map +1 -0
  225. package/dist/providers/deepseek.d.ts +3 -0
  226. package/dist/providers/deepseek.d.ts.map +1 -0
  227. package/dist/providers/deepseek.js +15 -0
  228. package/dist/providers/deepseek.js.map +1 -0
  229. package/dist/providers/deepseek.models.d.ts +63 -0
  230. package/dist/providers/deepseek.models.d.ts.map +1 -0
  231. package/dist/providers/deepseek.models.js +43 -0
  232. package/dist/providers/deepseek.models.js.map +1 -0
  233. package/dist/providers/faux.d.ts +43 -2
  234. package/dist/providers/faux.d.ts.map +1 -1
  235. package/dist/providers/faux.js +34 -7
  236. package/dist/providers/faux.js.map +1 -1
  237. package/dist/providers/fireworks.d.ts +3 -0
  238. package/dist/providers/fireworks.d.ts.map +1 -0
  239. package/dist/providers/fireworks.js +19 -0
  240. package/dist/providers/fireworks.js.map +1 -0
  241. package/dist/providers/fireworks.models.d.ts +381 -0
  242. package/dist/providers/fireworks.models.d.ts.map +1 -0
  243. package/dist/providers/fireworks.models.js +295 -0
  244. package/dist/providers/fireworks.models.js.map +1 -0
  245. package/dist/providers/github-copilot.d.ts +3 -0
  246. package/dist/providers/github-copilot.d.ts.map +1 -0
  247. package/dist/providers/github-copilot.js +25 -0
  248. package/dist/providers/github-copilot.js.map +1 -0
  249. package/dist/providers/github-copilot.models.d.ts +698 -0
  250. package/dist/providers/github-copilot.models.d.ts.map +1 -0
  251. package/dist/providers/github-copilot.models.js +483 -0
  252. package/dist/providers/github-copilot.models.js.map +1 -0
  253. package/dist/providers/google-vertex.d.ts +2 -14
  254. package/dist/providers/google-vertex.d.ts.map +1 -1
  255. package/dist/providers/google-vertex.js +30 -441
  256. package/dist/providers/google-vertex.js.map +1 -1
  257. package/dist/providers/google-vertex.models.d.ts +202 -0
  258. package/dist/providers/google-vertex.models.d.ts.map +1 -0
  259. package/dist/providers/google-vertex.models.js +182 -0
  260. package/dist/providers/google-vertex.models.js.map +1 -0
  261. package/dist/providers/google.d.ts +2 -12
  262. package/dist/providers/google.d.ts.map +1 -1
  263. package/dist/providers/google.js +12 -399
  264. package/dist/providers/google.js.map +1 -1
  265. package/dist/providers/google.models.d.ts +328 -0
  266. package/dist/providers/google.models.d.ts.map +1 -0
  267. package/dist/providers/google.models.js +288 -0
  268. package/dist/providers/google.models.js.map +1 -0
  269. package/dist/providers/groq.d.ts +3 -0
  270. package/dist/providers/groq.d.ts.map +1 -0
  271. package/dist/providers/groq.js +15 -0
  272. package/dist/providers/groq.js.map +1 -0
  273. package/dist/providers/groq.models.d.ts +128 -0
  274. package/dist/providers/groq.models.d.ts.map +1 -0
  275. package/dist/providers/groq.models.js +125 -0
  276. package/dist/providers/groq.models.js.map +1 -0
  277. package/dist/providers/huggingface.d.ts +3 -0
  278. package/dist/providers/huggingface.d.ts.map +1 -0
  279. package/dist/providers/huggingface.js +15 -0
  280. package/dist/providers/huggingface.js.map +1 -0
  281. package/dist/providers/huggingface.models.d.ts +983 -0
  282. package/dist/providers/huggingface.models.d.ts.map +1 -0
  283. package/dist/providers/huggingface.models.js +887 -0
  284. package/dist/providers/huggingface.models.js.map +1 -0
  285. package/dist/providers/images/register-builtins.d.ts.map +1 -1
  286. package/dist/providers/images/register-builtins.js +2 -2
  287. package/dist/providers/images/register-builtins.js.map +1 -1
  288. package/dist/providers/kimi-coding.d.ts +3 -0
  289. package/dist/providers/kimi-coding.d.ts.map +1 -0
  290. package/dist/providers/kimi-coding.js +15 -0
  291. package/dist/providers/kimi-coding.js.map +1 -0
  292. package/dist/providers/kimi-coding.models.d.ts +63 -0
  293. package/dist/providers/kimi-coding.models.d.ts.map +1 -0
  294. package/dist/providers/kimi-coding.models.js +59 -0
  295. package/dist/providers/kimi-coding.models.js.map +1 -0
  296. package/dist/providers/minimax-cn.d.ts +3 -0
  297. package/dist/providers/minimax-cn.d.ts.map +1 -0
  298. package/dist/providers/minimax-cn.js +15 -0
  299. package/dist/providers/minimax-cn.js.map +1 -0
  300. package/dist/providers/minimax-cn.models.d.ts +54 -0
  301. package/dist/providers/minimax-cn.models.d.ts.map +1 -0
  302. package/dist/providers/minimax-cn.models.js +56 -0
  303. package/dist/providers/minimax-cn.models.js.map +1 -0
  304. package/dist/providers/minimax.d.ts +3 -0
  305. package/dist/providers/minimax.d.ts.map +1 -0
  306. package/dist/providers/minimax.js +15 -0
  307. package/dist/providers/minimax.js.map +1 -0
  308. package/dist/providers/minimax.models.d.ts +54 -0
  309. package/dist/providers/minimax.models.d.ts.map +1 -0
  310. package/dist/providers/minimax.models.js +56 -0
  311. package/dist/providers/minimax.models.js.map +1 -0
  312. package/dist/providers/mistral.d.ts +2 -24
  313. package/dist/providers/mistral.d.ts.map +1 -1
  314. package/dist/providers/mistral.js +12 -531
  315. package/dist/providers/mistral.js.map +1 -1
  316. package/dist/providers/mistral.models.d.ts +513 -0
  317. package/dist/providers/mistral.models.d.ts.map +1 -0
  318. package/dist/providers/mistral.models.js +515 -0
  319. package/dist/providers/mistral.models.js.map +1 -0
  320. package/dist/providers/moonshotai-cn.d.ts +3 -0
  321. package/dist/providers/moonshotai-cn.d.ts.map +1 -0
  322. package/dist/providers/moonshotai-cn.js +15 -0
  323. package/dist/providers/moonshotai-cn.js.map +1 -0
  324. package/dist/providers/moonshotai-cn.models.d.ts +234 -0
  325. package/dist/providers/moonshotai-cn.models.d.ts.map +1 -0
  326. package/dist/providers/moonshotai-cn.models.js +169 -0
  327. package/dist/providers/moonshotai-cn.models.js.map +1 -0
  328. package/dist/providers/moonshotai.d.ts +3 -0
  329. package/dist/providers/moonshotai.d.ts.map +1 -0
  330. package/dist/providers/moonshotai.js +15 -0
  331. package/dist/providers/moonshotai.js.map +1 -0
  332. package/dist/providers/moonshotai.models.d.ts +234 -0
  333. package/dist/providers/moonshotai.models.d.ts.map +1 -0
  334. package/dist/providers/moonshotai.models.js +169 -0
  335. package/dist/providers/moonshotai.models.js.map +1 -0
  336. package/dist/providers/nvidia.d.ts +3 -0
  337. package/dist/providers/nvidia.d.ts.map +1 -0
  338. package/dist/providers/nvidia.js +15 -0
  339. package/dist/providers/nvidia.js.map +1 -0
  340. package/dist/providers/nvidia.models.d.ts +563 -0
  341. package/dist/providers/nvidia.models.d.ts.map +1 -0
  342. package/dist/providers/nvidia.models.js +385 -0
  343. package/dist/providers/nvidia.models.js.map +1 -0
  344. package/dist/providers/openai-codex.d.ts +3 -0
  345. package/dist/providers/openai-codex.d.ts.map +1 -0
  346. package/dist/providers/openai-codex.js +18 -0
  347. package/dist/providers/openai-codex.js.map +1 -0
  348. package/dist/providers/openai-codex.models.d.ts +87 -0
  349. package/dist/providers/openai-codex.models.d.ts.map +1 -0
  350. package/dist/providers/openai-codex.models.js +77 -0
  351. package/dist/providers/openai-codex.models.js.map +1 -0
  352. package/dist/providers/openai.d.ts +3 -0
  353. package/dist/providers/openai.d.ts.map +1 -0
  354. package/dist/providers/openai.js +15 -0
  355. package/dist/providers/openai.js.map +1 -0
  356. package/dist/providers/openai.models.d.ts +805 -0
  357. package/dist/providers/openai.models.d.ts.map +1 -0
  358. package/dist/providers/openai.models.js +743 -0
  359. package/dist/providers/openai.models.js.map +1 -0
  360. package/dist/providers/opencode-go.d.ts +3 -0
  361. package/dist/providers/opencode-go.d.ts.map +1 -0
  362. package/dist/providers/opencode-go.js +18 -0
  363. package/dist/providers/opencode-go.js.map +1 -0
  364. package/dist/providers/opencode-go.models.d.ts +309 -0
  365. package/dist/providers/opencode-go.models.d.ts.map +1 -0
  366. package/dist/providers/opencode-go.models.js +240 -0
  367. package/dist/providers/opencode-go.models.js.map +1 -0
  368. package/dist/providers/opencode.d.ts +3 -0
  369. package/dist/providers/opencode.d.ts.map +1 -0
  370. package/dist/providers/opencode.js +22 -0
  371. package/dist/providers/opencode.js.map +1 -0
  372. package/dist/providers/opencode.models.d.ts +1086 -0
  373. package/dist/providers/opencode.models.d.ts.map +1 -0
  374. package/dist/providers/opencode.models.js +906 -0
  375. package/dist/providers/opencode.models.js.map +1 -0
  376. package/dist/providers/openrouter-images.d.ts +3 -0
  377. package/dist/providers/openrouter-images.d.ts.map +1 -0
  378. package/dist/providers/openrouter-images.js +14 -0
  379. package/dist/providers/openrouter-images.js.map +1 -0
  380. package/dist/providers/openrouter.d.ts +3 -0
  381. package/dist/providers/openrouter.d.ts.map +1 -0
  382. package/dist/providers/openrouter.js +15 -0
  383. package/dist/providers/openrouter.js.map +1 -0
  384. package/dist/providers/openrouter.models.d.ts +5528 -0
  385. package/dist/providers/openrouter.models.d.ts.map +1 -0
  386. package/dist/providers/openrouter.models.js +4742 -0
  387. package/dist/providers/openrouter.models.js.map +1 -0
  388. package/dist/providers/together.d.ts +3 -0
  389. package/dist/providers/together.d.ts.map +1 -0
  390. package/dist/providers/together.js +15 -0
  391. package/dist/providers/together.js.map +1 -0
  392. package/dist/providers/together.models.d.ts +598 -0
  393. package/dist/providers/together.models.d.ts.map +1 -0
  394. package/dist/providers/together.models.js +380 -0
  395. package/dist/providers/together.models.js.map +1 -0
  396. package/dist/providers/vercel-ai-gateway.d.ts +3 -0
  397. package/dist/providers/vercel-ai-gateway.d.ts.map +1 -0
  398. package/dist/providers/vercel-ai-gateway.js +15 -0
  399. package/dist/providers/vercel-ai-gateway.js.map +1 -0
  400. package/dist/providers/vercel-ai-gateway.models.d.ts +3254 -0
  401. package/dist/providers/vercel-ai-gateway.models.d.ts.map +1 -0
  402. package/dist/providers/vercel-ai-gateway.models.js +3206 -0
  403. package/dist/providers/vercel-ai-gateway.models.js.map +1 -0
  404. package/dist/providers/xai.d.ts +3 -0
  405. package/dist/providers/xai.d.ts.map +1 -0
  406. package/dist/providers/xai.js +15 -0
  407. package/dist/providers/xai.js.map +1 -0
  408. package/dist/providers/xai.models.d.ts +157 -0
  409. package/dist/providers/xai.models.d.ts.map +1 -0
  410. package/dist/providers/xai.models.js +131 -0
  411. package/dist/providers/xai.models.js.map +1 -0
  412. package/dist/providers/xiaomi-token-plan-ams.d.ts +3 -0
  413. package/dist/providers/xiaomi-token-plan-ams.d.ts.map +1 -0
  414. package/dist/providers/xiaomi-token-plan-ams.js +15 -0
  415. package/dist/providers/xiaomi-token-plan-ams.js.map +1 -0
  416. package/dist/providers/xiaomi-token-plan-ams.models.d.ts +108 -0
  417. package/dist/providers/xiaomi-token-plan-ams.models.d.ts.map +1 -0
  418. package/dist/providers/xiaomi-token-plan-ams.models.js +95 -0
  419. package/dist/providers/xiaomi-token-plan-ams.models.js.map +1 -0
  420. package/dist/providers/xiaomi-token-plan-cn.d.ts +3 -0
  421. package/dist/providers/xiaomi-token-plan-cn.d.ts.map +1 -0
  422. package/dist/providers/xiaomi-token-plan-cn.js +15 -0
  423. package/dist/providers/xiaomi-token-plan-cn.js.map +1 -0
  424. package/dist/providers/xiaomi-token-plan-cn.models.d.ts +108 -0
  425. package/dist/providers/xiaomi-token-plan-cn.models.d.ts.map +1 -0
  426. package/dist/providers/xiaomi-token-plan-cn.models.js +95 -0
  427. package/dist/providers/xiaomi-token-plan-cn.models.js.map +1 -0
  428. package/dist/providers/xiaomi-token-plan-sgp.d.ts +3 -0
  429. package/dist/providers/xiaomi-token-plan-sgp.d.ts.map +1 -0
  430. package/dist/providers/xiaomi-token-plan-sgp.js +15 -0
  431. package/dist/providers/xiaomi-token-plan-sgp.js.map +1 -0
  432. package/dist/providers/xiaomi-token-plan-sgp.models.d.ts +108 -0
  433. package/dist/providers/xiaomi-token-plan-sgp.models.d.ts.map +1 -0
  434. package/dist/providers/xiaomi-token-plan-sgp.models.js +95 -0
  435. package/dist/providers/xiaomi-token-plan-sgp.models.js.map +1 -0
  436. package/dist/providers/xiaomi.d.ts +3 -0
  437. package/dist/providers/xiaomi.d.ts.map +1 -0
  438. package/dist/providers/xiaomi.js +15 -0
  439. package/dist/providers/xiaomi.js.map +1 -0
  440. package/dist/providers/xiaomi.models.d.ts +129 -0
  441. package/dist/providers/xiaomi.models.d.ts.map +1 -0
  442. package/dist/providers/xiaomi.models.js +113 -0
  443. package/dist/providers/xiaomi.models.js.map +1 -0
  444. package/dist/providers/zai-coding-cn.d.ts +3 -0
  445. package/dist/providers/zai-coding-cn.d.ts.map +1 -0
  446. package/dist/providers/zai-coding-cn.js +15 -0
  447. package/dist/providers/zai-coding-cn.js.map +1 -0
  448. package/dist/providers/zai-coding-cn.models.d.ts +153 -0
  449. package/dist/providers/zai-coding-cn.models.d.ts.map +1 -0
  450. package/dist/providers/zai-coding-cn.models.js +114 -0
  451. package/dist/providers/zai-coding-cn.models.js.map +1 -0
  452. package/dist/providers/zai.d.ts +3 -0
  453. package/dist/providers/zai.d.ts.map +1 -0
  454. package/dist/providers/zai.js +15 -0
  455. package/dist/providers/zai.js.map +1 -0
  456. package/dist/providers/zai.models.d.ts +153 -0
  457. package/dist/providers/zai.models.d.ts.map +1 -0
  458. package/dist/providers/zai.models.js +114 -0
  459. package/dist/providers/zai.models.js.map +1 -0
  460. package/dist/types.d.ts +91 -10
  461. package/dist/types.d.ts.map +1 -1
  462. package/dist/types.js.map +1 -1
  463. package/dist/utils/error-body.d.ts +25 -0
  464. package/dist/utils/error-body.d.ts.map +1 -0
  465. package/dist/utils/error-body.js +109 -0
  466. package/dist/utils/error-body.js.map +1 -0
  467. package/dist/utils/estimate.d.ts +17 -0
  468. package/dist/utils/estimate.d.ts.map +1 -0
  469. package/dist/utils/estimate.js +95 -0
  470. package/dist/utils/estimate.js.map +1 -0
  471. package/dist/utils/headers.d.ts +2 -0
  472. package/dist/utils/headers.d.ts.map +1 -1
  473. package/dist/utils/headers.js +10 -0
  474. package/dist/utils/headers.js.map +1 -1
  475. package/dist/utils/node-http-proxy.d.ts +2 -8
  476. package/dist/utils/node-http-proxy.d.ts.map +1 -1
  477. package/dist/utils/node-http-proxy.js +17 -22
  478. package/dist/utils/node-http-proxy.js.map +1 -1
  479. package/dist/utils/oauth/anthropic.d.ts +2 -0
  480. package/dist/utils/oauth/anthropic.d.ts.map +1 -1
  481. package/dist/utils/oauth/anthropic.js +33 -1
  482. package/dist/utils/oauth/anthropic.js.map +1 -1
  483. package/dist/utils/oauth/device-code.d.ts +2 -0
  484. package/dist/utils/oauth/device-code.d.ts.map +1 -1
  485. package/dist/utils/oauth/device-code.js +15 -2
  486. package/dist/utils/oauth/device-code.js.map +1 -1
  487. package/dist/utils/oauth/github-copilot.d.ts +2 -0
  488. package/dist/utils/oauth/github-copilot.d.ts.map +1 -1
  489. package/dist/utils/oauth/github-copilot.js +102 -11
  490. package/dist/utils/oauth/github-copilot.js.map +1 -1
  491. package/dist/utils/oauth/load.d.ts +5 -0
  492. package/dist/utils/oauth/load.d.ts.map +1 -0
  493. package/dist/utils/oauth/load.js +22 -0
  494. package/dist/utils/oauth/load.js.map +1 -0
  495. package/dist/utils/oauth/openai-codex.d.ts +2 -0
  496. package/dist/utils/oauth/openai-codex.d.ts.map +1 -1
  497. package/dist/utils/oauth/openai-codex.js +51 -1
  498. package/dist/utils/oauth/openai-codex.js.map +1 -1
  499. package/dist/utils/overflow.d.ts +2 -1
  500. package/dist/utils/overflow.d.ts.map +1 -1
  501. package/dist/utils/overflow.js +6 -2
  502. package/dist/utils/overflow.js.map +1 -1
  503. package/dist/utils/provider-env.d.ts +7 -0
  504. package/dist/utils/provider-env.d.ts.map +1 -0
  505. package/dist/utils/provider-env.js +44 -0
  506. package/dist/utils/provider-env.js.map +1 -0
  507. package/dist/utils/retry.d.ts +12 -0
  508. package/dist/utils/retry.d.ts.map +1 -0
  509. package/dist/utils/retry.js +87 -0
  510. package/dist/utils/retry.js.map +1 -0
  511. package/dist/utils/validation.d.ts.map +1 -1
  512. package/dist/utils/validation.js +9 -21
  513. package/dist/utils/validation.js.map +1 -1
  514. package/package.json +12 -31
  515. package/dist/api-registry.d.ts +0 -20
  516. package/dist/api-registry.d.ts.map +0 -1
  517. package/dist/api-registry.js +0 -44
  518. package/dist/api-registry.js.map +0 -1
  519. package/dist/providers/cloudflare.d.ts.map +0 -1
  520. package/dist/providers/cloudflare.js.map +0 -1
  521. package/dist/providers/github-copilot-headers.d.ts.map +0 -1
  522. package/dist/providers/github-copilot-headers.js.map +0 -1
  523. package/dist/providers/google-shared.d.ts.map +0 -1
  524. package/dist/providers/google-shared.js.map +0 -1
  525. package/dist/providers/images/openrouter.d.ts +0 -3
  526. package/dist/providers/images/openrouter.d.ts.map +0 -1
  527. package/dist/providers/images/openrouter.js.map +0 -1
  528. package/dist/providers/openai-codex-responses.d.ts.map +0 -1
  529. package/dist/providers/openai-codex-responses.js.map +0 -1
  530. package/dist/providers/openai-completions.d.ts.map +0 -1
  531. package/dist/providers/openai-completions.js.map +0 -1
  532. package/dist/providers/openai-prompt-cache.d.ts.map +0 -1
  533. package/dist/providers/openai-prompt-cache.js.map +0 -1
  534. package/dist/providers/openai-responses-shared.d.ts.map +0 -1
  535. package/dist/providers/openai-responses-shared.js.map +0 -1
  536. package/dist/providers/openai-responses.d.ts.map +0 -1
  537. package/dist/providers/openai-responses.js.map +0 -1
  538. package/dist/providers/register-builtins.d.ts +0 -35
  539. package/dist/providers/register-builtins.d.ts.map +0 -1
  540. package/dist/providers/register-builtins.js +0 -254
  541. package/dist/providers/register-builtins.js.map +0 -1
  542. package/dist/providers/simple-options.d.ts.map +0 -1
  543. package/dist/providers/simple-options.js.map +0 -1
  544. package/dist/providers/transform-messages.d.ts.map +0 -1
  545. package/dist/providers/transform-messages.js.map +0 -1
  546. package/dist/stream.d.ts +0 -8
  547. package/dist/stream.d.ts.map +0 -1
  548. package/dist/stream.js +0 -39
  549. package/dist/stream.js.map +0 -1
  550. /package/dist/{providers → api}/github-copilot-headers.d.ts +0 -0
  551. /package/dist/{providers → api}/github-copilot-headers.js +0 -0
  552. /package/dist/{providers → api}/google-shared.d.ts +0 -0
  553. /package/dist/{providers → api}/google-shared.js +0 -0
  554. /package/dist/{providers → api}/openai-prompt-cache.d.ts +0 -0
  555. /package/dist/{providers → api}/openai-prompt-cache.js +0 -0
  556. /package/dist/{providers → api}/openai-responses-shared.d.ts +0 -0
  557. /package/dist/{providers → api}/transform-messages.d.ts +0 -0
@@ -0,0 +1,3 @@
1
+ import { lazyApi } from "./lazy.js";
2
+ export const mistralConversationsApi = () => lazyApi(() => import("./mistral-conversations.js"));
3
+ //# sourceMappingURL=mistral-conversations.lazy.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mistral-conversations.lazy.js","sourceRoot":"","sources":["../../src/api/mistral-conversations.lazy.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEpC,MAAM,CAAC,MAAM,uBAAuB,GAAG,GAAoB,EAAE,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,4BAA4B,CAAC,CAAC,CAAC","sourcesContent":["import type { ProviderStreams } from \"../types.ts\";\nimport { lazyApi } from \"./lazy.ts\";\n\nexport const mistralConversationsApi = (): ProviderStreams => lazyApi(() => import(\"./mistral-conversations.ts\"));\n"]}
@@ -6,8 +6,8 @@ export interface OpenAICodexResponsesOptions extends StreamOptions {
6
6
  serviceTier?: ResponseCreateParamsStreaming["service_tier"];
7
7
  textVerbosity?: "low" | "medium" | "high";
8
8
  }
9
- export declare const streamOpenAICodexResponses: StreamFunction<"openai-codex-responses", OpenAICodexResponsesOptions>;
10
- export declare const streamSimpleOpenAICodexResponses: StreamFunction<"openai-codex-responses", SimpleStreamOptions>;
9
+ export declare const stream: StreamFunction<"openai-codex-responses", OpenAICodexResponsesOptions>;
10
+ export declare const streamSimple: StreamFunction<"openai-codex-responses", SimpleStreamOptions>;
11
11
  export interface OpenAICodexWebSocketDebugStats {
12
12
  requests: number;
13
13
  connectionsCreated: number;
@@ -0,0 +1 @@
1
+ {"version":3,"file":"openai-codex-responses.d.ts","sourceRoot":"","sources":["../../src/api/openai-codex-responses.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAEX,6BAA6B,EAG7B,MAAM,yCAAyC,CAAC;AAkBjD,OAAO,KAAK,EAOX,mBAAmB,EACnB,cAAc,EACd,aAAa,EAEb,MAAM,aAAa,CAAC;AA6CrB,MAAM,WAAW,2BAA4B,SAAQ,aAAa;IACjE,eAAe,CAAC,EAAE,MAAM,GAAG,SAAS,GAAG,KAAK,GAAG,QAAQ,GAAG,MAAM,GAAG,OAAO,CAAC;IAC3E,gBAAgB,CAAC,EAAE,MAAM,GAAG,SAAS,GAAG,UAAU,GAAG,KAAK,GAAG,IAAI,GAAG,IAAI,CAAC;IACzE,WAAW,CAAC,EAAE,6BAA6B,CAAC,cAAc,CAAC,CAAC;IAC5D,aAAa,CAAC,EAAE,KAAK,GAAG,QAAQ,GAAG,MAAM,CAAC;CAC1C;AA0ID,eAAO,MAAM,MAAM,EAAE,cAAc,CAAC,wBAAwB,EAAE,2BAA2B,CAwOxF,CAAC;AAEF,eAAO,MAAM,YAAY,EAAE,cAAc,CAAC,wBAAwB,EAAE,mBAAmB,CAmBtF,CAAC;AAkTF,MAAM,WAAW,8BAA8B;IAC9C,QAAQ,EAAE,MAAM,CAAC;IACjB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,iBAAiB,EAAE,MAAM,CAAC;IAC1B,qBAAqB,EAAE,MAAM,CAAC;IAC9B,iBAAiB,EAAE,MAAM,CAAC;IAC1B,mBAAmB,EAAE,MAAM,CAAC;IAC5B,aAAa,EAAE,MAAM,CAAC;IACtB,cAAc,EAAE,MAAM,CAAC;IACvB,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,sBAAsB,CAAC,EAAE,MAAM,CAAC;IAChC,iBAAiB,EAAE,MAAM,CAAC;IAC1B,YAAY,EAAE,MAAM,CAAC;IACrB,uBAAuB,CAAC,EAAE,OAAO,CAAC;IAClC,kBAAkB,CAAC,EAAE,MAAM,CAAC;CAC5B;AA0BD,wBAAgB,iCAAiC,CAAC,SAAS,EAAE,MAAM,GAAG,8BAA8B,GAAG,SAAS,CAG/G;AAED,wBAAgB,mCAAmC,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAQ5E;AAED,wBAAgB,iCAAiC,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAe1E","sourcesContent":["import type * as NodeOs from \"node:os\";\nimport type * as NodeZlib from \"node:zlib\";\nimport type {\n\tTool as OpenAITool,\n\tResponseCreateParamsStreaming,\n\tResponseInput,\n\tResponseStreamEvent,\n} from \"openai/resources/responses/responses.js\";\n\ntype ProcessWithOsBuiltinModule = typeof process & {\n\tgetBuiltinModule?: (id: \"node:os\") => typeof NodeOs;\n};\n\nfunction loadNodeOs(): typeof NodeOs | null {\n\tif (typeof process === \"undefined\" || !(process.versions?.node || process.versions?.bun)) {\n\t\treturn null;\n\t}\n\treturn (process as ProcessWithOsBuiltinModule).getBuiltinModule?.(\"node:os\") ?? null;\n}\n\n// NEVER convert to top-level runtime imports - breaks browser/Vite builds\nconst _os: typeof NodeOs | null = loadNodeOs();\n\nimport { clampThinkingLevel } from \"../models.ts\";\nimport { registerSessionResourceCleanup } from \"../session-resources.ts\";\nimport type {\n\tApi,\n\tAssistantMessage,\n\tContext,\n\tModel,\n\tProviderEnv,\n\tProviderHeaders,\n\tSimpleStreamOptions,\n\tStreamFunction,\n\tStreamOptions,\n\tUsage,\n} from \"../types.ts\";\nimport { combineAbortSignals } from \"../utils/abort-signals.ts\";\nimport {\n\tappendAssistantMessageDiagnostic,\n\tcreateAssistantMessageDiagnostic,\n\tformatThrownValue,\n} from \"../utils/diagnostics.ts\";\nimport { formatProviderError, normalizeProviderError } from \"../utils/error-body.ts\";\nimport { AssistantMessageEventStream } from \"../utils/event-stream.ts\";\nimport { headersToRecord } from \"../utils/headers.ts\";\nimport { resolveHttpProxyUrlForTarget } from \"../utils/node-http-proxy.ts\";\nimport { clampOpenAIPromptCacheKey } from \"./openai-prompt-cache.ts\";\nimport { convertResponsesMessages, convertResponsesTools, processResponsesStream } from \"./openai-responses-shared.ts\";\nimport { buildBaseOptions, clampToXhigh } from \"./simple-options.ts\";\n\n// ============================================================================\n// Configuration\n// ============================================================================\n\nconst DEFAULT_CODEX_BASE_URL = \"https://chatgpt.com/backend-api\";\nconst JWT_CLAIM_PATH = \"https://api.openai.com/auth\" as const;\nconst DEFAULT_MAX_RETRIES = 0;\nconst BASE_DELAY_MS = 1000;\nconst DEFAULT_MAX_RETRY_DELAY_MS = 60_000;\nconst DEFAULT_WEBSOCKET_CONNECT_TIMEOUT_MS = 15_000;\n// The Codex backend accepts zstd-compressed request bodies on the SSE responses\n// endpoint (the same endpoint the official Codex client compresses against).\nconst REQUEST_COMPRESSION_ZSTD_LEVEL = 3;\nconst CODEX_TOOL_CALL_PROVIDERS = new Set([\"openai\", \"openai-codex\", \"opencode\"]);\nconst WEBSOCKET_MESSAGE_TOO_BIG_CLOSE_CODE = 1009;\nconst WEBSOCKET_CONNECTION_LIMIT_REACHED_CODE = \"websocket_connection_limit_reached\";\n\nconst CODEX_RESPONSE_STATUSES = new Set<CodexResponseStatus>([\n\t\"completed\",\n\t\"incomplete\",\n\t\"failed\",\n\t\"cancelled\",\n\t\"queued\",\n\t\"in_progress\",\n]);\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface OpenAICodexResponsesOptions extends StreamOptions {\n\treasoningEffort?: \"none\" | \"minimal\" | \"low\" | \"medium\" | \"high\" | \"xhigh\";\n\treasoningSummary?: \"auto\" | \"concise\" | \"detailed\" | \"off\" | \"on\" | null;\n\tserviceTier?: ResponseCreateParamsStreaming[\"service_tier\"];\n\ttextVerbosity?: \"low\" | \"medium\" | \"high\";\n}\n\ntype CodexResponseStatus = \"completed\" | \"incomplete\" | \"failed\" | \"cancelled\" | \"queued\" | \"in_progress\";\n\ninterface RequestBody {\n\tmodel: string;\n\tstore?: boolean;\n\tstream?: boolean;\n\tinstructions?: string;\n\tprevious_response_id?: string;\n\tinput?: ResponseInput;\n\ttools?: OpenAITool[];\n\ttool_choice?: \"auto\";\n\tparallel_tool_calls?: boolean;\n\ttemperature?: number;\n\treasoning?: { effort?: string; summary?: string };\n\tservice_tier?: ResponseCreateParamsStreaming[\"service_tier\"];\n\ttext?: { verbosity?: string };\n\tinclude?: string[];\n\tprompt_cache_key?: string;\n\t[key: string]: unknown;\n}\n\n// ============================================================================\n// Retry Helpers\n// ============================================================================\n\nfunction isTerminalRateLimitError(errorText: string): boolean {\n\treturn /GoUsageLimitError|FreeUsageLimitError|Monthly usage limit reached|available balance|insufficient_quota|out of budget|quota exceeded|billing/i.test(\n\t\terrorText,\n\t);\n}\n\nfunction isRetryableError(status: number, errorText: string): boolean {\n\tif (status === 429 && isTerminalRateLimitError(errorText)) {\n\t\treturn false;\n\t}\n\tif (status === 429 || status === 500 || status === 502 || status === 503 || status === 504) {\n\t\treturn true;\n\t}\n\treturn /rate.?limit|overloaded|service.?unavailable|upstream.?connect|connection.?refused/i.test(errorText);\n}\n\nfunction getRetryAfterDelayMs(headers: Headers): number | undefined {\n\tconst retryAfterMs = headers.get(\"retry-after-ms\");\n\tif (retryAfterMs !== null) {\n\t\tconst millis = Number(retryAfterMs);\n\t\tif (Number.isFinite(millis)) {\n\t\t\treturn Math.max(0, millis);\n\t\t}\n\t}\n\n\tconst retryAfter = headers.get(\"retry-after\");\n\tif (!retryAfter) {\n\t\treturn undefined;\n\t}\n\n\tconst seconds = Number(retryAfter);\n\tif (Number.isFinite(seconds)) {\n\t\treturn Math.max(0, seconds * 1000);\n\t}\n\n\tconst date = Date.parse(retryAfter);\n\tif (!Number.isNaN(date)) {\n\t\treturn Math.max(0, date - Date.now());\n\t}\n\n\treturn undefined;\n}\n\nfunction capRetryDelayMs(delayMs: number, options?: StreamOptions): number {\n\tconst maxRetryDelayMs = options?.maxRetryDelayMs ?? DEFAULT_MAX_RETRY_DELAY_MS;\n\treturn maxRetryDelayMs > 0 ? Math.min(delayMs, maxRetryDelayMs) : delayMs;\n}\n\nfunction sleep(ms: number, signal?: AbortSignal): Promise<void> {\n\treturn new Promise((resolve, reject) => {\n\t\tif (signal?.aborted) {\n\t\t\treject(new Error(\"Request was aborted\"));\n\t\t\treturn;\n\t\t}\n\t\tconst timeout = setTimeout(resolve, ms);\n\t\tsignal?.addEventListener(\"abort\", () => {\n\t\t\tclearTimeout(timeout);\n\t\t\treject(new Error(\"Request was aborted\"));\n\t\t});\n\t});\n}\n\nfunction normalizeTimeoutMs(value: number | undefined): number | undefined {\n\tif (value === undefined) return undefined;\n\tif (!Number.isFinite(value) || value < 0) {\n\t\tthrow new Error(`Invalid timeoutMs: ${String(value)}`);\n\t}\n\treturn Math.floor(value);\n}\n\n// ============================================================================\n// Request Compression\n// ============================================================================\n\ntype ProcessWithBuiltinModule = typeof process & {\n\tgetBuiltinModule?: (id: \"node:zlib\") => typeof NodeZlib;\n};\n\nfunction loadNodeZlib(): typeof NodeZlib | null {\n\tif (typeof process === \"undefined\" || !(process.versions?.node || process.versions?.bun)) {\n\t\treturn null;\n\t}\n\treturn (process as ProcessWithBuiltinModule).getBuiltinModule?.(\"node:zlib\") ?? null;\n}\n\n// Returns the zstd-compressed body bytes, or null when compression is\n// unavailable (browser/Vite builds). Callers fall back to sending the\n// uncompressed JSON when this returns null.\nfunction compressRequestBodyZstd(bodyJson: string): Uint8Array<ArrayBuffer> | null {\n\tconst zlib = loadNodeZlib();\n\tif (!zlib || typeof zlib.zstdCompressSync !== \"function\") {\n\t\treturn null;\n\t}\n\ttry {\n\t\tconst compressed = zlib.zstdCompressSync(bodyJson, {\n\t\t\tparams: { [zlib.constants.ZSTD_c_compressionLevel]: REQUEST_COMPRESSION_ZSTD_LEVEL },\n\t\t});\n\t\t// Use the ArrayLike overload (not (buffer, offset, length)) so the result is\n\t\t// typed Uint8Array<ArrayBuffer>, matching the DOM BodyInit/BufferSource type\n\t\t// expected by fetch()'s RequestInit.body. The (buffer, offset, length) overload\n\t\t// would infer Uint8Array<ArrayBufferLike>, which fetch's body type rejects.\n\t\treturn new Uint8Array(compressed);\n\t} catch {\n\t\treturn null;\n\t}\n}\n\n// ============================================================================\n// Main Stream Function\n// ============================================================================\n\nexport const stream: StreamFunction<\"openai-codex-responses\", OpenAICodexResponsesOptions> = (\n\tmodel: Model<\"openai-codex-responses\">,\n\tcontext: Context,\n\toptions?: OpenAICodexResponsesOptions,\n): AssistantMessageEventStream => {\n\tconst stream = new AssistantMessageEventStream();\n\n\t(async () => {\n\t\tconst output: AssistantMessage = {\n\t\t\trole: \"assistant\",\n\t\t\tcontent: [],\n\t\t\tapi: \"openai-codex-responses\" as Api,\n\t\t\tprovider: model.provider,\n\t\t\tmodel: model.id,\n\t\t\tusage: {\n\t\t\t\tinput: 0,\n\t\t\t\toutput: 0,\n\t\t\t\tcacheRead: 0,\n\t\t\t\tcacheWrite: 0,\n\t\t\t\ttotalTokens: 0,\n\t\t\t\tcost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },\n\t\t\t},\n\t\t\tstopReason: \"stop\",\n\t\t\ttimestamp: Date.now(),\n\t\t};\n\n\t\ttry {\n\t\t\tconst apiKey = options?.apiKey;\n\t\t\tif (!apiKey) {\n\t\t\t\tthrow new Error(`No API key for provider: ${model.provider}`);\n\t\t\t}\n\n\t\t\tconst accountId = extractAccountId(apiKey);\n\t\t\tlet body = buildRequestBody(model, context, options);\n\t\t\tconst nextBody = await options?.onPayload?.(body, model);\n\t\t\tif (nextBody !== undefined) {\n\t\t\t\tbody = nextBody as RequestBody;\n\t\t\t}\n\t\t\tconst websocketRequestId = options?.sessionId || createCodexRequestId();\n\t\t\tconst sseHeaders = buildSSEHeaders(model.headers, options?.headers, accountId, apiKey, options?.sessionId);\n\t\t\tconst websocketHeaders = buildWebSocketHeaders(\n\t\t\t\tmodel.headers,\n\t\t\t\toptions?.headers,\n\t\t\t\taccountId,\n\t\t\t\tapiKey,\n\t\t\t\twebsocketRequestId,\n\t\t\t);\n\t\t\tconst bodyJson = JSON.stringify(body);\n\t\t\tconst httpTimeoutMs = normalizeTimeoutMs(options?.timeoutMs);\n\t\t\tconst websocketConnectTimeoutMs = normalizeTimeoutMs(options?.websocketConnectTimeoutMs);\n\t\t\tconst transport = options?.transport || \"auto\";\n\t\t\tconst websocketDisabledForSession = transport !== \"sse\" && isWebSocketSseFallbackActive(options?.sessionId);\n\t\t\tif (websocketDisabledForSession) {\n\t\t\t\trecordWebSocketSseFallback(options?.sessionId);\n\t\t\t}\n\n\t\t\tif (transport !== \"sse\" && !websocketDisabledForSession) {\n\t\t\t\tlet websocketStarted = false;\n\t\t\t\tlet retriedWebSocketConnectionLimit = false;\n\t\t\t\twhile (true) {\n\t\t\t\t\twebsocketStarted = false;\n\t\t\t\t\ttry {\n\t\t\t\t\t\tawait processWebSocketStream(\n\t\t\t\t\t\t\tresolveCodexWebSocketUrl(model.baseUrl),\n\t\t\t\t\t\t\tbody,\n\t\t\t\t\t\t\twebsocketHeaders,\n\t\t\t\t\t\t\toutput,\n\t\t\t\t\t\t\tstream,\n\t\t\t\t\t\t\tmodel,\n\t\t\t\t\t\t\t() => {\n\t\t\t\t\t\t\t\twebsocketStarted = true;\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\thttpTimeoutMs,\n\t\t\t\t\t\t\twebsocketConnectTimeoutMs,\n\t\t\t\t\t\t\toptions,\n\t\t\t\t\t\t);\n\n\t\t\t\t\t\tif (options?.signal?.aborted) {\n\t\t\t\t\t\t\tthrow new Error(\"Request was aborted\");\n\t\t\t\t\t\t}\n\t\t\t\t\t\tstream.push({\n\t\t\t\t\t\t\ttype: \"done\",\n\t\t\t\t\t\t\treason: output.stopReason as \"stop\" | \"length\" | \"toolUse\",\n\t\t\t\t\t\t\tmessage: output,\n\t\t\t\t\t\t});\n\t\t\t\t\t\tstream.end();\n\t\t\t\t\t\treturn;\n\t\t\t\t\t} catch (error) {\n\t\t\t\t\t\tconst aborted = options?.signal?.aborted;\n\t\t\t\t\t\tconst connectionLimitBeforeStart = !websocketStarted && isWebSocketConnectionLimitReachedError(error);\n\t\t\t\t\t\tif (!aborted && connectionLimitBeforeStart && !retriedWebSocketConnectionLimit) {\n\t\t\t\t\t\t\tretriedWebSocketConnectionLimit = true;\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (aborted || (isCodexNonTransportError(error) && !connectionLimitBeforeStart)) {\n\t\t\t\t\t\t\tthrow error;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tappendAssistantMessageDiagnostic(\n\t\t\t\t\t\t\toutput,\n\t\t\t\t\t\t\tcreateAssistantMessageDiagnostic(\"provider_transport_failure\", error, {\n\t\t\t\t\t\t\t\tconfiguredTransport: transport,\n\t\t\t\t\t\t\t\tfallbackTransport: websocketStarted ? undefined : \"sse\",\n\t\t\t\t\t\t\t\teventsEmitted: websocketStarted,\n\t\t\t\t\t\t\t\tphase: websocketStarted ? \"after_message_stream_start\" : \"before_message_stream_start\",\n\t\t\t\t\t\t\t\trequestBytes: new TextEncoder().encode(bodyJson).byteLength,\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t);\n\t\t\t\t\t\trecordWebSocketFailure(options?.sessionId, error);\n\t\t\t\t\t\tif (websocketStarted) {\n\t\t\t\t\t\t\tthrow error;\n\t\t\t\t\t\t}\n\t\t\t\t\t\trecordWebSocketSseFallback(options?.sessionId);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Compress the request body once for the SSE path. The Codex backend\n\t\t\t// decodes Content-Encoding: zstd; the WebSocket transport above sends the\n\t\t\t// uncompressed JSON frame, matching the official Codex client.\n\t\t\tconst compressedBody = compressRequestBodyZstd(bodyJson);\n\t\t\tif (compressedBody) {\n\t\t\t\tsseHeaders.set(\"content-encoding\", \"zstd\");\n\t\t\t}\n\t\t\tconst sseBody: Uint8Array<ArrayBuffer> | string = compressedBody ?? bodyJson;\n\n\t\t\t// Fetch with retry logic for rate limits and transient errors\n\t\t\tlet response: Response | undefined;\n\t\t\tlet lastError: Error | undefined;\n\t\t\tconst maxRetries = options?.maxRetries ?? DEFAULT_MAX_RETRIES;\n\n\t\t\tfor (let attempt = 0; attempt <= maxRetries; attempt++) {\n\t\t\t\tif (options?.signal?.aborted) {\n\t\t\t\t\tthrow new Error(\"Request was aborted\");\n\t\t\t\t}\n\n\t\t\t\ttry {\n\t\t\t\t\tconst headerTimeoutSignal =\n\t\t\t\t\t\thttpTimeoutMs !== undefined && httpTimeoutMs > 0 ? AbortSignal.timeout(httpTimeoutMs) : undefined;\n\t\t\t\t\tconst combinedSignal = combineAbortSignals([options?.signal, headerTimeoutSignal]);\n\t\t\t\t\ttry {\n\t\t\t\t\t\tresponse = await fetch(resolveCodexUrl(model.baseUrl), {\n\t\t\t\t\t\t\tmethod: \"POST\",\n\t\t\t\t\t\t\theaders: sseHeaders,\n\t\t\t\t\t\t\tbody: sseBody,\n\t\t\t\t\t\t\tsignal: combinedSignal.signal,\n\t\t\t\t\t\t});\n\t\t\t\t\t} catch (error) {\n\t\t\t\t\t\tif (headerTimeoutSignal?.aborted && !options?.signal?.aborted) {\n\t\t\t\t\t\t\tthrow new Error(`Codex SSE response headers timed out after ${httpTimeoutMs}ms`);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tthrow error;\n\t\t\t\t\t} finally {\n\t\t\t\t\t\tcombinedSignal.cleanup();\n\t\t\t\t\t}\n\t\t\t\t\tawait options?.onResponse?.(\n\t\t\t\t\t\t{ status: response.status, headers: headersToRecord(response.headers) },\n\t\t\t\t\t\tmodel,\n\t\t\t\t\t);\n\n\t\t\t\t\tif (response.ok) {\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\n\t\t\t\t\tconst errorText = await response.text();\n\t\t\t\t\tif (attempt < maxRetries && isRetryableError(response.status, errorText)) {\n\t\t\t\t\t\tconst retryAfterDelayMs = getRetryAfterDelayMs(response.headers);\n\t\t\t\t\t\tconst delayMs =\n\t\t\t\t\t\t\tretryAfterDelayMs === undefined\n\t\t\t\t\t\t\t\t? BASE_DELAY_MS * 2 ** attempt\n\t\t\t\t\t\t\t\t: response.status === 429\n\t\t\t\t\t\t\t\t\t? capRetryDelayMs(retryAfterDelayMs, options)\n\t\t\t\t\t\t\t\t\t: retryAfterDelayMs;\n\n\t\t\t\t\t\tawait sleep(delayMs, options?.signal);\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\n\t\t\t\t\t// Parse error for friendly message on final attempt or non-retryable error\n\t\t\t\t\tconst fakeResponse = new Response(errorText, {\n\t\t\t\t\t\tstatus: response.status,\n\t\t\t\t\t\tstatusText: response.statusText,\n\t\t\t\t\t});\n\t\t\t\t\tconst info = await parseErrorResponse(fakeResponse);\n\t\t\t\t\tthrow new Error(info.friendlyMessage || info.message);\n\t\t\t\t} catch (error) {\n\t\t\t\t\tif (error instanceof Error) {\n\t\t\t\t\t\tif (error.name === \"AbortError\" || error.message === \"Request was aborted\") {\n\t\t\t\t\t\t\tthrow new Error(\"Request was aborted\");\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tlastError = error instanceof Error ? error : new Error(String(error));\n\t\t\t\t\t// Network errors are retryable\n\t\t\t\t\tif (attempt < maxRetries && !lastError.message.includes(\"usage limit\")) {\n\t\t\t\t\t\tconst delayMs = BASE_DELAY_MS * 2 ** attempt;\n\t\t\t\t\t\tawait sleep(delayMs, options?.signal);\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\tthrow lastError;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (!response?.ok) {\n\t\t\t\tthrow lastError ?? new Error(\"Failed after retries\");\n\t\t\t}\n\n\t\t\tif (!response.body) {\n\t\t\t\tthrow new Error(\"No response body\");\n\t\t\t}\n\n\t\t\tstream.push({ type: \"start\", partial: output });\n\t\t\tawait processStream(response, output, stream, model, options);\n\n\t\t\tif (options?.signal?.aborted) {\n\t\t\t\tthrow new Error(\"Request was aborted\");\n\t\t\t}\n\n\t\t\tstream.push({ type: \"done\", reason: output.stopReason as \"stop\" | \"length\" | \"toolUse\", message: output });\n\t\t\tstream.end();\n\t\t} catch (error) {\n\t\t\tfor (const block of output.content) {\n\t\t\t\t// partialJson is only a streaming scratch buffer; never persist it.\n\t\t\t\tdelete (block as { partialJson?: string }).partialJson;\n\t\t\t}\n\t\t\toutput.stopReason = options?.signal?.aborted ? \"aborted\" : \"error\";\n\t\t\toutput.errorMessage = formatProviderError(normalizeProviderError(error));\n\t\t\tstream.push({ type: \"error\", reason: output.stopReason, error: output });\n\t\t\tstream.end();\n\t\t}\n\t})();\n\n\treturn stream;\n};\n\nexport const streamSimple: StreamFunction<\"openai-codex-responses\", SimpleStreamOptions> = (\n\tmodel: Model<\"openai-codex-responses\">,\n\tcontext: Context,\n\toptions?: SimpleStreamOptions,\n): AssistantMessageEventStream => {\n\tconst apiKey = options?.apiKey;\n\tif (!apiKey) {\n\t\tthrow new Error(`No API key for provider: ${model.provider}`);\n\t}\n\n\tconst base = buildBaseOptions(model, context, options, apiKey);\n\tconst rawLevel = options?.reasoning ? clampThinkingLevel(model, options.reasoning) : undefined;\n\tconst clampedReasoning = rawLevel === \"off\" ? rawLevel : clampToXhigh(rawLevel);\n\tconst reasoningEffort = clampedReasoning === \"off\" ? undefined : clampedReasoning;\n\n\treturn stream(model, context, {\n\t\t...base,\n\t\treasoningEffort,\n\t} satisfies OpenAICodexResponsesOptions);\n};\n\n// ============================================================================\n// Request Building\n// ============================================================================\n\nfunction buildRequestBody(\n\tmodel: Model<\"openai-codex-responses\">,\n\tcontext: Context,\n\toptions?: OpenAICodexResponsesOptions,\n): RequestBody {\n\tconst messages = convertResponsesMessages(model, context, CODEX_TOOL_CALL_PROVIDERS, {\n\t\tincludeSystemPrompt: false,\n\t});\n\n\tconst body: RequestBody = {\n\t\tmodel: model.id,\n\t\tstore: false,\n\t\tstream: true,\n\t\tinstructions: context.systemPrompt || \"You are a helpful assistant.\",\n\t\tinput: messages,\n\t\ttext: { verbosity: options?.textVerbosity || \"low\" },\n\t\tinclude: [\"reasoning.encrypted_content\"],\n\t\tprompt_cache_key: clampOpenAIPromptCacheKey(options?.sessionId),\n\t\ttool_choice: \"auto\",\n\t\tparallel_tool_calls: true,\n\t};\n\n\tif (options?.temperature !== undefined) {\n\t\tbody.temperature = options.temperature;\n\t}\n\n\tif (options?.serviceTier !== undefined) {\n\t\tbody.service_tier = options.serviceTier;\n\t}\n\n\tif (context.tools && context.tools.length > 0) {\n\t\tbody.tools = convertResponsesTools(context.tools, { strict: null });\n\t}\n\n\tif (options?.reasoningEffort !== undefined) {\n\t\tconst effort =\n\t\t\toptions.reasoningEffort === \"none\"\n\t\t\t\t? (model.thinkingLevelMap?.off ?? \"none\")\n\t\t\t\t: (model.thinkingLevelMap?.[options.reasoningEffort] ?? options.reasoningEffort);\n\t\tif (effort !== null) {\n\t\t\tbody.reasoning = {\n\t\t\t\teffort,\n\t\t\t\tsummary: options.reasoningSummary ?? \"auto\",\n\t\t\t};\n\t\t}\n\t}\n\n\treturn body;\n}\n\nfunction getServiceTierCostMultiplier(\n\tmodel: Pick<Model<\"openai-codex-responses\">, \"id\">,\n\tserviceTier: ResponseCreateParamsStreaming[\"service_tier\"] | undefined,\n): number {\n\tswitch (serviceTier) {\n\t\tcase \"flex\":\n\t\t\treturn 0.5;\n\t\tcase \"priority\":\n\t\t\treturn model.id === \"gpt-5.5\" ? 2.5 : 2;\n\t\tdefault:\n\t\t\treturn 1;\n\t}\n}\n\nfunction applyServiceTierPricing(\n\tusage: Usage,\n\tserviceTier: ResponseCreateParamsStreaming[\"service_tier\"] | undefined,\n\tmodel: Pick<Model<\"openai-codex-responses\">, \"id\">,\n) {\n\tconst multiplier = getServiceTierCostMultiplier(model, serviceTier);\n\tif (multiplier === 1) return;\n\n\tusage.cost.input *= multiplier;\n\tusage.cost.output *= multiplier;\n\tusage.cost.cacheRead *= multiplier;\n\tusage.cost.cacheWrite *= multiplier;\n\tusage.cost.total = usage.cost.input + usage.cost.output + usage.cost.cacheRead + usage.cost.cacheWrite;\n}\n\nfunction resolveCodexServiceTier(\n\tresponseServiceTier: ResponseCreateParamsStreaming[\"service_tier\"] | undefined,\n\trequestServiceTier: ResponseCreateParamsStreaming[\"service_tier\"] | undefined,\n): ResponseCreateParamsStreaming[\"service_tier\"] | undefined {\n\tif (responseServiceTier === \"default\" && (requestServiceTier === \"flex\" || requestServiceTier === \"priority\")) {\n\t\treturn requestServiceTier;\n\t}\n\treturn responseServiceTier ?? requestServiceTier;\n}\n\nfunction resolveCodexUrl(baseUrl?: string): string {\n\tconst raw = baseUrl && baseUrl.trim().length > 0 ? baseUrl : DEFAULT_CODEX_BASE_URL;\n\tconst normalized = raw.replace(/\\/+$/, \"\");\n\tif (normalized.endsWith(\"/codex/responses\")) return normalized;\n\tif (normalized.endsWith(\"/codex\")) return `${normalized}/responses`;\n\treturn `${normalized}/codex/responses`;\n}\n\nfunction resolveCodexWebSocketUrl(baseUrl?: string): string {\n\tconst url = new URL(resolveCodexUrl(baseUrl));\n\tif (url.protocol === \"https:\") url.protocol = \"wss:\";\n\tif (url.protocol === \"http:\") url.protocol = \"ws:\";\n\treturn url.toString();\n}\n\n// ============================================================================\n// Response Processing\n// ============================================================================\n\nasync function processStream(\n\tresponse: Response,\n\toutput: AssistantMessage,\n\tstream: AssistantMessageEventStream,\n\tmodel: Model<\"openai-codex-responses\">,\n\toptions?: OpenAICodexResponsesOptions,\n): Promise<void> {\n\tawait processResponsesStream(mapCodexEvents(parseSSE(response, options?.signal)), output, stream, model, {\n\t\tserviceTier: options?.serviceTier,\n\t\tresolveServiceTier: resolveCodexServiceTier,\n\t\tapplyServiceTierPricing: (usage, serviceTier) => applyServiceTierPricing(usage, serviceTier, model),\n\t});\n}\n\nclass CodexApiError extends Error {\n\treadonly code?: string;\n\treadonly payload?: Record<string, unknown>;\n\n\tconstructor(message: string, options?: { code?: string; payload?: Record<string, unknown>; cause?: unknown }) {\n\t\tsuper(message);\n\t\tthis.name = \"CodexApiError\";\n\t\tthis.code = options?.code;\n\t\tthis.payload = options?.payload;\n\t\tthis.cause = options?.cause;\n\t}\n}\n\nclass CodexProtocolError extends Error {\n\treadonly payload?: unknown;\n\n\tconstructor(message: string, options?: { payload?: unknown; cause?: unknown }) {\n\t\tsuper(message);\n\t\tthis.name = \"CodexProtocolError\";\n\t\tthis.payload = options?.payload;\n\t\tthis.cause = options?.cause;\n\t}\n}\n\nfunction isCodexNonTransportError(error: unknown): boolean {\n\treturn error instanceof CodexApiError || error instanceof CodexProtocolError;\n}\n\nfunction isWebSocketConnectionLimitReachedError(error: unknown): boolean {\n\treturn error instanceof CodexApiError && error.code === WEBSOCKET_CONNECTION_LIMIT_REACHED_CODE;\n}\n\nfunction extractCodexEventError(event: Record<string, unknown>): { code?: string; message?: string } {\n\tconst nested = event.error && typeof event.error === \"object\" ? (event.error as Record<string, unknown>) : undefined;\n\treturn {\n\t\tcode: typeof event.code === \"string\" ? event.code : typeof nested?.code === \"string\" ? nested.code : undefined,\n\t\tmessage:\n\t\t\ttypeof event.message === \"string\"\n\t\t\t\t? event.message\n\t\t\t\t: typeof nested?.message === \"string\"\n\t\t\t\t\t? nested.message\n\t\t\t\t\t: undefined,\n\t};\n}\n\nasync function* mapCodexEvents(events: AsyncIterable<Record<string, unknown>>): AsyncGenerator<ResponseStreamEvent> {\n\tfor await (const event of events) {\n\t\tconst type = typeof event.type === \"string\" ? event.type : undefined;\n\t\tif (!type) continue;\n\n\t\tif (type === \"error\") {\n\t\t\tconst { code, message } = extractCodexEventError(event);\n\t\t\tthrow new CodexApiError(`Codex error: ${message || code || JSON.stringify(event)}`, {\n\t\t\t\tcode,\n\t\t\t\tpayload: event,\n\t\t\t});\n\t\t}\n\n\t\tif (type === \"response.failed\") {\n\t\t\tconst response = (event as { response?: { error?: { code?: string; message?: string } } }).response;\n\t\t\tconst code = response?.error?.code;\n\t\t\tconst message = response?.error?.message;\n\t\t\tthrow new CodexApiError(message || \"Codex response failed\", { code, payload: event });\n\t\t}\n\n\t\tif (type === \"response.done\" || type === \"response.completed\" || type === \"response.incomplete\") {\n\t\t\tconst response = (event as { response?: { status?: unknown } }).response;\n\t\t\tconst normalizedResponse = response\n\t\t\t\t? { ...response, status: normalizeCodexStatus(response.status) }\n\t\t\t\t: response;\n\t\t\tyield { ...event, type: \"response.completed\", response: normalizedResponse } as ResponseStreamEvent;\n\t\t\treturn;\n\t\t}\n\n\t\tyield event as unknown as ResponseStreamEvent;\n\t}\n}\n\nfunction normalizeCodexStatus(status: unknown): CodexResponseStatus | undefined {\n\tif (typeof status !== \"string\") return undefined;\n\treturn CODEX_RESPONSE_STATUSES.has(status as CodexResponseStatus) ? (status as CodexResponseStatus) : undefined;\n}\n\n// ============================================================================\n// SSE Parsing\n// ============================================================================\n\nasync function* parseSSE(response: Response, signal?: AbortSignal): AsyncGenerator<Record<string, unknown>> {\n\tif (!response.body) return;\n\n\tconst reader = response.body.getReader();\n\tconst decoder = new TextDecoder();\n\tlet buffer = \"\";\n\tconst onAbort = () => {\n\t\tvoid reader.cancel().catch(() => {});\n\t};\n\tsignal?.addEventListener(\"abort\", onAbort, { once: true });\n\n\ttry {\n\t\twhile (true) {\n\t\t\tif (signal?.aborted) {\n\t\t\t\tthrow new Error(\"Request was aborted\");\n\t\t\t}\n\t\t\tconst { done, value } = await reader.read();\n\t\t\tif (signal?.aborted) {\n\t\t\t\tthrow new Error(\"Request was aborted\");\n\t\t\t}\n\t\t\tif (done) break;\n\t\t\tbuffer += decoder.decode(value, { stream: true });\n\n\t\t\tlet idx = buffer.indexOf(\"\\n\\n\");\n\t\t\twhile (idx !== -1) {\n\t\t\t\tconst chunk = buffer.slice(0, idx);\n\t\t\t\tbuffer = buffer.slice(idx + 2);\n\n\t\t\t\tconst dataLines = chunk\n\t\t\t\t\t.split(\"\\n\")\n\t\t\t\t\t.filter((l) => l.startsWith(\"data:\"))\n\t\t\t\t\t.map((l) => l.slice(5).trim());\n\t\t\t\tif (dataLines.length > 0) {\n\t\t\t\t\tconst data = dataLines.join(\"\\n\").trim();\n\t\t\t\t\tif (data && data !== \"[DONE]\") {\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tyield JSON.parse(data) as Record<string, unknown>;\n\t\t\t\t\t\t} catch (cause) {\n\t\t\t\t\t\t\tthrow new CodexProtocolError(`Invalid Codex SSE JSON: ${formatThrownValue(cause)}`, {\n\t\t\t\t\t\t\t\tcause,\n\t\t\t\t\t\t\t\tpayload: data,\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tidx = buffer.indexOf(\"\\n\\n\");\n\t\t\t}\n\t\t}\n\t} finally {\n\t\tsignal?.removeEventListener(\"abort\", onAbort);\n\t\ttry {\n\t\t\tawait reader.cancel();\n\t\t} catch {}\n\t\ttry {\n\t\t\treader.releaseLock();\n\t\t} catch {}\n\t}\n}\n\n// ============================================================================\n// WebSocket Parsing\n// ============================================================================\n\nconst OPENAI_BETA_RESPONSES_WEBSOCKETS = \"responses_websockets=2026-02-06\";\nconst SESSION_WEBSOCKET_CACHE_TTL_MS = 5 * 60 * 1000;\nconst SESSION_WEBSOCKET_MAX_AGE_MS = 55 * 60 * 1000;\n\ntype WebSocketEventType = \"open\" | \"message\" | \"error\" | \"close\";\ntype WebSocketListener = (event: unknown) => void;\n\ninterface WebSocketLike {\n\tclose(code?: number, reason?: string): void;\n\tsend(data: string): void;\n\taddEventListener(type: WebSocketEventType, listener: WebSocketListener): void;\n\tremoveEventListener(type: WebSocketEventType, listener: WebSocketListener): void;\n}\n\ninterface CachedWebSocketContinuationState {\n\tlastRequestBody: RequestBody;\n\tlastResponseId: string;\n\tlastResponseItems: ResponseInput;\n}\n\ninterface CachedWebSocketConnection {\n\tsocket: WebSocketLike;\n\tbusy: boolean;\n\tcreatedAt: number;\n\tidleTimer?: ReturnType<typeof setTimeout>;\n\tcontinuation?: CachedWebSocketContinuationState;\n}\n\nexport interface OpenAICodexWebSocketDebugStats {\n\trequests: number;\n\tconnectionsCreated: number;\n\tconnectionsReused: number;\n\tcachedContextRequests: number;\n\tstoreTrueRequests: number;\n\tfullContextRequests: number;\n\tdeltaRequests: number;\n\tlastInputItems: number;\n\tlastDeltaInputItems?: number;\n\tlastPreviousResponseId?: string;\n\twebsocketFailures: number;\n\tsseFallbacks: number;\n\twebsocketFallbackActive?: boolean;\n\tlastWebSocketError?: string;\n}\n\nconst websocketSessionCache = new Map<string, CachedWebSocketConnection>();\nconst websocketDebugStats = new Map<string, OpenAICodexWebSocketDebugStats>();\nconst websocketSseFallbackSessions = new Set<string>();\n\nfunction getOrCreateWebSocketDebugStats(sessionId: string): OpenAICodexWebSocketDebugStats {\n\tlet stats = websocketDebugStats.get(sessionId);\n\tif (!stats) {\n\t\tstats = {\n\t\t\trequests: 0,\n\t\t\tconnectionsCreated: 0,\n\t\t\tconnectionsReused: 0,\n\t\t\tcachedContextRequests: 0,\n\t\t\tstoreTrueRequests: 0,\n\t\t\tfullContextRequests: 0,\n\t\t\tdeltaRequests: 0,\n\t\t\tlastInputItems: 0,\n\t\t\twebsocketFailures: 0,\n\t\t\tsseFallbacks: 0,\n\t\t};\n\t\twebsocketDebugStats.set(sessionId, stats);\n\t}\n\treturn stats;\n}\n\nexport function getOpenAICodexWebSocketDebugStats(sessionId: string): OpenAICodexWebSocketDebugStats | undefined {\n\tconst stats = websocketDebugStats.get(sessionId);\n\treturn stats ? { ...stats } : undefined;\n}\n\nexport function resetOpenAICodexWebSocketDebugStats(sessionId?: string): void {\n\tif (sessionId) {\n\t\twebsocketDebugStats.delete(sessionId);\n\t\twebsocketSseFallbackSessions.delete(sessionId);\n\t\treturn;\n\t}\n\twebsocketDebugStats.clear();\n\twebsocketSseFallbackSessions.clear();\n}\n\nexport function closeOpenAICodexWebSocketSessions(sessionId?: string): void {\n\tconst closeEntry = (entry: CachedWebSocketConnection) => {\n\t\tif (entry.idleTimer) clearTimeout(entry.idleTimer);\n\t\tcloseWebSocketSilently(entry.socket, 1000, \"debug_close\");\n\t};\n\tif (sessionId) {\n\t\tconst entry = websocketSessionCache.get(sessionId);\n\t\tif (entry) closeEntry(entry);\n\t\twebsocketSessionCache.delete(sessionId);\n\t\treturn;\n\t}\n\tfor (const entry of websocketSessionCache.values()) {\n\t\tcloseEntry(entry);\n\t}\n\twebsocketSessionCache.clear();\n}\n\nregisterSessionResourceCleanup(closeOpenAICodexWebSocketSessions);\n\nfunction isWebSocketSseFallbackActive(sessionId: string | undefined): boolean {\n\treturn sessionId ? websocketSseFallbackSessions.has(sessionId) : false;\n}\n\nfunction recordWebSocketSseFallback(sessionId: string | undefined): void {\n\tif (!sessionId) return;\n\tconst stats = getOrCreateWebSocketDebugStats(sessionId);\n\tstats.sseFallbacks++;\n\tstats.websocketFallbackActive = isWebSocketSseFallbackActive(sessionId);\n}\n\nfunction recordWebSocketFailure(sessionId: string | undefined, error: unknown): void {\n\tif (!sessionId) return;\n\twebsocketSseFallbackSessions.add(sessionId);\n\n\tconst stats = getOrCreateWebSocketDebugStats(sessionId);\n\tstats.websocketFailures++;\n\tstats.lastWebSocketError = formatThrownValue(error);\n\tstats.websocketFallbackActive = true;\n}\n\ntype WebSocketConstructor = new (\n\turl: string,\n\tprotocols?: string | string[] | { headers?: Record<string, string> },\n) => WebSocketLike;\n\nlet _cachedWebsocket: WebSocketConstructor | null = null;\nasync function getWebSocketConstructor(env?: ProviderEnv): Promise<WebSocketConstructor | null> {\n\tif (!env && _cachedWebsocket) return _cachedWebsocket;\n\n\t// bun doesn't respect http proxy envs, ref: https://github.com/oven-sh/bun/issues/15489\n\t// TODO: remove this when bun supports proxy envs in websocket.\n\tif (typeof process !== \"undefined\" && process.versions?.bun) {\n\t\tconst WebSocketWithProxy = class extends WebSocket {\n\t\t\tconstructor(url: string | URL, options?: string | string[] | Record<string, unknown>) {\n\t\t\t\tlet _opts: Record<string, unknown> = {};\n\t\t\t\tif (Array.isArray(options) || typeof options === \"string\") {\n\t\t\t\t\t_opts = { protocols: options };\n\t\t\t\t} else {\n\t\t\t\t\t_opts = { ...options };\n\t\t\t\t}\n\n\t\t\t\tconst proxyUrl = resolveHttpProxyUrlForTarget(\n\t\t\t\t\turl.toString().replace(/^wss:/, \"https:\").replace(/^ws:/, \"http:\"),\n\t\t\t\t\tenv,\n\t\t\t\t);\n\t\t\t\tsuper(url, { ..._opts, ...(proxyUrl ? { proxy: proxyUrl.toString() } : {}) } as any);\n\t\t\t}\n\t\t};\n\t\tif (!env) {\n\t\t\t_cachedWebsocket = WebSocketWithProxy;\n\t\t}\n\t\treturn WebSocketWithProxy;\n\t}\n\n\tconst ctor = (globalThis as { WebSocket?: unknown }).WebSocket;\n\tif (typeof ctor !== \"function\") return null;\n\treturn ctor as unknown as WebSocketConstructor;\n}\n\nclass WebSocketCloseError extends Error {\n\treadonly code?: number;\n\treadonly reason?: string;\n\treadonly wasClean?: boolean;\n\n\tconstructor(message: string, options?: { code?: number; reason?: string; wasClean?: boolean }) {\n\t\tsuper(message);\n\t\tthis.name = \"WebSocketCloseError\";\n\t\tthis.code = options?.code;\n\t\tthis.reason = options?.reason;\n\t\tthis.wasClean = options?.wasClean;\n\t}\n}\n\nfunction getWebSocketReadyState(socket: WebSocketLike): number | undefined {\n\tconst readyState = (socket as { readyState?: unknown }).readyState;\n\treturn typeof readyState === \"number\" ? readyState : undefined;\n}\n\nfunction isWebSocketReusable(socket: WebSocketLike): boolean {\n\tconst readyState = getWebSocketReadyState(socket);\n\t// If readyState is unavailable, assume the runtime keeps it open/reusable.\n\treturn readyState === undefined || readyState === 1;\n}\n\nfunction isWebSocketSessionExpired(entry: CachedWebSocketConnection): boolean {\n\treturn Date.now() - entry.createdAt >= SESSION_WEBSOCKET_MAX_AGE_MS;\n}\n\nfunction closeWebSocketSilently(socket: WebSocketLike, code = 1000, reason = \"done\"): void {\n\ttry {\n\t\tsocket.close(code, reason);\n\t} catch {}\n}\n\nfunction scheduleSessionWebSocketExpiry(sessionId: string, entry: CachedWebSocketConnection): void {\n\tif (entry.idleTimer) {\n\t\tclearTimeout(entry.idleTimer);\n\t}\n\tentry.idleTimer = setTimeout(() => {\n\t\tif (entry.busy) return;\n\t\tcloseWebSocketSilently(entry.socket, 1000, \"idle_timeout\");\n\t\twebsocketSessionCache.delete(sessionId);\n\t}, SESSION_WEBSOCKET_CACHE_TTL_MS);\n}\n\nasync function connectWebSocket(\n\turl: string,\n\theaders: Headers,\n\tsignal?: AbortSignal,\n\tconnectTimeoutMs = DEFAULT_WEBSOCKET_CONNECT_TIMEOUT_MS,\n\tenv?: ProviderEnv,\n): Promise<WebSocketLike> {\n\tconst WebSocketCtor = await getWebSocketConstructor(env);\n\tif (!WebSocketCtor) {\n\t\tthrow new Error(\"WebSocket transport is not available in this runtime\");\n\t}\n\n\tconst wsHeaders = headersToRecord(headers);\n\tdelete wsHeaders[\"OpenAI-Beta\"];\n\n\treturn new Promise<WebSocketLike>((resolve, reject) => {\n\t\tlet settled = false;\n\t\tlet timeout: ReturnType<typeof setTimeout> | undefined;\n\t\tlet socket: WebSocketLike;\n\n\t\ttry {\n\t\t\tsocket = new WebSocketCtor(url, { headers: wsHeaders });\n\t\t} catch (error) {\n\t\t\treject(error instanceof Error ? error : new Error(String(error)));\n\t\t\treturn;\n\t\t}\n\n\t\tconst cleanup = () => {\n\t\t\tif (timeout) {\n\t\t\t\tclearTimeout(timeout);\n\t\t\t\ttimeout = undefined;\n\t\t\t}\n\t\t\tsocket.removeEventListener(\"open\", onOpen);\n\t\t\tsocket.removeEventListener(\"error\", onError);\n\t\t\tsocket.removeEventListener(\"close\", onClose);\n\t\t\tsignal?.removeEventListener(\"abort\", onAbort);\n\t\t};\n\t\tconst fail = (error: Error, closeReason?: string) => {\n\t\t\tif (settled) return;\n\t\t\tsettled = true;\n\t\t\tcleanup();\n\t\t\tif (closeReason) {\n\t\t\t\tcloseWebSocketSilently(socket, 1000, closeReason);\n\t\t\t}\n\t\t\treject(error);\n\t\t};\n\t\tconst onOpen: WebSocketListener = () => {\n\t\t\tif (settled) return;\n\t\t\tsettled = true;\n\t\t\tcleanup();\n\t\t\tresolve(socket);\n\t\t};\n\t\tconst onError: WebSocketListener = (event) => {\n\t\t\tfail(extractWebSocketError(event));\n\t\t};\n\t\tconst onClose: WebSocketListener = (event) => {\n\t\t\tfail(extractWebSocketCloseError(event));\n\t\t};\n\t\tconst onAbort = () => {\n\t\t\tfail(new Error(\"Request was aborted\"), \"aborted\");\n\t\t};\n\n\t\tsocket.addEventListener(\"open\", onOpen);\n\t\tsocket.addEventListener(\"error\", onError);\n\t\tsocket.addEventListener(\"close\", onClose);\n\t\tsignal?.addEventListener(\"abort\", onAbort);\n\n\t\tif (connectTimeoutMs > 0) {\n\t\t\ttimeout = setTimeout(() => {\n\t\t\t\tfail(new Error(`WebSocket connect timeout after ${connectTimeoutMs}ms`), \"connect_timeout\");\n\t\t\t}, connectTimeoutMs);\n\t\t}\n\t\tif (signal?.aborted) {\n\t\t\tonAbort();\n\t\t}\n\t});\n}\n\nasync function acquireWebSocket(\n\turl: string,\n\theaders: Headers,\n\tsessionId: string | undefined,\n\tsignal?: AbortSignal,\n\tconnectTimeoutMs?: number,\n\tenv?: ProviderEnv,\n): Promise<{\n\tsocket: WebSocketLike;\n\tentry?: CachedWebSocketConnection;\n\treused: boolean;\n\trelease: (options?: { keep?: boolean }) => void;\n}> {\n\tif (!sessionId) {\n\t\tconst socket = await connectWebSocket(url, headers, signal, connectTimeoutMs, env);\n\t\treturn {\n\t\t\tsocket,\n\t\t\treused: false,\n\t\t\trelease: () => closeWebSocketSilently(socket),\n\t\t};\n\t}\n\n\tconst cached = websocketSessionCache.get(sessionId);\n\tif (cached) {\n\t\tif (cached.idleTimer) {\n\t\t\tclearTimeout(cached.idleTimer);\n\t\t\tcached.idleTimer = undefined;\n\t\t}\n\t\tif (!cached.busy && isWebSocketSessionExpired(cached)) {\n\t\t\tcloseWebSocketSilently(cached.socket, 1000, \"connection_age_limit\");\n\t\t\twebsocketSessionCache.delete(sessionId);\n\t\t} else if (!cached.busy && isWebSocketReusable(cached.socket)) {\n\t\t\tcached.busy = true;\n\t\t\treturn {\n\t\t\t\tsocket: cached.socket,\n\t\t\t\tentry: cached,\n\t\t\t\treused: true,\n\t\t\t\trelease: ({ keep } = {}) => {\n\t\t\t\t\tif (!keep || !isWebSocketReusable(cached.socket)) {\n\t\t\t\t\t\tcloseWebSocketSilently(cached.socket);\n\t\t\t\t\t\twebsocketSessionCache.delete(sessionId);\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\tcached.busy = false;\n\t\t\t\t\tscheduleSessionWebSocketExpiry(sessionId, cached);\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\t\tif (cached.busy) {\n\t\t\tconst socket = await connectWebSocket(url, headers, signal, connectTimeoutMs, env);\n\t\t\treturn {\n\t\t\t\tsocket,\n\t\t\t\treused: false,\n\t\t\t\trelease: () => {\n\t\t\t\t\tcloseWebSocketSilently(socket);\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\t\tif (!isWebSocketReusable(cached.socket)) {\n\t\t\tcloseWebSocketSilently(cached.socket);\n\t\t\twebsocketSessionCache.delete(sessionId);\n\t\t}\n\t}\n\n\tconst socket = await connectWebSocket(url, headers, signal, connectTimeoutMs, env);\n\tconst entry: CachedWebSocketConnection = { socket, busy: true, createdAt: Date.now() };\n\twebsocketSessionCache.set(sessionId, entry);\n\treturn {\n\t\tsocket,\n\t\tentry,\n\t\treused: false,\n\t\trelease: ({ keep } = {}) => {\n\t\t\tif (!keep || !isWebSocketReusable(entry.socket)) {\n\t\t\t\tcloseWebSocketSilently(entry.socket);\n\t\t\t\tif (entry.idleTimer) clearTimeout(entry.idleTimer);\n\t\t\t\tif (websocketSessionCache.get(sessionId) === entry) {\n\t\t\t\t\twebsocketSessionCache.delete(sessionId);\n\t\t\t\t}\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tentry.busy = false;\n\t\t\tscheduleSessionWebSocketExpiry(sessionId, entry);\n\t\t},\n\t};\n}\n\nfunction extractWebSocketError(event: unknown): Error {\n\tif (event && typeof event === \"object\") {\n\t\tconst message = \"message\" in event ? (event as { message?: unknown }).message : undefined;\n\t\tif (typeof message === \"string\" && message.length > 0) {\n\t\t\treturn new Error(message);\n\t\t}\n\n\t\tconst nestedError = \"error\" in event ? (event as { error?: unknown }).error : undefined;\n\t\tif (nestedError instanceof Error && nestedError.message.length > 0) {\n\t\t\treturn nestedError;\n\t\t}\n\t\tif (nestedError && typeof nestedError === \"object\" && \"message\" in nestedError) {\n\t\t\tconst nestedMessage = (nestedError as { message?: unknown }).message;\n\t\t\tif (typeof nestedMessage === \"string\" && nestedMessage.length > 0) {\n\t\t\t\treturn new Error(nestedMessage);\n\t\t\t}\n\t\t}\n\t}\n\treturn new Error(\"WebSocket error\");\n}\n\nfunction extractWebSocketCloseError(event: unknown): Error {\n\tif (event && typeof event === \"object\") {\n\t\tconst code = \"code\" in event ? (event as { code?: unknown }).code : undefined;\n\t\tconst reason = \"reason\" in event ? (event as { reason?: unknown }).reason : undefined;\n\t\tconst wasClean = \"wasClean\" in event ? (event as { wasClean?: unknown }).wasClean : undefined;\n\t\tconst codeText = typeof code === \"number\" ? ` ${code}` : \"\";\n\t\tlet reasonText = typeof reason === \"string\" && reason.length > 0 ? ` ${reason}` : \"\";\n\t\tif (!reasonText && code === WEBSOCKET_MESSAGE_TOO_BIG_CLOSE_CODE) {\n\t\t\treasonText = \" message too big\";\n\t\t}\n\t\treturn new WebSocketCloseError(`WebSocket closed${codeText}${reasonText}`.trim(), {\n\t\t\tcode: typeof code === \"number\" ? code : undefined,\n\t\t\treason: typeof reason === \"string\" && reason.length > 0 ? reason : undefined,\n\t\t\twasClean: typeof wasClean === \"boolean\" ? wasClean : undefined,\n\t\t});\n\t}\n\treturn new Error(\"WebSocket closed\");\n}\n\nasync function decodeWebSocketData(data: unknown): Promise<string | null> {\n\tif (typeof data === \"string\") return data;\n\tif (data instanceof ArrayBuffer) {\n\t\treturn new TextDecoder().decode(new Uint8Array(data));\n\t}\n\tif (ArrayBuffer.isView(data)) {\n\t\tconst view = data as ArrayBufferView;\n\t\treturn new TextDecoder().decode(new Uint8Array(view.buffer, view.byteOffset, view.byteLength));\n\t}\n\tif (data && typeof data === \"object\" && \"arrayBuffer\" in data) {\n\t\tconst blobLike = data as { arrayBuffer: () => Promise<ArrayBuffer> };\n\t\tconst arrayBuffer = await blobLike.arrayBuffer();\n\t\treturn new TextDecoder().decode(new Uint8Array(arrayBuffer));\n\t}\n\treturn null;\n}\n\nasync function* parseWebSocket(\n\tsocket: WebSocketLike,\n\tsignal?: AbortSignal,\n\tidleTimeoutMs?: number,\n): AsyncGenerator<Record<string, unknown>> {\n\tconst queue: Record<string, unknown>[] = [];\n\tlet pending: (() => void) | null = null;\n\tlet done = false;\n\tlet failed: Error | null = null;\n\tlet sawCompletion = false;\n\n\tconst wake = () => {\n\t\tif (!pending) return;\n\t\tconst resolve = pending;\n\t\tpending = null;\n\t\tresolve();\n\t};\n\n\tconst onMessage: WebSocketListener = (event) => {\n\t\tvoid (async () => {\n\t\t\tlet text: string | null = null;\n\t\t\ttry {\n\t\t\t\tif (!event || typeof event !== \"object\" || !(\"data\" in event)) return;\n\t\t\t\ttext = await decodeWebSocketData((event as { data?: unknown }).data);\n\t\t\t\tif (!text) return;\n\t\t\t\tconst parsed = JSON.parse(text) as Record<string, unknown>;\n\t\t\t\tconst type = typeof parsed.type === \"string\" ? parsed.type : \"\";\n\t\t\t\tif (type === \"response.completed\" || type === \"response.done\" || type === \"response.incomplete\") {\n\t\t\t\t\tsawCompletion = true;\n\t\t\t\t\tdone = true;\n\t\t\t\t}\n\t\t\t\tqueue.push(parsed);\n\t\t\t\twake();\n\t\t\t} catch (cause) {\n\t\t\t\tfailed = new CodexProtocolError(`Invalid Codex WebSocket JSON: ${formatThrownValue(cause)}`, {\n\t\t\t\t\tcause,\n\t\t\t\t\tpayload: text,\n\t\t\t\t});\n\t\t\t\tdone = true;\n\t\t\t\twake();\n\t\t\t}\n\t\t})();\n\t};\n\n\tconst onError: WebSocketListener = (event) => {\n\t\tfailed = extractWebSocketError(event);\n\t\tdone = true;\n\t\twake();\n\t};\n\n\tconst onClose: WebSocketListener = (event) => {\n\t\tif (sawCompletion) {\n\t\t\tdone = true;\n\t\t\twake();\n\t\t\treturn;\n\t\t}\n\t\tif (!failed) {\n\t\t\tfailed = extractWebSocketCloseError(event);\n\t\t}\n\t\tdone = true;\n\t\twake();\n\t};\n\n\tconst onAbort = () => {\n\t\tfailed = new Error(\"Request was aborted\");\n\t\tdone = true;\n\t\twake();\n\t};\n\n\tsocket.addEventListener(\"message\", onMessage);\n\tsocket.addEventListener(\"error\", onError);\n\tsocket.addEventListener(\"close\", onClose);\n\tsignal?.addEventListener(\"abort\", onAbort);\n\n\ttry {\n\t\twhile (true) {\n\t\t\tif (signal?.aborted) {\n\t\t\t\tthrow new Error(\"Request was aborted\");\n\t\t\t}\n\t\t\tif (queue.length > 0) {\n\t\t\t\tyield queue.shift()!;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (done) break;\n\t\t\tlet timeout: ReturnType<typeof setTimeout> | undefined;\n\t\t\tawait new Promise<void>((resolve, reject) => {\n\t\t\t\tpending = resolve;\n\t\t\t\tif (idleTimeoutMs !== undefined && idleTimeoutMs > 0) {\n\t\t\t\t\ttimeout = setTimeout(() => {\n\t\t\t\t\t\tconst error = new Error(`WebSocket idle timeout after ${idleTimeoutMs}ms`);\n\t\t\t\t\t\tfailed = error;\n\t\t\t\t\t\tdone = true;\n\t\t\t\t\t\tpending = null;\n\t\t\t\t\t\tcloseWebSocketSilently(socket, 1000, \"idle_timeout\");\n\t\t\t\t\t\treject(error);\n\t\t\t\t\t}, idleTimeoutMs);\n\t\t\t\t}\n\t\t\t}).finally(() => {\n\t\t\t\tif (timeout) {\n\t\t\t\t\tclearTimeout(timeout);\n\t\t\t\t}\n\t\t\t});\n\t\t}\n\n\t\tif (failed) {\n\t\t\tthrow failed;\n\t\t}\n\t\tif (!sawCompletion) {\n\t\t\tthrow new Error(\"WebSocket stream closed before response.completed\");\n\t\t}\n\t} finally {\n\t\tsocket.removeEventListener(\"message\", onMessage);\n\t\tsocket.removeEventListener(\"error\", onError);\n\t\tsocket.removeEventListener(\"close\", onClose);\n\t\tsignal?.removeEventListener(\"abort\", onAbort);\n\t}\n}\n\nfunction requestBodyWithoutInput(body: RequestBody): RequestBody {\n\tconst { input: _input, previous_response_id: _previousResponseId, ...rest } = body;\n\treturn rest;\n}\n\nfunction responseInputsEqual(a: ResponseInput | undefined, b: ResponseInput | undefined): boolean {\n\treturn JSON.stringify(a ?? []) === JSON.stringify(b ?? []);\n}\n\nfunction requestBodiesMatchExceptInput(a: RequestBody, b: RequestBody): boolean {\n\treturn JSON.stringify(requestBodyWithoutInput(a)) === JSON.stringify(requestBodyWithoutInput(b));\n}\n\nfunction getCachedWebSocketInputDelta(\n\tbody: RequestBody,\n\tcontinuation: CachedWebSocketContinuationState,\n): ResponseInput | undefined {\n\tif (!requestBodiesMatchExceptInput(body, continuation.lastRequestBody)) {\n\t\treturn undefined;\n\t}\n\n\tconst currentInput = body.input ?? [];\n\tconst baseline = [...(continuation.lastRequestBody.input ?? []), ...continuation.lastResponseItems];\n\tif (currentInput.length < baseline.length) {\n\t\treturn undefined;\n\t}\n\n\tconst prefix = currentInput.slice(0, baseline.length);\n\tif (!responseInputsEqual(prefix, baseline)) {\n\t\treturn undefined;\n\t}\n\n\treturn currentInput.slice(baseline.length);\n}\n\nfunction buildCachedWebSocketRequestBody(entry: CachedWebSocketConnection, body: RequestBody): RequestBody {\n\tconst continuation = entry.continuation;\n\tif (!continuation) {\n\t\treturn body;\n\t}\n\n\tconst delta = getCachedWebSocketInputDelta(body, continuation);\n\tif (!delta || !continuation.lastResponseId) {\n\t\tentry.continuation = undefined;\n\t\treturn body;\n\t}\n\n\treturn {\n\t\t...body,\n\t\tprevious_response_id: continuation.lastResponseId,\n\t\tinput: delta,\n\t};\n}\n\nasync function* startWebSocketOutputOnFirstEvent(\n\tevents: AsyncIterable<ResponseStreamEvent>,\n\toutput: AssistantMessage,\n\tstream: AssistantMessageEventStream,\n\tonStart: () => void,\n): AsyncGenerator<ResponseStreamEvent> {\n\tlet started = false;\n\tfor await (const event of events) {\n\t\tif (!started) {\n\t\t\tstarted = true;\n\t\t\tonStart();\n\t\t\tstream.push({ type: \"start\", partial: output });\n\t\t}\n\t\tyield event;\n\t}\n}\n\nasync function processWebSocketStream(\n\turl: string,\n\tbody: RequestBody,\n\theaders: Headers,\n\toutput: AssistantMessage,\n\tstream: AssistantMessageEventStream,\n\tmodel: Model<\"openai-codex-responses\">,\n\tonStart: () => void,\n\tidleTimeoutMs: number | undefined,\n\twebsocketConnectTimeoutMs: number | undefined,\n\toptions?: OpenAICodexResponsesOptions,\n): Promise<void> {\n\tconst { socket, entry, reused, release } = await acquireWebSocket(\n\t\turl,\n\t\theaders,\n\t\toptions?.sessionId,\n\t\toptions?.signal,\n\t\twebsocketConnectTimeoutMs,\n\t\toptions?.env,\n\t);\n\tlet keepConnection = true;\n\tconst useCachedContext = options?.transport === \"websocket-cached\" || options?.transport === \"auto\";\n\t// ChatGPT Codex Responses rejects `store: true` (\"Store must be set to false\").\n\t// WebSocket continuation still works via connection-scoped previous_response_id state.\n\tconst fullBody = body;\n\tconst requestBody = useCachedContext && entry ? buildCachedWebSocketRequestBody(entry, fullBody) : fullBody;\n\tconst stats = options?.sessionId ? getOrCreateWebSocketDebugStats(options.sessionId) : undefined;\n\tif (stats) {\n\t\tstats.requests++;\n\t\tif (reused) stats.connectionsReused++;\n\t\telse stats.connectionsCreated++;\n\t\tif (useCachedContext) stats.cachedContextRequests++;\n\t\tif (requestBody.store === true) stats.storeTrueRequests++;\n\t\tstats.lastInputItems = requestBody.input?.length ?? 0;\n\t\tif (requestBody.previous_response_id) {\n\t\t\tstats.deltaRequests++;\n\t\t\tstats.lastDeltaInputItems = requestBody.input?.length ?? 0;\n\t\t\tstats.lastPreviousResponseId = requestBody.previous_response_id;\n\t\t} else {\n\t\t\tstats.fullContextRequests++;\n\t\t\tstats.lastDeltaInputItems = undefined;\n\t\t\tstats.lastPreviousResponseId = undefined;\n\t\t}\n\t}\n\ttry {\n\t\tsocket.send(JSON.stringify({ type: \"response.create\", ...requestBody }));\n\t\tawait processResponsesStream(\n\t\t\tstartWebSocketOutputOnFirstEvent(\n\t\t\t\tmapCodexEvents(parseWebSocket(socket, options?.signal, idleTimeoutMs)),\n\t\t\t\toutput,\n\t\t\t\tstream,\n\t\t\t\tonStart,\n\t\t\t),\n\t\t\toutput,\n\t\t\tstream,\n\t\t\tmodel,\n\t\t\t{\n\t\t\t\tserviceTier: options?.serviceTier,\n\t\t\t\tresolveServiceTier: resolveCodexServiceTier,\n\t\t\t\tapplyServiceTierPricing: (usage, serviceTier) => applyServiceTierPricing(usage, serviceTier, model),\n\t\t\t},\n\t\t);\n\t\tif (options?.signal?.aborted) {\n\t\t\tkeepConnection = false;\n\t\t} else if (useCachedContext && entry && output.responseId) {\n\t\t\tconst responseItems = convertResponsesMessages(model, { messages: [output] }, CODEX_TOOL_CALL_PROVIDERS, {\n\t\t\t\tincludeSystemPrompt: false,\n\t\t\t}).filter((item) => item.type !== \"function_call_output\");\n\t\t\tentry.continuation = {\n\t\t\t\tlastRequestBody: fullBody,\n\t\t\t\tlastResponseId: output.responseId,\n\t\t\t\tlastResponseItems: responseItems,\n\t\t\t};\n\t\t}\n\t} catch (error) {\n\t\tif (entry) {\n\t\t\tentry.continuation = undefined;\n\t\t}\n\t\tkeepConnection = false;\n\t\tthrow error;\n\t} finally {\n\t\trelease({ keep: keepConnection });\n\t}\n}\n\n// ============================================================================\n// Error Handling\n// ============================================================================\n\nasync function parseErrorResponse(response: Response): Promise<{ message: string; friendlyMessage?: string }> {\n\tconst raw = await response.text();\n\tlet message = raw || response.statusText || \"Request failed\";\n\tlet friendlyMessage: string | undefined;\n\n\ttry {\n\t\tconst parsed = JSON.parse(raw) as {\n\t\t\terror?: { code?: string; type?: string; message?: string; plan_type?: string; resets_at?: number };\n\t\t};\n\t\tconst err = parsed?.error;\n\t\tif (err) {\n\t\t\tconst code = err.code || err.type || \"\";\n\t\t\tif (/usage_limit_reached|usage_not_included|rate_limit_exceeded/i.test(code) || response.status === 429) {\n\t\t\t\tconst plan = err.plan_type ? ` (${err.plan_type.toLowerCase()} plan)` : \"\";\n\t\t\t\tconst mins = err.resets_at\n\t\t\t\t\t? Math.max(0, Math.round((err.resets_at * 1000 - Date.now()) / 60000))\n\t\t\t\t\t: undefined;\n\t\t\t\tconst when = mins !== undefined ? ` Try again in ~${mins} min.` : \"\";\n\t\t\t\tfriendlyMessage = `You have hit your ChatGPT usage limit${plan}.${when}`.trim();\n\t\t\t}\n\t\t\tmessage = err.message || friendlyMessage || message;\n\t\t}\n\t} catch {}\n\n\treturn { message, friendlyMessage };\n}\n\n// ============================================================================\n// Auth & Headers\n// ============================================================================\n\nfunction extractAccountId(token: string): string {\n\ttry {\n\t\tconst parts = token.split(\".\");\n\t\tif (parts.length !== 3) throw new Error(\"Invalid token\");\n\t\tconst payload = JSON.parse(atob(parts[1]));\n\t\tconst accountId = payload?.[JWT_CLAIM_PATH]?.chatgpt_account_id;\n\t\tif (!accountId) throw new Error(\"No account ID in token\");\n\t\treturn accountId;\n\t} catch {\n\t\tthrow new Error(\"Failed to extract accountId from token\");\n\t}\n}\n\nfunction createCodexRequestId(): string {\n\tif (typeof globalThis.crypto?.randomUUID === \"function\") {\n\t\treturn globalThis.crypto.randomUUID();\n\t}\n\treturn `codex_${Date.now()}_${Math.random().toString(36).slice(2, 10)}`;\n}\n\nfunction buildBaseCodexHeaders(\n\tinitHeaders: Record<string, string> | undefined,\n\tadditionalHeaders: ProviderHeaders | undefined,\n\taccountId: string,\n\ttoken: string,\n): Headers {\n\tconst headers = new Headers(initHeaders);\n\tfor (const [key, value] of Object.entries(additionalHeaders || {})) {\n\t\tif (value === null) {\n\t\t\theaders.delete(key);\n\t\t} else {\n\t\t\theaders.set(key, value);\n\t\t}\n\t}\n\theaders.set(\"Authorization\", `Bearer ${token}`);\n\theaders.set(\"chatgpt-account-id\", accountId);\n\theaders.set(\"originator\", \"pi\");\n\tconst userAgent = _os ? `pi (${_os.platform()} ${_os.release()}; ${_os.arch()})` : \"pi (browser)\";\n\theaders.set(\"User-Agent\", userAgent);\n\treturn headers;\n}\n\nfunction buildSSEHeaders(\n\tinitHeaders: Record<string, string> | undefined,\n\tadditionalHeaders: ProviderHeaders | undefined,\n\taccountId: string,\n\ttoken: string,\n\tsessionId?: string,\n): Headers {\n\tconst headers = buildBaseCodexHeaders(initHeaders, additionalHeaders, accountId, token);\n\theaders.set(\"OpenAI-Beta\", \"responses=experimental\");\n\theaders.set(\"accept\", \"text/event-stream\");\n\theaders.set(\"content-type\", \"application/json\");\n\n\tif (sessionId) {\n\t\theaders.set(\"session-id\", sessionId);\n\t\theaders.set(\"x-client-request-id\", sessionId);\n\t}\n\n\treturn headers;\n}\n\nfunction buildWebSocketHeaders(\n\tinitHeaders: Record<string, string> | undefined,\n\tadditionalHeaders: ProviderHeaders | undefined,\n\taccountId: string,\n\ttoken: string,\n\trequestId: string,\n): Headers {\n\tconst headers = buildBaseCodexHeaders(initHeaders, additionalHeaders, accountId, token);\n\theaders.delete(\"accept\");\n\theaders.delete(\"content-type\");\n\theaders.delete(\"OpenAI-Beta\");\n\theaders.delete(\"openai-beta\");\n\theaders.set(\"OpenAI-Beta\", OPENAI_BETA_RESPONSES_WEBSOCKETS);\n\theaders.set(\"x-client-request-id\", requestId);\n\theaders.set(\"session-id\", requestId);\n\treturn headers;\n}\n"]}
@@ -1,26 +1,19 @@
1
- var __rewriteRelativeImportExtension = (this && this.__rewriteRelativeImportExtension) || function (path, preserveJsx) {
2
- if (typeof path === "string" && /^\.\.?\//.test(path)) {
3
- return path.replace(/\.(tsx)$|((?:\.d)?)((?:\.[^./]+?)?)\.([cm]?)ts$/i, function (m, tsx, d, ext, cm) {
4
- return tsx ? preserveJsx ? ".jsx" : ".js" : d && (!ext || !cm) ? m : (d + ext + "." + cm.toLowerCase() + "js");
5
- });
1
+ function loadNodeOs() {
2
+ if (typeof process === "undefined" || !(process.versions?.node || process.versions?.bun)) {
3
+ return null;
6
4
  }
7
- return path;
8
- };
9
- // NEVER convert to top-level runtime imports - breaks browser/Vite builds
10
- let _os = null;
11
- const dynamicImport = (specifier) => import(__rewriteRelativeImportExtension(specifier));
12
- const NODE_OS_SPECIFIER = "node:" + "os";
13
- if (typeof process !== "undefined" && (process.versions?.node || process.versions?.bun)) {
14
- dynamicImport(NODE_OS_SPECIFIER).then((m) => {
15
- _os = m;
16
- });
5
+ return process.getBuiltinModule?.("node:os") ?? null;
17
6
  }
7
+ // NEVER convert to top-level runtime imports - breaks browser/Vite builds
8
+ const _os = loadNodeOs();
18
9
  import { clampThinkingLevel } from "../models.js";
19
10
  import { registerSessionResourceCleanup } from "../session-resources.js";
20
11
  import { combineAbortSignals } from "../utils/abort-signals.js";
21
12
  import { appendAssistantMessageDiagnostic, createAssistantMessageDiagnostic, formatThrownValue, } from "../utils/diagnostics.js";
13
+ import { formatProviderError, normalizeProviderError } from "../utils/error-body.js";
22
14
  import { AssistantMessageEventStream } from "../utils/event-stream.js";
23
15
  import { headersToRecord } from "../utils/headers.js";
16
+ import { resolveHttpProxyUrlForTarget } from "../utils/node-http-proxy.js";
24
17
  import { clampOpenAIPromptCacheKey } from "./openai-prompt-cache.js";
25
18
  import { convertResponsesMessages, convertResponsesTools, processResponsesStream } from "./openai-responses-shared.js";
26
19
  import { buildBaseOptions, clampToXhigh } from "./simple-options.js";
@@ -32,10 +25,13 @@ const JWT_CLAIM_PATH = "https://api.openai.com/auth";
32
25
  const DEFAULT_MAX_RETRIES = 0;
33
26
  const BASE_DELAY_MS = 1000;
34
27
  const DEFAULT_MAX_RETRY_DELAY_MS = 60_000;
35
- const DEFAULT_SSE_HEADER_TIMEOUT_MS = 10_000;
36
28
  const DEFAULT_WEBSOCKET_CONNECT_TIMEOUT_MS = 15_000;
29
+ // The Codex backend accepts zstd-compressed request bodies on the SSE responses
30
+ // endpoint (the same endpoint the official Codex client compresses against).
31
+ const REQUEST_COMPRESSION_ZSTD_LEVEL = 3;
37
32
  const CODEX_TOOL_CALL_PROVIDERS = new Set(["openai", "openai-codex", "opencode"]);
38
33
  const WEBSOCKET_MESSAGE_TOO_BIG_CLOSE_CODE = 1009;
34
+ const WEBSOCKET_CONNECTION_LIMIT_REACHED_CODE = "websocket_connection_limit_reached";
39
35
  const CODEX_RESPONSE_STATUSES = new Set([
40
36
  "completed",
41
37
  "incomplete",
@@ -106,23 +102,38 @@ function normalizeTimeoutMs(value) {
106
102
  }
107
103
  return Math.floor(value);
108
104
  }
109
- function createSSEHeaderTimeout() {
110
- const controller = new AbortController();
111
- let error;
112
- const timeout = setTimeout(() => {
113
- error = new Error(`Codex SSE response headers timed out after ${DEFAULT_SSE_HEADER_TIMEOUT_MS}ms`);
114
- controller.abort(error);
115
- }, DEFAULT_SSE_HEADER_TIMEOUT_MS);
116
- return {
117
- signal: controller.signal,
118
- clear: () => clearTimeout(timeout),
119
- error: () => error,
120
- };
105
+ function loadNodeZlib() {
106
+ if (typeof process === "undefined" || !(process.versions?.node || process.versions?.bun)) {
107
+ return null;
108
+ }
109
+ return process.getBuiltinModule?.("node:zlib") ?? null;
110
+ }
111
+ // Returns the zstd-compressed body bytes, or null when compression is
112
+ // unavailable (browser/Vite builds). Callers fall back to sending the
113
+ // uncompressed JSON when this returns null.
114
+ function compressRequestBodyZstd(bodyJson) {
115
+ const zlib = loadNodeZlib();
116
+ if (!zlib || typeof zlib.zstdCompressSync !== "function") {
117
+ return null;
118
+ }
119
+ try {
120
+ const compressed = zlib.zstdCompressSync(bodyJson, {
121
+ params: { [zlib.constants.ZSTD_c_compressionLevel]: REQUEST_COMPRESSION_ZSTD_LEVEL },
122
+ });
123
+ // Use the ArrayLike overload (not (buffer, offset, length)) so the result is
124
+ // typed Uint8Array<ArrayBuffer>, matching the DOM BodyInit/BufferSource type
125
+ // expected by fetch()'s RequestInit.body. The (buffer, offset, length) overload
126
+ // would infer Uint8Array<ArrayBufferLike>, which fetch's body type rejects.
127
+ return new Uint8Array(compressed);
128
+ }
129
+ catch {
130
+ return null;
131
+ }
121
132
  }
122
133
  // ============================================================================
123
134
  // Main Stream Function
124
135
  // ============================================================================
125
- export const streamOpenAICodexResponses = (model, context, options) => {
136
+ export const stream = (model, context, options) => {
126
137
  const stream = new AssistantMessageEventStream();
127
138
  (async () => {
128
139
  const output = {
@@ -157,7 +168,7 @@ export const streamOpenAICodexResponses = (model, context, options) => {
157
168
  const sseHeaders = buildSSEHeaders(model.headers, options?.headers, accountId, apiKey, options?.sessionId);
158
169
  const websocketHeaders = buildWebSocketHeaders(model.headers, options?.headers, accountId, apiKey, websocketRequestId);
159
170
  const bodyJson = JSON.stringify(body);
160
- const idleTimeoutMs = normalizeTimeoutMs(options?.timeoutMs);
171
+ const httpTimeoutMs = normalizeTimeoutMs(options?.timeoutMs);
161
172
  const websocketConnectTimeoutMs = normalizeTimeoutMs(options?.websocketConnectTimeoutMs);
162
173
  const transport = options?.transport || "auto";
163
174
  const websocketDisabledForSession = transport !== "sse" && isWebSocketSseFallbackActive(options?.sessionId);
@@ -166,40 +177,58 @@ export const streamOpenAICodexResponses = (model, context, options) => {
166
177
  }
167
178
  if (transport !== "sse" && !websocketDisabledForSession) {
168
179
  let websocketStarted = false;
169
- try {
170
- await processWebSocketStream(resolveCodexWebSocketUrl(model.baseUrl), body, websocketHeaders, output, stream, model, () => {
171
- websocketStarted = true;
172
- }, idleTimeoutMs, websocketConnectTimeoutMs, options);
173
- if (options?.signal?.aborted) {
174
- throw new Error("Request was aborted");
175
- }
176
- stream.push({
177
- type: "done",
178
- reason: output.stopReason,
179
- message: output,
180
- });
181
- stream.end();
182
- return;
183
- }
184
- catch (error) {
185
- const aborted = options?.signal?.aborted;
186
- if (aborted || isCodexNonTransportError(error)) {
187
- throw error;
180
+ let retriedWebSocketConnectionLimit = false;
181
+ while (true) {
182
+ websocketStarted = false;
183
+ try {
184
+ await processWebSocketStream(resolveCodexWebSocketUrl(model.baseUrl), body, websocketHeaders, output, stream, model, () => {
185
+ websocketStarted = true;
186
+ }, httpTimeoutMs, websocketConnectTimeoutMs, options);
187
+ if (options?.signal?.aborted) {
188
+ throw new Error("Request was aborted");
189
+ }
190
+ stream.push({
191
+ type: "done",
192
+ reason: output.stopReason,
193
+ message: output,
194
+ });
195
+ stream.end();
196
+ return;
188
197
  }
189
- appendAssistantMessageDiagnostic(output, createAssistantMessageDiagnostic("provider_transport_failure", error, {
190
- configuredTransport: transport,
191
- fallbackTransport: websocketStarted ? undefined : "sse",
192
- eventsEmitted: websocketStarted,
193
- phase: websocketStarted ? "after_message_stream_start" : "before_message_stream_start",
194
- requestBytes: new TextEncoder().encode(bodyJson).byteLength,
195
- }));
196
- recordWebSocketFailure(options?.sessionId, error);
197
- if (websocketStarted) {
198
- throw error;
198
+ catch (error) {
199
+ const aborted = options?.signal?.aborted;
200
+ const connectionLimitBeforeStart = !websocketStarted && isWebSocketConnectionLimitReachedError(error);
201
+ if (!aborted && connectionLimitBeforeStart && !retriedWebSocketConnectionLimit) {
202
+ retriedWebSocketConnectionLimit = true;
203
+ continue;
204
+ }
205
+ if (aborted || (isCodexNonTransportError(error) && !connectionLimitBeforeStart)) {
206
+ throw error;
207
+ }
208
+ appendAssistantMessageDiagnostic(output, createAssistantMessageDiagnostic("provider_transport_failure", error, {
209
+ configuredTransport: transport,
210
+ fallbackTransport: websocketStarted ? undefined : "sse",
211
+ eventsEmitted: websocketStarted,
212
+ phase: websocketStarted ? "after_message_stream_start" : "before_message_stream_start",
213
+ requestBytes: new TextEncoder().encode(bodyJson).byteLength,
214
+ }));
215
+ recordWebSocketFailure(options?.sessionId, error);
216
+ if (websocketStarted) {
217
+ throw error;
218
+ }
219
+ recordWebSocketSseFallback(options?.sessionId);
220
+ break;
199
221
  }
200
- recordWebSocketSseFallback(options?.sessionId);
201
222
  }
202
223
  }
224
+ // Compress the request body once for the SSE path. The Codex backend
225
+ // decodes Content-Encoding: zstd; the WebSocket transport above sends the
226
+ // uncompressed JSON frame, matching the official Codex client.
227
+ const compressedBody = compressRequestBodyZstd(bodyJson);
228
+ if (compressedBody) {
229
+ sseHeaders.set("content-encoding", "zstd");
230
+ }
231
+ const sseBody = compressedBody ?? bodyJson;
203
232
  // Fetch with retry logic for rate limits and transient errors
204
233
  let response;
205
234
  let lastError;
@@ -209,23 +238,24 @@ export const streamOpenAICodexResponses = (model, context, options) => {
209
238
  throw new Error("Request was aborted");
210
239
  }
211
240
  try {
212
- const headerTimeout = createSSEHeaderTimeout();
213
- const combinedSignal = combineAbortSignals([options?.signal, headerTimeout.signal]);
241
+ const headerTimeoutSignal = httpTimeoutMs !== undefined && httpTimeoutMs > 0 ? AbortSignal.timeout(httpTimeoutMs) : undefined;
242
+ const combinedSignal = combineAbortSignals([options?.signal, headerTimeoutSignal]);
214
243
  try {
215
244
  response = await fetch(resolveCodexUrl(model.baseUrl), {
216
245
  method: "POST",
217
246
  headers: sseHeaders,
218
- body: bodyJson,
247
+ body: sseBody,
219
248
  signal: combinedSignal.signal,
220
249
  });
221
250
  }
222
251
  catch (error) {
223
- const timeoutError = headerTimeout.error();
224
- throw timeoutError && !options?.signal?.aborted ? timeoutError : error;
252
+ if (headerTimeoutSignal?.aborted && !options?.signal?.aborted) {
253
+ throw new Error(`Codex SSE response headers timed out after ${httpTimeoutMs}ms`);
254
+ }
255
+ throw error;
225
256
  }
226
257
  finally {
227
258
  combinedSignal.cleanup();
228
- headerTimeout.clear();
229
259
  }
230
260
  await options?.onResponse?.({ status: response.status, headers: headersToRecord(response.headers) }, model);
231
261
  if (response.ok) {
@@ -286,23 +316,23 @@ export const streamOpenAICodexResponses = (model, context, options) => {
286
316
  delete block.partialJson;
287
317
  }
288
318
  output.stopReason = options?.signal?.aborted ? "aborted" : "error";
289
- output.errorMessage = error instanceof Error ? error.message : String(error);
319
+ output.errorMessage = formatProviderError(normalizeProviderError(error));
290
320
  stream.push({ type: "error", reason: output.stopReason, error: output });
291
321
  stream.end();
292
322
  }
293
323
  })();
294
324
  return stream;
295
325
  };
296
- export const streamSimpleOpenAICodexResponses = (model, context, options) => {
326
+ export const streamSimple = (model, context, options) => {
297
327
  const apiKey = options?.apiKey;
298
328
  if (!apiKey) {
299
329
  throw new Error(`No API key for provider: ${model.provider}`);
300
330
  }
301
- const base = buildBaseOptions(model, options, apiKey);
331
+ const base = buildBaseOptions(model, context, options, apiKey);
302
332
  const rawLevel = options?.reasoning ? clampThinkingLevel(model, options.reasoning) : undefined;
303
333
  const clampedReasoning = rawLevel === "off" ? rawLevel : clampToXhigh(rawLevel);
304
334
  const reasoningEffort = clampedReasoning === "off" ? undefined : clampedReasoning;
305
- return streamOpenAICodexResponses(model, context, {
335
+ return stream(model, context, {
306
336
  ...base,
307
337
  reasoningEffort,
308
338
  });
@@ -424,16 +454,29 @@ class CodexProtocolError extends Error {
424
454
  function isCodexNonTransportError(error) {
425
455
  return error instanceof CodexApiError || error instanceof CodexProtocolError;
426
456
  }
457
+ function isWebSocketConnectionLimitReachedError(error) {
458
+ return error instanceof CodexApiError && error.code === WEBSOCKET_CONNECTION_LIMIT_REACHED_CODE;
459
+ }
460
+ function extractCodexEventError(event) {
461
+ const nested = event.error && typeof event.error === "object" ? event.error : undefined;
462
+ return {
463
+ code: typeof event.code === "string" ? event.code : typeof nested?.code === "string" ? nested.code : undefined,
464
+ message: typeof event.message === "string"
465
+ ? event.message
466
+ : typeof nested?.message === "string"
467
+ ? nested.message
468
+ : undefined,
469
+ };
470
+ }
427
471
  async function* mapCodexEvents(events) {
428
472
  for await (const event of events) {
429
473
  const type = typeof event.type === "string" ? event.type : undefined;
430
474
  if (!type)
431
475
  continue;
432
476
  if (type === "error") {
433
- const code = event.code || "";
434
- const message = event.message || "";
477
+ const { code, message } = extractCodexEventError(event);
435
478
  throw new CodexApiError(`Codex error: ${message || code || JSON.stringify(event)}`, {
436
- code: code || undefined,
479
+ code,
437
480
  payload: event,
438
481
  });
439
482
  }
@@ -527,6 +570,7 @@ async function* parseSSE(response, signal) {
527
570
  // ============================================================================
528
571
  const OPENAI_BETA_RESPONSES_WEBSOCKETS = "responses_websockets=2026-02-06";
529
572
  const SESSION_WEBSOCKET_CACHE_TTL_MS = 5 * 60 * 1000;
573
+ const SESSION_WEBSOCKET_MAX_AGE_MS = 55 * 60 * 1000;
530
574
  const websocketSessionCache = new Map();
531
575
  const websocketDebugStats = new Map();
532
576
  const websocketSseFallbackSessions = new Set();
@@ -601,16 +645,13 @@ function recordWebSocketFailure(sessionId, error) {
601
645
  stats.websocketFallbackActive = true;
602
646
  }
603
647
  let _cachedWebsocket = null;
604
- async function getWebSocketConstructor() {
605
- if (_cachedWebsocket)
648
+ async function getWebSocketConstructor(env) {
649
+ if (!env && _cachedWebsocket)
606
650
  return _cachedWebsocket;
607
651
  // bun doesn't respect http proxy envs, ref: https://github.com/oven-sh/bun/issues/15489
608
652
  // TODO: remove this when bun supports proxy envs in websocket.
609
- if (process?.versions?.bun &&
610
- (process.env.HTTP_PROXY || process.env.HTTPS_PROXY || process.env.http_proxy || process.env.https_proxy)) {
611
- const m = await dynamicImport("proxy-from-env");
612
- const getProxyForUrl = m.getProxyForUrl;
613
- _cachedWebsocket = class extends WebSocket {
653
+ if (typeof process !== "undefined" && process.versions?.bun) {
654
+ const WebSocketWithProxy = class extends WebSocket {
614
655
  constructor(url, options) {
615
656
  let _opts = {};
616
657
  if (Array.isArray(options) || typeof options === "string") {
@@ -619,11 +660,14 @@ async function getWebSocketConstructor() {
619
660
  else {
620
661
  _opts = { ...options };
621
662
  }
622
- const proxy = getProxyForUrl(url.toString().replace(/^wss:/, "https:").replace(/^ws:/, "http:"));
623
- super(url, { ..._opts, ...(proxy ? { proxy } : {}) });
663
+ const proxyUrl = resolveHttpProxyUrlForTarget(url.toString().replace(/^wss:/, "https:").replace(/^ws:/, "http:"), env);
664
+ super(url, { ..._opts, ...(proxyUrl ? { proxy: proxyUrl.toString() } : {}) });
624
665
  }
625
666
  };
626
- return _cachedWebsocket;
667
+ if (!env) {
668
+ _cachedWebsocket = WebSocketWithProxy;
669
+ }
670
+ return WebSocketWithProxy;
627
671
  }
628
672
  const ctor = globalThis.WebSocket;
629
673
  if (typeof ctor !== "function")
@@ -651,6 +695,9 @@ function isWebSocketReusable(socket) {
651
695
  // If readyState is unavailable, assume the runtime keeps it open/reusable.
652
696
  return readyState === undefined || readyState === 1;
653
697
  }
698
+ function isWebSocketSessionExpired(entry) {
699
+ return Date.now() - entry.createdAt >= SESSION_WEBSOCKET_MAX_AGE_MS;
700
+ }
654
701
  function closeWebSocketSilently(socket, code = 1000, reason = "done") {
655
702
  try {
656
703
  socket.close(code, reason);
@@ -668,8 +715,8 @@ function scheduleSessionWebSocketExpiry(sessionId, entry) {
668
715
  websocketSessionCache.delete(sessionId);
669
716
  }, SESSION_WEBSOCKET_CACHE_TTL_MS);
670
717
  }
671
- async function connectWebSocket(url, headers, signal, connectTimeoutMs = DEFAULT_WEBSOCKET_CONNECT_TIMEOUT_MS) {
672
- const WebSocketCtor = await getWebSocketConstructor();
718
+ async function connectWebSocket(url, headers, signal, connectTimeoutMs = DEFAULT_WEBSOCKET_CONNECT_TIMEOUT_MS, env) {
719
+ const WebSocketCtor = await getWebSocketConstructor(env);
673
720
  if (!WebSocketCtor) {
674
721
  throw new Error("WebSocket transport is not available in this runtime");
675
722
  }
@@ -736,9 +783,9 @@ async function connectWebSocket(url, headers, signal, connectTimeoutMs = DEFAULT
736
783
  }
737
784
  });
738
785
  }
739
- async function acquireWebSocket(url, headers, sessionId, signal, connectTimeoutMs) {
786
+ async function acquireWebSocket(url, headers, sessionId, signal, connectTimeoutMs, env) {
740
787
  if (!sessionId) {
741
- const socket = await connectWebSocket(url, headers, signal, connectTimeoutMs);
788
+ const socket = await connectWebSocket(url, headers, signal, connectTimeoutMs, env);
742
789
  return {
743
790
  socket,
744
791
  reused: false,
@@ -751,7 +798,11 @@ async function acquireWebSocket(url, headers, sessionId, signal, connectTimeoutM
751
798
  clearTimeout(cached.idleTimer);
752
799
  cached.idleTimer = undefined;
753
800
  }
754
- if (!cached.busy && isWebSocketReusable(cached.socket)) {
801
+ if (!cached.busy && isWebSocketSessionExpired(cached)) {
802
+ closeWebSocketSilently(cached.socket, 1000, "connection_age_limit");
803
+ websocketSessionCache.delete(sessionId);
804
+ }
805
+ else if (!cached.busy && isWebSocketReusable(cached.socket)) {
755
806
  cached.busy = true;
756
807
  return {
757
808
  socket: cached.socket,
@@ -769,7 +820,7 @@ async function acquireWebSocket(url, headers, sessionId, signal, connectTimeoutM
769
820
  };
770
821
  }
771
822
  if (cached.busy) {
772
- const socket = await connectWebSocket(url, headers, signal, connectTimeoutMs);
823
+ const socket = await connectWebSocket(url, headers, signal, connectTimeoutMs, env);
773
824
  return {
774
825
  socket,
775
826
  reused: false,
@@ -783,8 +834,8 @@ async function acquireWebSocket(url, headers, sessionId, signal, connectTimeoutM
783
834
  websocketSessionCache.delete(sessionId);
784
835
  }
785
836
  }
786
- const socket = await connectWebSocket(url, headers, signal, connectTimeoutMs);
787
- const entry = { socket, busy: true };
837
+ const socket = await connectWebSocket(url, headers, signal, connectTimeoutMs, env);
838
+ const entry = { socket, busy: true, createdAt: Date.now() };
788
839
  websocketSessionCache.set(sessionId, entry);
789
840
  return {
790
841
  socket,
@@ -1023,7 +1074,7 @@ async function* startWebSocketOutputOnFirstEvent(events, output, stream, onStart
1023
1074
  }
1024
1075
  }
1025
1076
  async function processWebSocketStream(url, body, headers, output, stream, model, onStart, idleTimeoutMs, websocketConnectTimeoutMs, options) {
1026
- const { socket, entry, reused, release } = await acquireWebSocket(url, headers, options?.sessionId, options?.signal, websocketConnectTimeoutMs);
1077
+ const { socket, entry, reused, release } = await acquireWebSocket(url, headers, options?.sessionId, options?.signal, websocketConnectTimeoutMs, options?.env);
1027
1078
  let keepConnection = true;
1028
1079
  const useCachedContext = options?.transport === "websocket-cached" || options?.transport === "auto";
1029
1080
  // ChatGPT Codex Responses rejects `store: true` ("Store must be set to false").
@@ -1138,7 +1189,12 @@ function createCodexRequestId() {
1138
1189
  function buildBaseCodexHeaders(initHeaders, additionalHeaders, accountId, token) {
1139
1190
  const headers = new Headers(initHeaders);
1140
1191
  for (const [key, value] of Object.entries(additionalHeaders || {})) {
1141
- headers.set(key, value);
1192
+ if (value === null) {
1193
+ headers.delete(key);
1194
+ }
1195
+ else {
1196
+ headers.set(key, value);
1197
+ }
1142
1198
  }
1143
1199
  headers.set("Authorization", `Bearer ${token}`);
1144
1200
  headers.set("chatgpt-account-id", accountId);