@aryee337/aery-ai 0.2.28 → 0.2.29

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 (417) hide show
  1. package/CHANGELOG.md +2914 -0
  2. package/README.md +614 -813
  3. package/package.json +140 -105
  4. package/src/api-registry.ts +96 -0
  5. package/src/auth-broker/client.ts +358 -0
  6. package/src/auth-broker/index.ts +5 -0
  7. package/src/auth-broker/refresher.ts +117 -0
  8. package/src/auth-broker/remote-store.ts +623 -0
  9. package/src/auth-broker/server.ts +644 -0
  10. package/src/auth-broker/types.ts +127 -0
  11. package/src/auth-broker/wire-schemas.ts +200 -0
  12. package/src/auth-gateway/http.ts +194 -0
  13. package/src/auth-gateway/index.ts +3 -0
  14. package/src/auth-gateway/server.ts +818 -0
  15. package/src/auth-gateway/types.ts +143 -0
  16. package/src/auth-storage.ts +4422 -0
  17. package/src/index.ts +54 -0
  18. package/src/model-cache.ts +129 -0
  19. package/src/model-manager.ts +469 -0
  20. package/src/model-thinking.ts +782 -0
  21. package/src/models.json +83530 -0
  22. package/src/models.json.d.ts +9 -0
  23. package/src/models.ts +56 -0
  24. package/src/prompts/turn-aborted-guidance.md +4 -0
  25. package/src/provider-details.ts +90 -0
  26. package/src/provider-models/bundled-references.ts +38 -0
  27. package/src/provider-models/descriptors.ts +355 -0
  28. package/src/provider-models/google.ts +88 -0
  29. package/src/provider-models/index.ts +5 -0
  30. package/src/provider-models/ollama.ts +153 -0
  31. package/src/provider-models/openai-compat.ts +2817 -0
  32. package/src/provider-models/special.ts +67 -0
  33. package/src/providers/aery-native-client.ts +228 -0
  34. package/src/providers/aery-native-server.ts +212 -0
  35. package/src/providers/amazon-bedrock.ts +873 -0
  36. package/src/providers/anthropic-client.ts +318 -0
  37. package/src/providers/anthropic-messages-server-schema.ts +243 -0
  38. package/src/providers/anthropic-messages-server.ts +683 -0
  39. package/src/providers/anthropic-wire.ts +268 -0
  40. package/src/providers/anthropic.ts +3094 -0
  41. package/src/providers/aws-credentials.ts +501 -0
  42. package/src/providers/aws-eventstream.ts +185 -0
  43. package/src/providers/aws-sigv4.ts +218 -0
  44. package/src/providers/azure-openai-responses.ts +361 -0
  45. package/src/providers/cursor/gen/agent_pb.ts +15274 -0
  46. package/src/providers/cursor/proto/agent.proto +3526 -0
  47. package/src/providers/cursor/proto/buf.gen.yaml +6 -0
  48. package/src/providers/cursor/proto/buf.yaml +17 -0
  49. package/src/providers/cursor.ts +2621 -0
  50. package/src/providers/error-message.ts +21 -0
  51. package/src/providers/github-copilot-headers.ts +140 -0
  52. package/src/providers/gitlab-duo.ts +372 -0
  53. package/src/providers/google-auth.ts +252 -0
  54. package/src/providers/google-gemini-cli.ts +809 -0
  55. package/src/providers/google-gemini-headers.ts +41 -0
  56. package/src/providers/google-shared.ts +917 -0
  57. package/src/providers/google-types.ts +167 -0
  58. package/src/providers/google-vertex.ts +91 -0
  59. package/src/providers/google.ts +41 -0
  60. package/src/providers/grammar.ts +70 -0
  61. package/src/providers/kimi.ts +52 -0
  62. package/src/providers/mock.ts +496 -0
  63. package/src/providers/ollama.ts +644 -0
  64. package/src/providers/openai-anthropic-shim.ts +138 -0
  65. package/src/providers/openai-chat-server-schema.ts +252 -0
  66. package/src/providers/openai-chat-server.ts +647 -0
  67. package/src/providers/openai-codex/constants.ts +43 -0
  68. package/src/providers/openai-codex/request-transformer.ts +161 -0
  69. package/src/providers/openai-codex/response-handler.ts +81 -0
  70. package/src/providers/openai-codex-responses.ts +3018 -0
  71. package/src/providers/openai-completions-compat.ts +300 -0
  72. package/src/providers/openai-completions.ts +1979 -0
  73. package/src/providers/openai-responses-server-schema.ts +290 -0
  74. package/src/providers/openai-responses-server.ts +1183 -0
  75. package/src/providers/openai-responses-shared.ts +873 -0
  76. package/src/providers/openai-responses.ts +679 -0
  77. package/src/providers/register-builtins.ts +436 -0
  78. package/src/providers/synthetic.ts +50 -0
  79. package/src/providers/transform-messages.ts +382 -0
  80. package/src/providers/vision-guard.ts +31 -0
  81. package/src/providers/xai-responses.ts +82 -0
  82. package/src/rate-limit-utils.ts +84 -0
  83. package/src/stream.ts +1065 -0
  84. package/src/types.ts +944 -0
  85. package/src/usage/claude.ts +482 -0
  86. package/src/usage/gemini.ts +250 -0
  87. package/src/usage/github-copilot.ts +421 -0
  88. package/src/usage/google-antigravity.ts +201 -0
  89. package/src/usage/kimi.ts +271 -0
  90. package/src/usage/minimax-code.ts +31 -0
  91. package/src/usage/openai-codex.ts +503 -0
  92. package/src/usage/shared.ts +10 -0
  93. package/src/usage/zai.ts +247 -0
  94. package/src/usage.ts +185 -0
  95. package/src/utils/abort.ts +51 -0
  96. package/src/utils/abortable-iterator.ts +69 -0
  97. package/src/utils/anthropic-auth.ts +93 -0
  98. package/src/utils/discovery/antigravity.ts +261 -0
  99. package/src/utils/discovery/codex.ts +371 -0
  100. package/src/utils/discovery/cursor.ts +306 -0
  101. package/src/utils/discovery/gemini.ts +248 -0
  102. package/src/utils/discovery/index.ts +4 -0
  103. package/src/utils/discovery/openai-compatible.ts +224 -0
  104. package/src/utils/event-stream.ts +142 -0
  105. package/src/utils/fireworks-model-id.ts +30 -0
  106. package/src/utils/foundry.ts +8 -0
  107. package/src/utils/http-inspector.ts +176 -0
  108. package/src/utils/idle-iterator.ts +267 -0
  109. package/src/utils/json-parse.ts +182 -0
  110. package/src/utils/oauth/__tests__/xai-oauth.test.ts +107 -0
  111. package/src/utils/oauth/alibaba-coding-plan.ts +59 -0
  112. package/src/utils/oauth/anthropic.ts +273 -0
  113. package/src/utils/oauth/api-key-login.ts +87 -0
  114. package/src/utils/oauth/api-key-validation.ts +92 -0
  115. package/src/utils/oauth/callback-server.ts +276 -0
  116. package/src/utils/oauth/cerebras.ts +16 -0
  117. package/src/utils/oauth/cloudflare-ai-gateway.ts +48 -0
  118. package/src/utils/oauth/cursor.ts +157 -0
  119. package/src/utils/oauth/deepseek.ts +53 -0
  120. package/src/utils/oauth/firepass.ts +24 -0
  121. package/src/utils/oauth/fireworks.ts +15 -0
  122. package/src/utils/oauth/github-copilot.ts +362 -0
  123. package/src/utils/oauth/gitlab-duo.ts +123 -0
  124. package/src/utils/oauth/google-antigravity.ts +200 -0
  125. package/src/utils/oauth/google-gemini-cli.ts +256 -0
  126. package/src/utils/oauth/google-oauth-shared.ts +110 -0
  127. package/src/utils/oauth/huggingface.ts +62 -0
  128. package/src/utils/oauth/index.ts +484 -0
  129. package/src/utils/oauth/kagi.ts +47 -0
  130. package/src/utils/oauth/kilo.ts +87 -0
  131. package/src/utils/oauth/kimi.ts +254 -0
  132. package/src/utils/oauth/litellm.ts +47 -0
  133. package/src/utils/oauth/lm-studio.ts +38 -0
  134. package/src/utils/oauth/minimax-code.ts +78 -0
  135. package/src/utils/oauth/moonshot.ts +23 -0
  136. package/src/utils/oauth/nanogpt.ts +15 -0
  137. package/src/utils/oauth/nvidia.ts +70 -0
  138. package/src/utils/oauth/oauth.html +203 -0
  139. package/src/utils/oauth/ollama-cloud.ts +28 -0
  140. package/src/utils/oauth/ollama.ts +47 -0
  141. package/src/utils/oauth/openai-codex.ts +299 -0
  142. package/src/utils/oauth/opencode.ts +49 -0
  143. package/src/utils/oauth/openrouter.ts +20 -0
  144. package/src/utils/oauth/parallel.ts +46 -0
  145. package/src/utils/oauth/perplexity.ts +206 -0
  146. package/src/utils/oauth/pkce.ts +18 -0
  147. package/src/utils/oauth/qianfan.ts +58 -0
  148. package/src/utils/oauth/qwen-portal.ts +60 -0
  149. package/src/utils/oauth/synthetic.ts +15 -0
  150. package/src/utils/oauth/tavily.ts +46 -0
  151. package/src/utils/oauth/together.ts +16 -0
  152. package/src/utils/oauth/types.ts +99 -0
  153. package/src/utils/oauth/venice.ts +59 -0
  154. package/src/utils/oauth/vercel-ai-gateway.ts +47 -0
  155. package/src/utils/oauth/vllm.ts +40 -0
  156. package/src/utils/oauth/wafer.ts +50 -0
  157. package/src/utils/oauth/xai-oauth.ts +342 -0
  158. package/src/utils/oauth/xiaomi.ts +139 -0
  159. package/src/utils/oauth/zai.ts +60 -0
  160. package/src/utils/oauth/zenmux.ts +15 -0
  161. package/src/utils/oauth/zhipu.ts +60 -0
  162. package/src/utils/overflow.ts +137 -0
  163. package/src/utils/parse-bind.ts +54 -0
  164. package/src/utils/provider-response.ts +30 -0
  165. package/src/utils/request-debug.ts +336 -0
  166. package/src/utils/retry-after.ts +110 -0
  167. package/src/utils/retry.ts +54 -0
  168. package/src/utils/schema/CONSTRAINTS.md +164 -0
  169. package/src/utils/schema/adapt.ts +36 -0
  170. package/src/utils/schema/compatibility.ts +435 -0
  171. package/src/utils/schema/dereference.ts +98 -0
  172. package/src/utils/schema/draft.ts +341 -0
  173. package/src/utils/schema/equality.ts +97 -0
  174. package/src/utils/schema/fields.ts +191 -0
  175. package/src/utils/schema/index.ts +13 -0
  176. package/src/utils/schema/json-schema-validator.ts +577 -0
  177. package/src/utils/schema/meta-validator.ts +167 -0
  178. package/src/utils/schema/normalize.ts +1588 -0
  179. package/src/utils/schema/spill.ts +43 -0
  180. package/src/utils/schema/stamps.ts +97 -0
  181. package/src/utils/schema/types.ts +10 -0
  182. package/src/utils/schema/wire.ts +293 -0
  183. package/src/utils/schema/zod-decontaminate.ts +331 -0
  184. package/src/utils/sdk-stream-timeout.ts +43 -0
  185. package/src/utils/sse-debug.ts +289 -0
  186. package/src/utils/stream-markup-healing.ts +612 -0
  187. package/src/utils/tool-choice.ts +99 -0
  188. package/src/utils/validation.ts +1024 -0
  189. package/src/utils.ts +166 -0
  190. package/dist/api-registry.d.ts +0 -20
  191. package/dist/api-registry.d.ts.map +0 -1
  192. package/dist/api-registry.js +0 -44
  193. package/dist/api-registry.js.map +0 -1
  194. package/dist/bedrock-provider.d.ts +0 -5
  195. package/dist/bedrock-provider.d.ts.map +0 -1
  196. package/dist/bedrock-provider.js +0 -6
  197. package/dist/bedrock-provider.js.map +0 -1
  198. package/dist/cli.d.ts +0 -3
  199. package/dist/cli.d.ts.map +0 -1
  200. package/dist/cli.js +0 -130
  201. package/dist/cli.js.map +0 -1
  202. package/dist/env-api-keys.d.ts +0 -18
  203. package/dist/env-api-keys.d.ts.map +0 -1
  204. package/dist/env-api-keys.js +0 -178
  205. package/dist/env-api-keys.js.map +0 -1
  206. package/dist/image-models.d.ts +0 -10
  207. package/dist/image-models.d.ts.map +0 -1
  208. package/dist/image-models.generated.d.ts +0 -440
  209. package/dist/image-models.generated.d.ts.map +0 -1
  210. package/dist/image-models.generated.js +0 -442
  211. package/dist/image-models.generated.js.map +0 -1
  212. package/dist/image-models.js +0 -23
  213. package/dist/image-models.js.map +0 -1
  214. package/dist/images-api-registry.d.ts +0 -14
  215. package/dist/images-api-registry.d.ts.map +0 -1
  216. package/dist/images-api-registry.js +0 -22
  217. package/dist/images-api-registry.js.map +0 -1
  218. package/dist/images.d.ts +0 -4
  219. package/dist/images.d.ts.map +0 -1
  220. package/dist/images.js +0 -14
  221. package/dist/images.js.map +0 -1
  222. package/dist/index.d.ts +0 -32
  223. package/dist/index.d.ts.map +0 -1
  224. package/dist/index.js +0 -20
  225. package/dist/index.js.map +0 -1
  226. package/dist/models.d.ts +0 -18
  227. package/dist/models.d.ts.map +0 -1
  228. package/dist/models.generated.d.ts +0 -17707
  229. package/dist/models.generated.d.ts.map +0 -1
  230. package/dist/models.generated.js +0 -16561
  231. package/dist/models.generated.js.map +0 -1
  232. package/dist/models.js +0 -71
  233. package/dist/models.js.map +0 -1
  234. package/dist/oauth.d.ts +0 -2
  235. package/dist/oauth.d.ts.map +0 -1
  236. package/dist/oauth.js +0 -2
  237. package/dist/oauth.js.map +0 -1
  238. package/dist/providers/aery-error-formatting.d.ts +0 -13
  239. package/dist/providers/aery-error-formatting.d.ts.map +0 -1
  240. package/dist/providers/aery-error-formatting.js +0 -112
  241. package/dist/providers/aery-error-formatting.js.map +0 -1
  242. package/dist/providers/amazon-bedrock.d.ts +0 -38
  243. package/dist/providers/amazon-bedrock.d.ts.map +0 -1
  244. package/dist/providers/amazon-bedrock.js +0 -763
  245. package/dist/providers/amazon-bedrock.js.map +0 -1
  246. package/dist/providers/anthropic.d.ts +0 -71
  247. package/dist/providers/anthropic.d.ts.map +0 -1
  248. package/dist/providers/anthropic.js +0 -949
  249. package/dist/providers/anthropic.js.map +0 -1
  250. package/dist/providers/azure-openai-responses.d.ts +0 -15
  251. package/dist/providers/azure-openai-responses.d.ts.map +0 -1
  252. package/dist/providers/azure-openai-responses.js +0 -225
  253. package/dist/providers/azure-openai-responses.js.map +0 -1
  254. package/dist/providers/cloudflare.d.ts +0 -13
  255. package/dist/providers/cloudflare.d.ts.map +0 -1
  256. package/dist/providers/cloudflare.js +0 -26
  257. package/dist/providers/cloudflare.js.map +0 -1
  258. package/dist/providers/faux.d.ts +0 -56
  259. package/dist/providers/faux.d.ts.map +0 -1
  260. package/dist/providers/faux.js +0 -368
  261. package/dist/providers/faux.js.map +0 -1
  262. package/dist/providers/github-copilot-headers.d.ts +0 -8
  263. package/dist/providers/github-copilot-headers.d.ts.map +0 -1
  264. package/dist/providers/github-copilot-headers.js +0 -29
  265. package/dist/providers/github-copilot-headers.js.map +0 -1
  266. package/dist/providers/google-gemini-cli.d.ts +0 -74
  267. package/dist/providers/google-gemini-cli.d.ts.map +0 -1
  268. package/dist/providers/google-gemini-cli.js +0 -779
  269. package/dist/providers/google-gemini-cli.js.map +0 -1
  270. package/dist/providers/google-shared.d.ts +0 -70
  271. package/dist/providers/google-shared.d.ts.map +0 -1
  272. package/dist/providers/google-shared.js +0 -329
  273. package/dist/providers/google-shared.js.map +0 -1
  274. package/dist/providers/google-vertex.d.ts +0 -15
  275. package/dist/providers/google-vertex.d.ts.map +0 -1
  276. package/dist/providers/google-vertex.js +0 -442
  277. package/dist/providers/google-vertex.js.map +0 -1
  278. package/dist/providers/google.d.ts +0 -13
  279. package/dist/providers/google.d.ts.map +0 -1
  280. package/dist/providers/google.js +0 -400
  281. package/dist/providers/google.js.map +0 -1
  282. package/dist/providers/images/openrouter.d.ts +0 -3
  283. package/dist/providers/images/openrouter.d.ts.map +0 -1
  284. package/dist/providers/images/openrouter.js +0 -129
  285. package/dist/providers/images/openrouter.js.map +0 -1
  286. package/dist/providers/images/register-builtins.d.ts +0 -4
  287. package/dist/providers/images/register-builtins.d.ts.map +0 -1
  288. package/dist/providers/images/register-builtins.js +0 -34
  289. package/dist/providers/images/register-builtins.js.map +0 -1
  290. package/dist/providers/mistral.d.ts +0 -25
  291. package/dist/providers/mistral.d.ts.map +0 -1
  292. package/dist/providers/mistral.js +0 -535
  293. package/dist/providers/mistral.js.map +0 -1
  294. package/dist/providers/openai-codex-responses.d.ts +0 -30
  295. package/dist/providers/openai-codex-responses.d.ts.map +0 -1
  296. package/dist/providers/openai-codex-responses.js +0 -1090
  297. package/dist/providers/openai-codex-responses.js.map +0 -1
  298. package/dist/providers/openai-completions.d.ts +0 -19
  299. package/dist/providers/openai-completions.d.ts.map +0 -1
  300. package/dist/providers/openai-completions.js +0 -950
  301. package/dist/providers/openai-completions.js.map +0 -1
  302. package/dist/providers/openai-prompt-cache.d.ts +0 -3
  303. package/dist/providers/openai-prompt-cache.d.ts.map +0 -1
  304. package/dist/providers/openai-prompt-cache.js +0 -10
  305. package/dist/providers/openai-prompt-cache.js.map +0 -1
  306. package/dist/providers/openai-responses-shared.d.ts +0 -18
  307. package/dist/providers/openai-responses-shared.d.ts.map +0 -1
  308. package/dist/providers/openai-responses-shared.js +0 -492
  309. package/dist/providers/openai-responses-shared.js.map +0 -1
  310. package/dist/providers/openai-responses.d.ts +0 -13
  311. package/dist/providers/openai-responses.d.ts.map +0 -1
  312. package/dist/providers/openai-responses.js +0 -237
  313. package/dist/providers/openai-responses.js.map +0 -1
  314. package/dist/providers/register-builtins.d.ts +0 -38
  315. package/dist/providers/register-builtins.d.ts.map +0 -1
  316. package/dist/providers/register-builtins.js +0 -278
  317. package/dist/providers/register-builtins.js.map +0 -1
  318. package/dist/providers/simple-options.d.ts +0 -8
  319. package/dist/providers/simple-options.d.ts.map +0 -1
  320. package/dist/providers/simple-options.js +0 -41
  321. package/dist/providers/simple-options.js.map +0 -1
  322. package/dist/providers/transform-messages.d.ts +0 -8
  323. package/dist/providers/transform-messages.d.ts.map +0 -1
  324. package/dist/providers/transform-messages.js +0 -184
  325. package/dist/providers/transform-messages.js.map +0 -1
  326. package/dist/session-resources.d.ts +0 -4
  327. package/dist/session-resources.d.ts.map +0 -1
  328. package/dist/session-resources.js +0 -22
  329. package/dist/session-resources.js.map +0 -1
  330. package/dist/stream.d.ts +0 -8
  331. package/dist/stream.d.ts.map +0 -1
  332. package/dist/stream.js +0 -27
  333. package/dist/stream.js.map +0 -1
  334. package/dist/types.d.ts +0 -498
  335. package/dist/types.d.ts.map +0 -1
  336. package/dist/types.js +0 -2
  337. package/dist/types.js.map +0 -1
  338. package/dist/utils/diagnostics.d.ts +0 -19
  339. package/dist/utils/diagnostics.d.ts.map +0 -1
  340. package/dist/utils/diagnostics.js +0 -25
  341. package/dist/utils/diagnostics.js.map +0 -1
  342. package/dist/utils/event-stream.d.ts +0 -21
  343. package/dist/utils/event-stream.d.ts.map +0 -1
  344. package/dist/utils/event-stream.js +0 -81
  345. package/dist/utils/event-stream.js.map +0 -1
  346. package/dist/utils/hash.d.ts +0 -3
  347. package/dist/utils/hash.d.ts.map +0 -1
  348. package/dist/utils/hash.js +0 -14
  349. package/dist/utils/hash.js.map +0 -1
  350. package/dist/utils/headers.d.ts +0 -2
  351. package/dist/utils/headers.d.ts.map +0 -1
  352. package/dist/utils/headers.js +0 -8
  353. package/dist/utils/headers.js.map +0 -1
  354. package/dist/utils/json-parse.d.ts +0 -16
  355. package/dist/utils/json-parse.d.ts.map +0 -1
  356. package/dist/utils/json-parse.js +0 -113
  357. package/dist/utils/json-parse.js.map +0 -1
  358. package/dist/utils/node-http-proxy.d.ts +0 -10
  359. package/dist/utils/node-http-proxy.d.ts.map +0 -1
  360. package/dist/utils/node-http-proxy.js +0 -97
  361. package/dist/utils/node-http-proxy.js.map +0 -1
  362. package/dist/utils/oauth/anthropic.d.ts +0 -25
  363. package/dist/utils/oauth/anthropic.d.ts.map +0 -1
  364. package/dist/utils/oauth/anthropic.js +0 -335
  365. package/dist/utils/oauth/anthropic.js.map +0 -1
  366. package/dist/utils/oauth/device-code.d.ts +0 -19
  367. package/dist/utils/oauth/device-code.d.ts.map +0 -1
  368. package/dist/utils/oauth/device-code.js +0 -55
  369. package/dist/utils/oauth/device-code.js.map +0 -1
  370. package/dist/utils/oauth/github-copilot.d.ts +0 -30
  371. package/dist/utils/oauth/github-copilot.d.ts.map +0 -1
  372. package/dist/utils/oauth/github-copilot.js +0 -268
  373. package/dist/utils/oauth/github-copilot.js.map +0 -1
  374. package/dist/utils/oauth/google-antigravity.d.ts +0 -26
  375. package/dist/utils/oauth/google-antigravity.d.ts.map +0 -1
  376. package/dist/utils/oauth/google-antigravity.js +0 -377
  377. package/dist/utils/oauth/google-antigravity.js.map +0 -1
  378. package/dist/utils/oauth/google-gemini-cli.d.ts +0 -26
  379. package/dist/utils/oauth/google-gemini-cli.d.ts.map +0 -1
  380. package/dist/utils/oauth/google-gemini-cli.js +0 -482
  381. package/dist/utils/oauth/google-gemini-cli.js.map +0 -1
  382. package/dist/utils/oauth/index.d.ts +0 -63
  383. package/dist/utils/oauth/index.d.ts.map +0 -1
  384. package/dist/utils/oauth/index.js +0 -131
  385. package/dist/utils/oauth/index.js.map +0 -1
  386. package/dist/utils/oauth/oauth-page.d.ts +0 -3
  387. package/dist/utils/oauth/oauth-page.d.ts.map +0 -1
  388. package/dist/utils/oauth/oauth-page.js +0 -105
  389. package/dist/utils/oauth/oauth-page.js.map +0 -1
  390. package/dist/utils/oauth/openai-codex.d.ts +0 -34
  391. package/dist/utils/oauth/openai-codex.d.ts.map +0 -1
  392. package/dist/utils/oauth/openai-codex.js +0 -385
  393. package/dist/utils/oauth/openai-codex.js.map +0 -1
  394. package/dist/utils/oauth/pkce.d.ts +0 -13
  395. package/dist/utils/oauth/pkce.d.ts.map +0 -1
  396. package/dist/utils/oauth/pkce.js +0 -31
  397. package/dist/utils/oauth/pkce.js.map +0 -1
  398. package/dist/utils/oauth/types.d.ts +0 -64
  399. package/dist/utils/oauth/types.d.ts.map +0 -1
  400. package/dist/utils/oauth/types.js +0 -2
  401. package/dist/utils/oauth/types.js.map +0 -1
  402. package/dist/utils/overflow.d.ts +0 -56
  403. package/dist/utils/overflow.d.ts.map +0 -1
  404. package/dist/utils/overflow.js +0 -151
  405. package/dist/utils/overflow.js.map +0 -1
  406. package/dist/utils/sanitize-unicode.d.ts +0 -22
  407. package/dist/utils/sanitize-unicode.d.ts.map +0 -1
  408. package/dist/utils/sanitize-unicode.js +0 -26
  409. package/dist/utils/sanitize-unicode.js.map +0 -1
  410. package/dist/utils/typebox-helpers.d.ts +0 -17
  411. package/dist/utils/typebox-helpers.d.ts.map +0 -1
  412. package/dist/utils/typebox-helpers.js +0 -21
  413. package/dist/utils/typebox-helpers.js.map +0 -1
  414. package/dist/utils/validation.d.ts +0 -18
  415. package/dist/utils/validation.d.ts.map +0 -1
  416. package/dist/utils/validation.js +0 -281
  417. package/dist/utils/validation.js.map +0 -1
@@ -0,0 +1,873 @@
1
+ import { structuredCloneJSON } from "@aryee337/aery-utils";
2
+ import type OpenAI from "openai";
3
+ import type {
4
+ ResponseCustomToolCall,
5
+ ResponseFunctionToolCall,
6
+ ResponseInput,
7
+ ResponseInputContent,
8
+ ResponseInputImage,
9
+ ResponseInputText,
10
+ ResponseOutputItem,
11
+ ResponseOutputMessage,
12
+ ResponseReasoningItem,
13
+ } from "openai/resources/responses/responses";
14
+ import { calculateCost } from "../models";
15
+ import {
16
+ type Api,
17
+ type AssistantMessage,
18
+ type ImageContent,
19
+ type Model,
20
+ resolveServiceTier,
21
+ type ServiceTier,
22
+ type StopReason,
23
+ type StreamOptions,
24
+ shouldSendServiceTier,
25
+ type TextContent,
26
+ type TextSignatureV1,
27
+ type ThinkingContent,
28
+ type ToolCall,
29
+ type ToolResultMessage,
30
+ } from "../types";
31
+ import { normalizeResponsesToolCallId } from "../utils";
32
+ import type { AssistantMessageEventStream } from "../utils/event-stream";
33
+ import { parseStreamingJson, parseStreamingJsonThrottled } from "../utils/json-parse";
34
+ import { joinTextWithImagePlaceholder, NON_VISION_IMAGE_PLACEHOLDER, partitionVisionContent } from "./vision-guard";
35
+ export const OPENAI_RESPONSES_PROGRESS_EVENT_TYPES: ReadonlySet<string> = new Set([
36
+ "response.created",
37
+ "response.output_item.added",
38
+ "response.reasoning_summary_part.added",
39
+ "response.reasoning_summary_text.delta",
40
+ "response.reasoning_summary_part.done",
41
+ "response.reasoning_text.delta",
42
+ "response.content_part.added",
43
+ "response.output_text.delta",
44
+ "response.refusal.delta",
45
+ "response.function_call_arguments.delta",
46
+ "response.function_call_arguments.done",
47
+ "response.custom_tool_call_input.delta",
48
+ "response.custom_tool_call_input.done",
49
+ "response.output_item.done",
50
+ "response.completed",
51
+ "response.failed",
52
+ "error",
53
+ ]);
54
+
55
+ export function isOpenAIResponsesProgressEvent(event: unknown): boolean {
56
+ if (!event || typeof event !== "object") return false;
57
+ const type = (event as { type?: unknown }).type;
58
+ return typeof type === "string" && OPENAI_RESPONSES_PROGRESS_EVENT_TYPES.has(type);
59
+ }
60
+
61
+ export function encodeTextSignatureV1(id: string, phase?: TextSignatureV1["phase"]): string {
62
+ const payload: TextSignatureV1 = { v: 1, id };
63
+ if (phase) payload.phase = phase;
64
+ return JSON.stringify(payload);
65
+ }
66
+
67
+ export function parseTextSignature(
68
+ signature: string | undefined,
69
+ ): { id: string; phase?: TextSignatureV1["phase"] } | undefined {
70
+ if (!signature) return undefined;
71
+ if (signature.startsWith("{")) {
72
+ try {
73
+ const parsed = JSON.parse(signature) as Partial<TextSignatureV1>;
74
+ if (parsed.v === 1 && typeof parsed.id === "string") {
75
+ if (parsed.phase === "commentary" || parsed.phase === "final_answer") {
76
+ return { id: parsed.id, phase: parsed.phase };
77
+ }
78
+ return { id: parsed.id };
79
+ }
80
+ } catch {
81
+ // Fall through to legacy plain-string handling.
82
+ }
83
+ }
84
+ return { id: signature };
85
+ }
86
+
87
+ export function encodeResponsesToolCallId(callId: string, itemId: string | null | undefined): string {
88
+ const stableItemId = itemId && itemId.length > 0 ? itemId : `fc_${Bun.hash(callId).toString(36)}`;
89
+ return `${callId}|${stableItemId}`;
90
+ }
91
+
92
+ export function normalizeResponsesToolCallIdForTransform(
93
+ id: string,
94
+ model?: Model<Api>,
95
+ source?: AssistantMessage,
96
+ ): string {
97
+ if (!id.includes("|")) return id;
98
+ const isForeignToolCall =
99
+ source != null && model != null && (source.provider !== model.provider || source.api !== model.api);
100
+ if (isForeignToolCall) {
101
+ const [callId, itemId] = id.split("|");
102
+ const normalizeIdPart = (part: string): string => {
103
+ const sanitized = part.replace(/[^a-zA-Z0-9_-]/g, "_");
104
+ const truncated = sanitized.length > 64 ? sanitized.slice(0, 64) : sanitized;
105
+ return truncated.replace(/_+$/, "");
106
+ };
107
+ const normalizedCallId = normalizeIdPart(callId);
108
+ let normalizedItemId = `fc_${Bun.hash(itemId).toString(36)}`;
109
+ if (normalizedItemId.length > 64) normalizedItemId = normalizedItemId.slice(0, 64);
110
+ return `${normalizedCallId}|${normalizedItemId}`;
111
+ }
112
+ const normalized = normalizeResponsesToolCallId(id);
113
+ return `${normalized.callId}|${normalized.itemId}`;
114
+ }
115
+
116
+ export function collectKnownCallIds(messages: ResponseInput): Set<string> {
117
+ const knownCallIds = new Set<string>();
118
+ for (const item of messages) {
119
+ if (item.type === "function_call" && typeof item.call_id === "string") {
120
+ knownCallIds.add(item.call_id);
121
+ } else if (
122
+ (item as { type?: string }).type === "custom_tool_call" &&
123
+ typeof (item as { call_id?: string }).call_id === "string"
124
+ ) {
125
+ knownCallIds.add((item as { call_id: string }).call_id);
126
+ }
127
+ }
128
+ return knownCallIds;
129
+ }
130
+
131
+ /** Scan replay items for call_ids that were originally custom tool calls. */
132
+ export function collectCustomCallIds(messages: ResponseInput): Set<string> {
133
+ const customCallIds = new Set<string>();
134
+ for (const item of messages) {
135
+ if (
136
+ (item as { type?: string }).type === "custom_tool_call" &&
137
+ typeof (item as { call_id?: string }).call_id === "string"
138
+ ) {
139
+ customCallIds.add((item as { call_id: string }).call_id);
140
+ }
141
+ }
142
+ return customCallIds;
143
+ }
144
+
145
+ /**
146
+ * Convert orphan `function_call_output` / `custom_tool_call_output` items —
147
+ * those whose `call_id` has no matching preceding `function_call` /
148
+ * `custom_tool_call` in the same input — into assistant text notes.
149
+ *
150
+ * The Responses API rejects unpaired outputs with
151
+ * `400 No tool call found for function call output with call_id …`. Orphans
152
+ * sneak in through two paths today:
153
+ *
154
+ * - A previous turn's `providerPayload` snapshot replaces the input array via
155
+ * the `dt: false` splice (see {@link convertConversationMessages}), wiping
156
+ * the matching `function_call` while leaving the matching
157
+ * `function_call_output` queued in a later `toolResult`.
158
+ * - A locally-rejected tool call (argument-validation failure, hook reject,
159
+ * aborted turn before the call streamed) produces a tool result without a
160
+ * `function_call` ever landing in any persisted provider payload.
161
+ *
162
+ * Dropping the result loses information the model needs to recover; sending
163
+ * it as-is 400s the request. Folding it into an assistant `message` preserves
164
+ * the payload (call_id + truncated output) while staying within the Responses
165
+ * input grammar. Matches the behavior of {@link transformRequestBody} in the
166
+ * codex provider — issue #1351 / regression of #472.
167
+ */
168
+ export function repairOrphanResponsesToolOutputs(input: ResponseInput): ResponseInput {
169
+ const knownCallIds = new Set<string>();
170
+ for (const item of input) {
171
+ const t = (item as { type?: string }).type;
172
+ const callId = (item as { call_id?: unknown }).call_id;
173
+ if (typeof callId !== "string") continue;
174
+ if (t === "function_call" || t === "custom_tool_call") knownCallIds.add(callId);
175
+ }
176
+ let hasOrphan = false;
177
+ for (const item of input) {
178
+ const t = (item as { type?: string }).type;
179
+ if (t !== "function_call_output" && t !== "custom_tool_call_output") continue;
180
+ const callId = (item as { call_id?: unknown }).call_id;
181
+ if (typeof callId === "string" && !knownCallIds.has(callId)) {
182
+ hasOrphan = true;
183
+ break;
184
+ }
185
+ }
186
+ if (!hasOrphan) return input;
187
+ return input.map(item => {
188
+ const t = (item as { type?: string }).type;
189
+ if (t !== "function_call_output" && t !== "custom_tool_call_output") return item;
190
+ const record = item as { call_id?: unknown; output?: unknown; name?: unknown };
191
+ const callId = record.call_id;
192
+ if (typeof callId !== "string" || knownCallIds.has(callId)) return item;
193
+ const toolName = typeof record.name === "string" && record.name.length > 0 ? record.name : "tool";
194
+ const rawOutput = record.output;
195
+ let text: string;
196
+ if (typeof rawOutput === "string") text = rawOutput;
197
+ else if (rawOutput == null) text = "";
198
+ else {
199
+ try {
200
+ text = JSON.stringify(rawOutput);
201
+ } catch {
202
+ text = String(rawOutput);
203
+ }
204
+ }
205
+ const ORPHAN_OUTPUT_LIMIT = 16_000;
206
+ if (text.length > ORPHAN_OUTPUT_LIMIT) text = `${text.slice(0, ORPHAN_OUTPUT_LIMIT)}\n...[truncated]`;
207
+ return {
208
+ type: "message",
209
+ role: "assistant",
210
+ content: `[Orphan ${toolName} result; call_id=${callId}]: ${text}`,
211
+ } as ResponseInput[number];
212
+ });
213
+ }
214
+
215
+ export function convertResponsesInputContent(
216
+ content: string | Array<TextContent | ImageContent>,
217
+ supportsImages: boolean,
218
+ ): ResponseInputContent[] | undefined {
219
+ if (typeof content === "string") {
220
+ if (content.trim().length === 0) return undefined;
221
+ return [{ type: "input_text", text: content.toWellFormed() } satisfies ResponseInputText];
222
+ }
223
+
224
+ const { textBlocks, imageBlocks, omittedImages } = partitionVisionContent(content, supportsImages);
225
+ const normalizedContent: ResponseInputContent[] = [];
226
+ for (const item of textBlocks) {
227
+ const text = item.text.toWellFormed();
228
+ if (text.trim().length === 0) continue;
229
+ normalizedContent.push({
230
+ type: "input_text",
231
+ text,
232
+ } satisfies ResponseInputText);
233
+ }
234
+ for (const item of imageBlocks) {
235
+ normalizedContent.push({
236
+ type: "input_image",
237
+ detail: "auto",
238
+ image_url: `data:${item.mimeType};base64,${item.data}`,
239
+ } satisfies ResponseInputImage);
240
+ }
241
+ if (omittedImages) {
242
+ normalizedContent.push({
243
+ type: "input_text",
244
+ text: NON_VISION_IMAGE_PLACEHOLDER,
245
+ } satisfies ResponseInputText);
246
+ }
247
+ return normalizedContent.length > 0 ? normalizedContent : undefined;
248
+ }
249
+
250
+ export function convertResponsesAssistantMessage<TApi extends Api>(
251
+ assistantMsg: AssistantMessage,
252
+ model: Model<TApi>,
253
+ msgIndex: number,
254
+ knownCallIds: Set<string>,
255
+ includeThinkingSignatures = true,
256
+ customCallIds?: Set<string>,
257
+ ): ResponseInput {
258
+ const outputItems: ResponseInput = [];
259
+ const isDifferentModel =
260
+ assistantMsg.model !== model.id && assistantMsg.provider === model.provider && assistantMsg.api === model.api;
261
+
262
+ for (const block of assistantMsg.content) {
263
+ if (block.type === "thinking" && assistantMsg.stopReason !== "error") {
264
+ if (!includeThinkingSignatures) {
265
+ continue;
266
+ }
267
+ if (block.thinkingSignature) {
268
+ outputItems.push(JSON.parse(block.thinkingSignature) as ResponseReasoningItem);
269
+ }
270
+ continue;
271
+ }
272
+
273
+ if (block.type === "text") {
274
+ const parsedSignature = parseTextSignature(block.textSignature);
275
+ let msgId = parsedSignature?.id;
276
+ if (!msgId) {
277
+ msgId = `msg_${msgIndex}`;
278
+ } else if (msgId.length > 64) {
279
+ msgId = `msg_${Bun.hash(msgId).toString(36)}`;
280
+ }
281
+ outputItems.push({
282
+ type: "message",
283
+ role: "assistant",
284
+ content: [{ type: "output_text", text: block.text.toWellFormed(), annotations: [] }],
285
+ status: "completed",
286
+ id: msgId,
287
+ phase: parsedSignature?.phase,
288
+ } satisfies ResponseOutputMessage);
289
+ continue;
290
+ }
291
+
292
+ if (block.type !== "toolCall") {
293
+ continue;
294
+ }
295
+
296
+ const normalized = normalizeResponsesToolCallId(block.id, block.customWireName ? "ctc" : "fc");
297
+ let itemId: string | undefined = normalized.itemId;
298
+ if (isDifferentModel && (itemId?.startsWith("fc_") || itemId?.startsWith("fcr_") || itemId?.startsWith("ctc_"))) {
299
+ itemId = undefined;
300
+ }
301
+ knownCallIds.add(normalized.callId);
302
+ if (block.customWireName) {
303
+ const rawInput = typeof block.arguments?.input === "string" ? block.arguments.input : "";
304
+ customCallIds?.add(normalized.callId);
305
+ outputItems.push({
306
+ type: "custom_tool_call",
307
+ id: itemId,
308
+ call_id: normalized.callId,
309
+ name: block.customWireName,
310
+ input: rawInput,
311
+ } as ResponseInput[number]);
312
+ continue;
313
+ }
314
+ outputItems.push({
315
+ type: "function_call",
316
+ id: itemId,
317
+ call_id: normalized.callId,
318
+ name: block.name,
319
+ arguments: JSON.stringify(block.arguments),
320
+ });
321
+ }
322
+
323
+ return outputItems;
324
+ }
325
+
326
+ export function appendResponsesToolResultMessages<TApi extends Api>(
327
+ messages: ResponseInput,
328
+ toolResult: ToolResultMessage,
329
+ model: Model<TApi>,
330
+ strictResponsesPairing: boolean,
331
+ knownCallIds: ReadonlySet<string>,
332
+ customCallIds?: ReadonlySet<string>,
333
+ ): void {
334
+ const supportsImages = model.input.includes("image");
335
+ const textResult = toolResult.content
336
+ .filter((block): block is TextContent => block.type === "text")
337
+ .map(block => block.text)
338
+ .join("\n");
339
+ const hasImages = toolResult.content.some((block): block is ImageContent => block.type === "image");
340
+ const omittedImages = hasImages && !supportsImages;
341
+ const normalized = normalizeResponsesToolCallId(toolResult.toolCallId);
342
+ if (strictResponsesPairing && !knownCallIds.has(normalized.callId)) {
343
+ return;
344
+ }
345
+
346
+ const output = (
347
+ omittedImages
348
+ ? joinTextWithImagePlaceholder(textResult, true)
349
+ : textResult.length > 0
350
+ ? textResult
351
+ : "(see attached image)"
352
+ ).toWellFormed();
353
+ if (customCallIds?.has(normalized.callId)) {
354
+ messages.push({
355
+ type: "custom_tool_call_output",
356
+ call_id: normalized.callId,
357
+ output,
358
+ } as ResponseInput[number]);
359
+ } else {
360
+ messages.push({
361
+ type: "function_call_output",
362
+ call_id: normalized.callId,
363
+ output,
364
+ });
365
+ }
366
+
367
+ if (!hasImages || !supportsImages) {
368
+ return;
369
+ }
370
+
371
+ const contentParts: ResponseInputContent[] = [
372
+ { type: "input_text", text: "Attached image(s) from tool result:" } satisfies ResponseInputText,
373
+ ];
374
+ for (const block of toolResult.content) {
375
+ if (block.type === "image") {
376
+ contentParts.push({
377
+ type: "input_image",
378
+ detail: "auto",
379
+ image_url: `data:${block.mimeType};base64,${block.data}`,
380
+ } satisfies ResponseInputImage);
381
+ }
382
+ }
383
+ messages.push({ role: "user", content: contentParts });
384
+ }
385
+
386
+ export interface ProcessResponsesStreamOptions {
387
+ onFirstToken?: () => void;
388
+ onOutputItemDone?: (item: ResponseOutputItem) => void;
389
+ }
390
+
391
+ export async function processResponsesStream<TApi extends Api>(
392
+ openaiStream: AsyncIterable<OpenAI.Responses.ResponseStreamEvent>,
393
+ output: AssistantMessage,
394
+ stream: AssistantMessageEventStream,
395
+ model: Model<TApi>,
396
+ options?: ProcessResponsesStreamOptions,
397
+ ): Promise<void> {
398
+ let currentItem:
399
+ | ResponseReasoningItem
400
+ | ResponseOutputMessage
401
+ | ResponseFunctionToolCall
402
+ | ResponseCustomToolCall
403
+ | null = null;
404
+ let currentBlock:
405
+ | ThinkingContent
406
+ | TextContent
407
+ | (ToolCall & { partialJson: string; lastParseLen?: number })
408
+ | null = null;
409
+ const blocks = output.content;
410
+ const blockIndex = () => blocks.length - 1;
411
+ let sawFirstToken = false;
412
+
413
+ for await (const event of openaiStream) {
414
+ if (event.type === "response.created") {
415
+ output.responseId = event.response.id;
416
+ } else if (event.type === "response.output_item.added") {
417
+ if (!sawFirstToken) {
418
+ sawFirstToken = true;
419
+ options?.onFirstToken?.();
420
+ }
421
+ const item = event.item;
422
+ if (item.type === "reasoning") {
423
+ currentItem = item;
424
+ currentBlock = { type: "thinking", thinking: "", itemId: item.id };
425
+ output.content.push(currentBlock);
426
+ stream.push({ type: "thinking_start", contentIndex: blockIndex(), partial: output });
427
+ } else if (item.type === "message") {
428
+ currentItem = item;
429
+ currentBlock = { type: "text", text: "" };
430
+ output.content.push(currentBlock);
431
+ stream.push({ type: "text_start", contentIndex: blockIndex(), partial: output });
432
+ } else if (item.type === "function_call") {
433
+ currentItem = item;
434
+ currentBlock = {
435
+ type: "toolCall",
436
+ id: encodeResponsesToolCallId(item.call_id, item.id),
437
+ name: item.name,
438
+ arguments: {},
439
+ partialJson: item.arguments || "",
440
+ };
441
+ output.content.push(currentBlock);
442
+ stream.push({ type: "toolcall_start", contentIndex: blockIndex(), partial: output });
443
+ } else if (item.type === "custom_tool_call") {
444
+ currentItem = item;
445
+ currentBlock = {
446
+ type: "toolCall",
447
+ id: encodeResponsesToolCallId(item.call_id, item.id),
448
+ // Preserve the raw wire name (e.g. `apply_patch`). The agent-loop
449
+ // dispatcher matches it against both `Tool.name` and
450
+ // `Tool.customWireName`, so this stays wire-accurate through
451
+ // history replay while still routing to the right handler.
452
+ name: item.name,
453
+ arguments: { input: item.input ?? "" },
454
+ customWireName: item.name,
455
+ // Custom tools stream a raw string, but we reuse `partialJson` as the
456
+ // accumulation buffer so later code that inspects the field still works.
457
+ partialJson: item.input ?? "",
458
+ };
459
+ output.content.push(currentBlock);
460
+ stream.push({ type: "toolcall_start", contentIndex: blockIndex(), partial: output });
461
+ }
462
+ } else if (event.type === "response.reasoning_summary_part.added") {
463
+ if (currentItem?.type === "reasoning") {
464
+ currentItem.summary = currentItem.summary || [];
465
+ currentItem.summary.push(event.part);
466
+ }
467
+ } else if (event.type === "response.reasoning_summary_text.delta") {
468
+ if (currentItem?.type === "reasoning" && currentBlock?.type === "thinking") {
469
+ currentItem.summary = currentItem.summary || [];
470
+ const lastPart = currentItem.summary[currentItem.summary.length - 1];
471
+ if (lastPart) {
472
+ currentBlock.thinking += event.delta;
473
+ lastPart.text += event.delta;
474
+ stream.push({
475
+ type: "thinking_delta",
476
+ contentIndex: blockIndex(),
477
+ delta: event.delta,
478
+ partial: output,
479
+ });
480
+ }
481
+ }
482
+ } else if (event.type === "response.reasoning_summary_part.done") {
483
+ if (currentItem?.type === "reasoning" && currentBlock?.type === "thinking") {
484
+ currentItem.summary = currentItem.summary || [];
485
+ const lastPart = currentItem.summary[currentItem.summary.length - 1];
486
+ if (lastPart) {
487
+ currentBlock.thinking += "\n\n";
488
+ lastPart.text += "\n\n";
489
+ stream.push({
490
+ type: "thinking_delta",
491
+ contentIndex: blockIndex(),
492
+ delta: "\n\n",
493
+ partial: output,
494
+ });
495
+ }
496
+ }
497
+ } else if (event.type === "response.reasoning_text.delta") {
498
+ // Raw reasoning text delta from local providers that stream thinking
499
+ // directly rather than via the OpenAI summary tracking protocol.
500
+ if (currentItem?.type === "reasoning" && currentBlock?.type === "thinking") {
501
+ currentBlock.thinking += event.delta;
502
+ stream.push({
503
+ type: "thinking_delta",
504
+ contentIndex: blockIndex(),
505
+ delta: event.delta,
506
+ partial: output,
507
+ });
508
+ }
509
+ } else if (event.type === "response.content_part.added") {
510
+ if (currentItem?.type === "message") {
511
+ currentItem.content = currentItem.content || [];
512
+ if (event.part.type === "output_text" || event.part.type === "refusal") {
513
+ currentItem.content.push(event.part);
514
+ }
515
+ }
516
+ } else if (event.type === "response.output_text.delta") {
517
+ if (currentItem?.type === "message" && currentBlock?.type === "text") {
518
+ const lastPart = currentItem.content?.[currentItem.content.length - 1];
519
+ if (lastPart?.type === "output_text") {
520
+ currentBlock.text += event.delta;
521
+ lastPart.text += event.delta;
522
+ stream.push({
523
+ type: "text_delta",
524
+ contentIndex: blockIndex(),
525
+ delta: event.delta,
526
+ partial: output,
527
+ });
528
+ }
529
+ }
530
+ } else if (event.type === "response.refusal.delta") {
531
+ if (currentItem?.type === "message" && currentBlock?.type === "text") {
532
+ const lastPart = currentItem.content?.[currentItem.content.length - 1];
533
+ if (lastPart?.type === "refusal") {
534
+ currentBlock.text += event.delta;
535
+ lastPart.refusal += event.delta;
536
+ stream.push({
537
+ type: "text_delta",
538
+ contentIndex: blockIndex(),
539
+ delta: event.delta,
540
+ partial: output,
541
+ });
542
+ }
543
+ }
544
+ } else if (event.type === "response.function_call_arguments.delta") {
545
+ if (currentItem?.type === "function_call" && currentBlock?.type === "toolCall") {
546
+ currentBlock.partialJson += event.delta;
547
+ const throttled = parseStreamingJsonThrottled(currentBlock.partialJson, currentBlock.lastParseLen ?? 0);
548
+ if (throttled) {
549
+ currentBlock.arguments = throttled.value;
550
+ currentBlock.lastParseLen = throttled.parsedLen;
551
+ }
552
+ stream.push({
553
+ type: "toolcall_delta",
554
+ contentIndex: blockIndex(),
555
+ delta: event.delta,
556
+ partial: output,
557
+ });
558
+ }
559
+ } else if (event.type === "response.function_call_arguments.done") {
560
+ if (currentItem?.type === "function_call" && currentBlock?.type === "toolCall") {
561
+ currentBlock.partialJson = event.arguments;
562
+ currentBlock.arguments = parseStreamingJson(currentBlock.partialJson);
563
+ delete (currentBlock as { partialJson?: string }).partialJson;
564
+ delete (currentBlock as { lastParseLen?: number }).lastParseLen;
565
+ }
566
+ } else if (event.type === "response.custom_tool_call_input.delta") {
567
+ if (currentItem?.type === "custom_tool_call" && currentBlock?.type === "toolCall") {
568
+ currentBlock.partialJson += event.delta;
569
+ currentBlock.arguments = { input: currentBlock.partialJson };
570
+ stream.push({
571
+ type: "toolcall_delta",
572
+ contentIndex: blockIndex(),
573
+ delta: event.delta,
574
+ partial: output,
575
+ });
576
+ }
577
+ } else if (event.type === "response.custom_tool_call_input.done") {
578
+ if (currentItem?.type === "custom_tool_call" && currentBlock?.type === "toolCall") {
579
+ currentBlock.partialJson = event.input;
580
+ currentBlock.arguments = { input: event.input };
581
+ }
582
+ } else if (event.type === "response.output_item.done") {
583
+ const item = structuredCloneJSON(event.item);
584
+ options?.onOutputItemDone?.(item);
585
+ if (item.type === "reasoning") {
586
+ const thinking =
587
+ item.summary?.length > 0
588
+ ? item.summary.map(part => part.text).join("\n\n")
589
+ : item.content?.[0]?.type === "reasoning_text"
590
+ ? (item.content[0].text ?? "")
591
+ : "";
592
+ const reasoningBlock = output.content.find(
593
+ b => b.type === "thinking" && (b as ThinkingContent).itemId === item.id,
594
+ ) as ThinkingContent | undefined;
595
+ if (reasoningBlock) {
596
+ reasoningBlock.thinking = thinking;
597
+ reasoningBlock.thinkingSignature = JSON.stringify(item);
598
+ const reasoningBlockIndex = output.content.indexOf(reasoningBlock);
599
+ stream.push({
600
+ type: "thinking_end",
601
+ contentIndex: reasoningBlockIndex,
602
+ content: thinking,
603
+ partial: output,
604
+ });
605
+ }
606
+ if ((currentBlock as ThinkingContent | null)?.itemId === item.id) currentBlock = null;
607
+ } else if (item.type === "message" && currentBlock?.type === "text") {
608
+ currentBlock.text = item.content
609
+ .map(part => (part.type === "output_text" ? (part.text ?? "") : (part.refusal ?? "")))
610
+ .join("");
611
+ currentBlock.textSignature = encodeTextSignatureV1(item.id, item.phase ?? undefined);
612
+ stream.push({
613
+ type: "text_end",
614
+ contentIndex: blockIndex(),
615
+ content: currentBlock.text,
616
+ partial: output,
617
+ });
618
+ currentBlock = null;
619
+ } else if (item.type === "function_call") {
620
+ const args =
621
+ currentBlock?.type === "toolCall" && currentBlock.partialJson
622
+ ? parseStreamingJson(currentBlock.partialJson)
623
+ : parseStreamingJson(item.arguments || "{}");
624
+ const toolCall: ToolCall = {
625
+ type: "toolCall",
626
+ id: encodeResponsesToolCallId(item.call_id, item.id),
627
+ name: item.name,
628
+ arguments: args,
629
+ };
630
+ if (currentBlock?.type === "toolCall") {
631
+ // Persist the authoritative final args on the stored block. The
632
+ // throttled delta parser may have skipped the last partial parse,
633
+ // leaving currentBlock.arguments stale (often `{}`); the emitted
634
+ // toolCall and the persisted block must agree.
635
+ currentBlock.arguments = args;
636
+ delete (currentBlock as { partialJson?: string }).partialJson;
637
+ delete (currentBlock as { lastParseLen?: number }).lastParseLen;
638
+ }
639
+ currentBlock = null;
640
+ stream.push({ type: "toolcall_end", contentIndex: blockIndex(), toolCall, partial: output });
641
+ } else if (item.type === "custom_tool_call") {
642
+ const rawInput =
643
+ currentBlock?.type === "toolCall" && currentBlock.partialJson
644
+ ? currentBlock.partialJson
645
+ : (item.input ?? "");
646
+ const toolCall: ToolCall = {
647
+ type: "toolCall",
648
+ id: encodeResponsesToolCallId(item.call_id, item.id),
649
+ name: item.name,
650
+ arguments: { input: rawInput },
651
+ customWireName: item.name,
652
+ };
653
+ currentBlock = null;
654
+ stream.push({ type: "toolcall_end", contentIndex: blockIndex(), toolCall, partial: output });
655
+ }
656
+ } else if (event.type === "response.completed") {
657
+ const response = event.response;
658
+ if (response?.id) {
659
+ output.responseId = response.id;
660
+ }
661
+ populateResponsesUsageFromResponse(output, response?.usage);
662
+ calculateCost(model, output.usage);
663
+ output.stopReason = mapOpenAIResponsesStopReason(response?.status);
664
+ if (response?.status === "failed" || response?.status === "cancelled") {
665
+ const error = response?.error ?? (response as any)?.status_details?.error;
666
+ const details = response?.incomplete_details;
667
+ const statusDetailsReason = (response as any)?.status_details?.reason;
668
+ const message = error
669
+ ? `${error.code || "unknown"}: ${error.message || "no message"}`
670
+ : details?.reason
671
+ ? `incomplete: ${details.reason}`
672
+ : typeof statusDetailsReason === "string" && statusDetailsReason.length > 0
673
+ ? `status_details: ${statusDetailsReason}`
674
+ : "Unknown error (no error details in response)";
675
+ throw new Error(message);
676
+ }
677
+ if (output.content.some(block => block.type === "toolCall") && output.stopReason === "stop") {
678
+ output.stopReason = "toolUse";
679
+ }
680
+ } else if (event.type === "error") {
681
+ throw new Error(`Error Code ${event.code}: ${event.message}` || "Unknown error");
682
+ } else if (event.type === "response.failed") {
683
+ const error = event.response?.error ?? (event.response as any)?.status_details?.error;
684
+ const details = event.response?.incomplete_details;
685
+ const message = error
686
+ ? `${error.code || "unknown"}: ${error.message || "no message"}`
687
+ : details?.reason
688
+ ? `incomplete: ${details.reason}`
689
+ : "Unknown error (no error details in response)";
690
+ throw new Error(message);
691
+ }
692
+ }
693
+ }
694
+
695
+ export function mapOpenAIResponsesStopReason(status: OpenAI.Responses.ResponseStatus | undefined): StopReason {
696
+ if (!status) return "stop";
697
+ switch (status) {
698
+ case "completed":
699
+ return "stop";
700
+ case "incomplete":
701
+ return "length";
702
+ case "failed":
703
+ case "cancelled":
704
+ return "error";
705
+ case "in_progress":
706
+ case "queued":
707
+ return "stop";
708
+ default: {
709
+ const exhaustive: never = status;
710
+ throw new Error(`Unhandled stop reason: ${exhaustive}`);
711
+ }
712
+ }
713
+ }
714
+
715
+ /** Initial empty `AssistantMessage` that streaming providers accumulate into. */
716
+ export function createInitialResponsesAssistantMessage(api: Api, provider: string, modelId: string): AssistantMessage {
717
+ return {
718
+ role: "assistant",
719
+ content: [],
720
+ api,
721
+ provider,
722
+ model: modelId,
723
+ usage: {
724
+ input: 0,
725
+ output: 0,
726
+ cacheRead: 0,
727
+ cacheWrite: 0,
728
+ totalTokens: 0,
729
+ cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },
730
+ },
731
+ stopReason: "stop",
732
+ timestamp: Date.now(),
733
+ };
734
+ }
735
+
736
+ /** Extension fields we add on top of `ResponseCreateParamsStreaming` across the Responses-family providers. */
737
+ export type ResponsesSamplingParamsExtras = {
738
+ top_p?: number;
739
+ top_k?: number;
740
+ min_p?: number;
741
+ presence_penalty?: number;
742
+ repetition_penalty?: number;
743
+ };
744
+
745
+ type CommonResponsesParams = OpenAI.Responses.ResponseCreateParamsStreaming & ResponsesSamplingParamsExtras;
746
+
747
+ type CommonSamplingOptions = Pick<
748
+ StreamOptions,
749
+ "temperature" | "topP" | "topK" | "minP" | "presencePenalty" | "repetitionPenalty" | "maxTokens"
750
+ > & { serviceTier?: ServiceTier };
751
+
752
+ /**
753
+ * Apply the common `StreamOptions` → Responses sampling-parameter mapping (max output tokens,
754
+ * temperature, top-p/k, min-p, presence/repetition penalties, service tier). Mutates `params`.
755
+ */
756
+ export function applyCommonResponsesSamplingParams<P extends CommonResponsesParams>(
757
+ params: P,
758
+ options: CommonSamplingOptions | undefined,
759
+ provider: string,
760
+ ): void {
761
+ if (options?.maxTokens) params.max_output_tokens = options.maxTokens;
762
+ if (options?.temperature !== undefined) params.temperature = options.temperature;
763
+ if (options?.topP !== undefined) params.top_p = options.topP;
764
+ if (options?.topK !== undefined) params.top_k = options.topK;
765
+ if (options?.minP !== undefined) params.min_p = options.minP;
766
+ if (options?.presencePenalty !== undefined) params.presence_penalty = options.presencePenalty;
767
+ if (options?.repetitionPenalty !== undefined) params.repetition_penalty = options.repetitionPenalty;
768
+ if (shouldSendServiceTier(options?.serviceTier, provider)) {
769
+ const resolved = resolveServiceTier(options?.serviceTier, provider);
770
+ if (resolved === "flex" || resolved === "scale" || resolved === "priority") {
771
+ params.service_tier = resolved;
772
+ }
773
+ }
774
+ }
775
+
776
+ type ReasoningOptions = {
777
+ reasoning?: string;
778
+ reasoningSummary?: "auto" | "detailed" | "concise" | null;
779
+ };
780
+
781
+ /**
782
+ * Apply reasoning-related Responses parameters: enable encrypted reasoning content for replay,
783
+ * set effort/summary when requested, and otherwise inject the GPT-5 "Juice: 0" no-reasoning hack.
784
+ * Mutates `params` and may push a developer message into `messages`.
785
+ *
786
+ * @param omitReasoningEffort - When `true`, suppresses `params.reasoning.effort` from the wire
787
+ * body. Set by `xai-responses.ts` via {@link OpenAIResponsesOptions.omitReasoningEffort} for
788
+ * xAI Grok models that return HTTP 400 on any `reasoning.effort` value (e.g. grok-build,
789
+ * grok-4.20-0309-reasoning). When `true` and `options.reasoning` is set but
790
+ * `options.reasoningSummary` is absent, `params.reasoning` is intentionally omitted from the
791
+ * wire body entirely — these models reason natively at their own internal default effort level
792
+ * without needing explicit activation. Callers that pass `options.reasoning` for such models
793
+ * should expect this documented downgrade: the model will reason, but at its default effort.
794
+ */
795
+ export function applyResponsesReasoningParams<P extends OpenAI.Responses.ResponseCreateParamsStreaming>(
796
+ params: P,
797
+ model: Model<Api>,
798
+ options: ReasoningOptions | undefined,
799
+ messages: ResponseInput,
800
+ mapEffort?: (effort: string) => string,
801
+ includeEncryptedReasoning: boolean = true,
802
+ omitReasoningEffort: boolean = false,
803
+ ): void {
804
+ if (!model.reasoning) return;
805
+ // Always request encrypted reasoning content so reasoning items can be replayed in
806
+ // multi-turn conversations when store is false (items aren't persisted server-side, so
807
+ // we must include the full content). See: https://github.com/eminent337/aery/issues/41
808
+ if (includeEncryptedReasoning) {
809
+ params.include = ["reasoning.encrypted_content"];
810
+ }
811
+
812
+ if (options?.reasoning || options?.reasoningSummary !== undefined) {
813
+ // Suppress the effort dial entirely when the upstream provider rejects
814
+ // `reasoning.effort` for this model (xAI Grok models outside the
815
+ // effort-capable allowlist 400 with "Model X does not support parameter
816
+ // reasoningEffort"). Default is false to preserve existing behavior for
817
+ // every non-xAI caller.
818
+ if (omitReasoningEffort) {
819
+ // Still honor reasoningSummary when explicitly requested; xAI
820
+ // accepts the summary field on every reasoning-capable model.
821
+ // When only options.reasoning (effort level) is set, params.reasoning
822
+ // is intentionally omitted — see @param omitReasoningEffort above.
823
+ if (options?.reasoningSummary !== undefined && options?.reasoningSummary !== null) {
824
+ type ReasoningParam = NonNullable<OpenAI.Responses.ResponseCreateParamsStreaming["reasoning"]>;
825
+ params.reasoning = { summary: options.reasoningSummary || "auto" } as P["reasoning"] & ReasoningParam;
826
+ }
827
+ } else {
828
+ const requested = options?.reasoning || "medium";
829
+ type ReasoningParam = NonNullable<OpenAI.Responses.ResponseCreateParamsStreaming["reasoning"]>;
830
+ const reasoningParams: ReasoningParam = {
831
+ effort: (mapEffort ? mapEffort(requested) : requested) as ReasoningParam["effort"],
832
+ };
833
+ if (options?.reasoningSummary !== null) {
834
+ reasoningParams.summary = options?.reasoningSummary || "auto";
835
+ }
836
+ params.reasoning = reasoningParams as P["reasoning"];
837
+ }
838
+ } else if (model.name.toLowerCase().startsWith("gpt-5")) {
839
+ // Jesus Christ, see https://community.openai.com/t/need-reasoning-false-option-for-gpt-5/1351588/7
840
+ messages.push({
841
+ role: "developer",
842
+ content: [{ type: "input_text", text: "# Juice: 0 !important" }],
843
+ });
844
+ }
845
+ }
846
+
847
+ /** Populate `output.usage` from a Responses-API `response.usage` payload. Does not invoke `calculateCost`. */
848
+ export function populateResponsesUsageFromResponse(
849
+ output: AssistantMessage,
850
+ usage:
851
+ | {
852
+ input_tokens?: number | null;
853
+ output_tokens?: number | null;
854
+ total_tokens?: number | null;
855
+ input_tokens_details?: { cached_tokens?: number | null } | null;
856
+ output_tokens_details?: { reasoning_tokens?: number | null } | null;
857
+ }
858
+ | null
859
+ | undefined,
860
+ ): void {
861
+ if (!usage) return;
862
+ const cachedTokens = usage.input_tokens_details?.cached_tokens || 0;
863
+ const reasoningTokens = usage.output_tokens_details?.reasoning_tokens || 0;
864
+ output.usage = {
865
+ input: (usage.input_tokens || 0) - cachedTokens,
866
+ output: usage.output_tokens || 0,
867
+ cacheRead: cachedTokens,
868
+ cacheWrite: 0,
869
+ totalTokens: usage.total_tokens || 0,
870
+ ...(reasoningTokens > 0 ? { reasoningTokens } : {}),
871
+ cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },
872
+ };
873
+ }