@aryee337/aery-ai 0.2.27 → 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,382 @@
1
+ import turnAbortedGuidance from "../prompts/turn-aborted-guidance.md" with { type: "text" };
2
+ import type {
3
+ Api,
4
+ AssistantMessage,
5
+ DeveloperMessage,
6
+ Message,
7
+ Model,
8
+ ToolCall,
9
+ ToolResultMessage,
10
+ UserMessage,
11
+ } from "../types";
12
+
13
+ const enum ToolCallStatus {
14
+ /** A tool result has already been emitted for this tool call; later duplicates must be skipped. */
15
+ Resolved = 1,
16
+ /** A synthetic aborted result was emitted; later real results must be skipped. */
17
+ Aborted = 2,
18
+ }
19
+
20
+ function shouldDropTruncatedThinkingOnlyAssistant(msg: AssistantMessage): boolean {
21
+ const isTruncatedStop = msg.stopReason === "length" || msg.stopReason === "error" || msg.stopReason === "aborted";
22
+ return isTruncatedStop && !msg.content.some(block => block.type === "toolCall" || block.type === "text");
23
+ }
24
+
25
+ function getLatestSurvivingAssistantIndex(messages: readonly Message[]): number {
26
+ for (let index = messages.length - 1; index >= 0; index -= 1) {
27
+ const msg = messages[index]!;
28
+ if (msg.role === "assistant" && !shouldDropTruncatedThinkingOnlyAssistant(msg)) {
29
+ return index;
30
+ }
31
+ }
32
+ return -1;
33
+ }
34
+
35
+ /**
36
+ * Normalize tool call ID for cross-provider compatibility.
37
+ * OpenAI Responses API generates IDs that are 450+ chars with special characters like `|`.
38
+ * Anthropic APIs require IDs matching ^[a-zA-Z0-9_-]+$ (max 64 chars).
39
+ *
40
+ * For aborted/errored turns, this function:
41
+ * - Preserves tool call structure (unlike converting to text summaries)
42
+ * - Injects synthetic "aborted" tool results
43
+ * - Adds a <turn-aborted> guidance marker for the model
44
+ */
45
+ export function transformMessages<TApi extends Api>(
46
+ messages: Message[],
47
+ model: Model<TApi>,
48
+ normalizeToolCallId?: (id: string, model: Model<TApi>, source: AssistantMessage) => string,
49
+ ): Message[] {
50
+ // Build a map of original tool call IDs to normalized IDs
51
+ const toolCallIdMap = new Map<string, string>();
52
+
53
+ const latestSurvivingAssistantIndex = getLatestSurvivingAssistantIndex(messages);
54
+ // First pass: transform messages (thinking blocks, tool call ID normalization)
55
+ const transformed = messages.map((msg, index) => {
56
+ // User and developer messages pass through unchanged
57
+ if (msg.role === "user" || msg.role === "developer") {
58
+ return msg;
59
+ }
60
+
61
+ // Handle toolResult messages - normalize toolCallId if we have a mapping
62
+ if (msg.role === "toolResult") {
63
+ const normalizedId = toolCallIdMap.get(msg.toolCallId);
64
+ if (normalizedId && normalizedId !== msg.toolCallId) {
65
+ return { ...msg, toolCallId: normalizedId };
66
+ }
67
+ return msg;
68
+ }
69
+
70
+ // Assistant messages need transformation check
71
+ if (msg.role === "assistant") {
72
+ const assistantMsg = msg as AssistantMessage;
73
+ const isSameModel =
74
+ assistantMsg.provider === model.provider &&
75
+ assistantMsg.api === model.api &&
76
+ assistantMsg.model === model.id;
77
+
78
+ const mustPreserveLatestAnthropicThinking =
79
+ index === latestSurvivingAssistantIndex &&
80
+ model.api === "anthropic-messages" &&
81
+ assistantMsg.api === "anthropic-messages";
82
+ // Aborted/errored messages may have partially-streamed thinking signatures.
83
+ // A partial signature is invalid and will be rejected by the API, so we must
84
+ // strip signatures from thinking blocks in these messages.
85
+ //
86
+ // Abandoned tool-use turns get the same treatment once they are no longer
87
+ // the latest assistant message. When a turn carries toolCall blocks but did
88
+ // NOT request tool execution (stopReason !== "toolUse" — e.g.
89
+ // adaptive-thinking Opus emitting tool calls and then ending the turn on
90
+ // `end_turn`/`stop`), the agent loop pairs those calls with placeholder
91
+ // tool_results to keep the tool_use/tool_result contract valid. Historical
92
+ // abandoned turns cannot safely replay their end_turn-bound signatures in
93
+ // that continuation, so stripping downgrades them to plain text downstream.
94
+ // Latest abandoned turns are exempt because Anthropic requires thinking
95
+ // blocks from its most recent response to remain byte-for-byte unmodified.
96
+ const invalidStopReason = assistantMsg.stopReason === "aborted" || assistantMsg.stopReason === "error";
97
+ const abandonedToolUse =
98
+ !invalidStopReason &&
99
+ assistantMsg.stopReason !== "toolUse" &&
100
+ assistantMsg.content.some(b => b.type === "toolCall");
101
+ const hasInvalidSignatures = invalidStopReason || abandonedToolUse;
102
+
103
+ const transformedContent = assistantMsg.content.flatMap(block => {
104
+ if (block.type === "thinking") {
105
+ // Strip untrustworthy signatures so the encoder can downgrade to text.
106
+ const sanitized =
107
+ hasInvalidSignatures && block.thinkingSignature ? { ...block, thinkingSignature: undefined } : block;
108
+ if (mustPreserveLatestAnthropicThinking) return abandonedToolUse ? block : sanitized;
109
+ // For same model: keep thinking blocks with signatures (needed for replay)
110
+ // even if the thinking text is empty (OpenAI encrypted reasoning)
111
+ if (isSameModel && sanitized.thinkingSignature) return sanitized;
112
+ // Skip empty thinking blocks, convert others to plain text
113
+ if (!sanitized.thinking || sanitized.thinking.trim() === "") return [];
114
+ if (isSameModel) return sanitized;
115
+ return {
116
+ type: "text" as const,
117
+ text: sanitized.thinking,
118
+ };
119
+ }
120
+
121
+ if (block.type === "redactedThinking") {
122
+ if (mustPreserveLatestAnthropicThinking) return block;
123
+ if (isSameModel) return block;
124
+ return [];
125
+ }
126
+
127
+ if (block.type === "text") {
128
+ if (isSameModel) return block;
129
+ return {
130
+ type: "text" as const,
131
+ text: block.text,
132
+ };
133
+ }
134
+
135
+ if (block.type === "toolCall") {
136
+ const toolCall = block as ToolCall;
137
+ let normalizedToolCall: ToolCall = toolCall;
138
+
139
+ if (!isSameModel && toolCall.thoughtSignature) {
140
+ normalizedToolCall = { ...toolCall };
141
+ delete (normalizedToolCall as { thoughtSignature?: string }).thoughtSignature;
142
+ }
143
+
144
+ if (!isSameModel && normalizeToolCallId) {
145
+ const normalizedId = normalizeToolCallId(toolCall.id, model, assistantMsg);
146
+ if (normalizedId !== toolCall.id) {
147
+ toolCallIdMap.set(toolCall.id, normalizedId);
148
+ normalizedToolCall = { ...normalizedToolCall, id: normalizedId };
149
+ }
150
+ }
151
+
152
+ return normalizedToolCall;
153
+ }
154
+
155
+ return block;
156
+ });
157
+
158
+ return {
159
+ ...assistantMsg,
160
+ content: transformedContent,
161
+ };
162
+ }
163
+ return msg;
164
+ });
165
+ const realToolResultsById = new Map<string, ToolResultMessage>();
166
+ for (const msg of transformed) {
167
+ if (msg.role === "toolResult" && !realToolResultsById.has(msg.toolCallId)) {
168
+ realToolResultsById.set(msg.toolCallId, msg);
169
+ }
170
+ }
171
+
172
+ // Anthropic rejects `tool_result` blocks whose `tool_use_id` does not appear in a prior
173
+ // `tool_use` block. After handoff/compaction folds an assistant turn into a summary
174
+ // string, the user-side `toolResult` for that turn can survive while the originating
175
+ // `tool_use` disappears — leaving an orphan that triggers HTTP 400. Track the set of
176
+ // `tool_use` ids that survive transformation so the second pass can drop orphans cleanly.
177
+ const validToolUseIds = new Set<string>();
178
+ for (const msg of transformed) {
179
+ if (msg.role !== "assistant") continue;
180
+ for (const block of msg.content) {
181
+ if (block.type === "toolCall") validToolUseIds.add(block.id);
182
+ }
183
+ }
184
+
185
+ // Second pass: ensure each surviving assistant tool call is immediately
186
+ // followed by exactly one corresponding tool result.
187
+ const result: Message[] = [];
188
+ let pendingToolCalls: ToolCall[] = [];
189
+ let pendingAbortedToolCalls = new Map<string, ToolCall>();
190
+ let pendingAbortedTimestamp: number | undefined;
191
+ // Track which tool calls already have an emitted result so delayed/duplicate
192
+ // toolResult messages cannot create a second provider-visible result.
193
+ const toolCallStatus = new Map<string, ToolCallStatus>();
194
+
195
+ const flushPendingToolCalls = (timestamp: number): void => {
196
+ if (pendingToolCalls.length === 0) return;
197
+ for (const tc of pendingToolCalls) {
198
+ if (toolCallStatus.has(tc.id)) continue;
199
+ const realToolResult = realToolResultsById.get(tc.id);
200
+ if (realToolResult) {
201
+ result.push(realToolResult);
202
+ toolCallStatus.set(tc.id, ToolCallStatus.Resolved);
203
+ continue;
204
+ }
205
+ result.push({
206
+ role: "toolResult",
207
+ toolCallId: tc.id,
208
+ toolName: tc.name,
209
+ content: [{ type: "text", text: "No result provided" }],
210
+ isError: true,
211
+ timestamp,
212
+ } as ToolResultMessage);
213
+ toolCallStatus.set(tc.id, ToolCallStatus.Resolved);
214
+ }
215
+ pendingToolCalls = [];
216
+ };
217
+
218
+ const flushPendingAbortedToolCalls = (): void => {
219
+ if (pendingAbortedTimestamp === undefined) return;
220
+ for (const tc of pendingAbortedToolCalls.values()) {
221
+ if (toolCallStatus.has(tc.id)) continue;
222
+ const realToolResult = realToolResultsById.get(tc.id);
223
+ if (realToolResult) {
224
+ result.push(realToolResult);
225
+ toolCallStatus.set(tc.id, ToolCallStatus.Resolved);
226
+ continue;
227
+ }
228
+ result.push({
229
+ role: "toolResult",
230
+ toolCallId: tc.id,
231
+ toolName: tc.name,
232
+ content: [{ type: "text", text: "aborted" }],
233
+ isError: true,
234
+ timestamp: pendingAbortedTimestamp,
235
+ } as ToolResultMessage);
236
+ toolCallStatus.set(tc.id, ToolCallStatus.Aborted);
237
+ }
238
+ result.push({
239
+ role: "developer",
240
+ content: turnAbortedGuidance,
241
+ timestamp: pendingAbortedTimestamp + 1,
242
+ } as DeveloperMessage);
243
+ pendingAbortedToolCalls = new Map();
244
+ pendingAbortedTimestamp = undefined;
245
+ };
246
+
247
+ for (let i = 0; i < transformed.length; i++) {
248
+ const msg = transformed[i];
249
+ const messageTimestamp = "timestamp" in msg && typeof msg.timestamp === "number" ? msg.timestamp : Date.now();
250
+
251
+ if (msg.role === "assistant") {
252
+ flushPendingToolCalls(messageTimestamp);
253
+ flushPendingAbortedToolCalls();
254
+
255
+ const assistantMsg = msg as AssistantMessage;
256
+
257
+ // Drop assistant turns that carry no actionable content (no `text`, no `toolCall`)
258
+ // AND were terminated by a truncating stop reason (`length` / `error` / `aborted`).
259
+ // These are produced when the provider returns `stop_reason: "max_tokens"` (or a
260
+ // stream error) mid-thinking, leaving a `[thinking]`-only message with a valid
261
+ // signature but nothing for the next turn to anchor on. Keeping it creates
262
+ // back-to-back assistant turns once the next response lands, which Anthropic
263
+ // rejects with "messages.X.content.Y: `thinking` blocks in the latest assistant
264
+ // message cannot be modified".
265
+ //
266
+ // `stopReason: "stop"` thinking-only messages are intentionally preserved: they
267
+ // represent reasoning-only assistant turns used for replay round-trips
268
+ // (OpenAI completions `reasoning_text`, Google signed thought parts).
269
+ const originalMsg = messages[i]!;
270
+ if (originalMsg.role === "assistant" && shouldDropTruncatedThinkingOnlyAssistant(originalMsg)) {
271
+ if (assistantMsg.stopReason === "error" || assistantMsg.stopReason === "aborted") {
272
+ // Still arm the aborted-turn note so downstream guidance fires.
273
+ pendingAbortedToolCalls = new Map();
274
+ pendingAbortedTimestamp = assistantMsg.timestamp;
275
+ }
276
+ continue;
277
+ }
278
+
279
+ const toolCalls = assistantMsg.content.filter(b => b.type === "toolCall") as ToolCall[];
280
+
281
+ if (assistantMsg.stopReason === "error" || assistantMsg.stopReason === "aborted") {
282
+ // Keep the assistant message with tool calls intact. Real tool results are
283
+ // emitted immediately if available; otherwise synthesize aborted results
284
+ // before the next turn boundary.
285
+ result.push(msg);
286
+ pendingAbortedToolCalls = new Map(toolCalls.map(toolCall => [toolCall.id, toolCall] as const));
287
+ pendingAbortedTimestamp = assistantMsg.timestamp;
288
+ continue;
289
+ }
290
+
291
+ if (toolCalls.length > 0) {
292
+ pendingToolCalls = toolCalls;
293
+ }
294
+
295
+ result.push(msg);
296
+ } else if (msg.role === "toolResult") {
297
+ if (toolCallStatus.has(msg.toolCallId)) continue;
298
+
299
+ if (pendingAbortedToolCalls.has(msg.toolCallId)) {
300
+ pendingAbortedToolCalls.delete(msg.toolCallId);
301
+ toolCallStatus.set(msg.toolCallId, ToolCallStatus.Resolved);
302
+ result.push(msg);
303
+ continue;
304
+ }
305
+
306
+ if (pendingToolCalls.some(tc => tc.id === msg.toolCallId)) {
307
+ toolCallStatus.set(msg.toolCallId, ToolCallStatus.Resolved);
308
+ result.push(msg);
309
+ continue;
310
+ }
311
+
312
+ if (!validToolUseIds.has(msg.toolCallId)) {
313
+ // Orphan `tool_result`: the originating `tool_use` is not present in the
314
+ // transformed history (typically because handoff/compaction folded the
315
+ // assistant message into a summary string while the user-side result
316
+ // survived). Sending the block as-is would 400 the request, so it must
317
+ // be dropped.
318
+ //
319
+ // If a pending tool-call window is still open (either normal or
320
+ // aborted), the orphan cannot be replaced with a developer note here:
321
+ //
322
+ // * Anthropic requires the next message after an assistant `tool_use`
323
+ // to be the matching `tool_result`. Inserting a developer message
324
+ // would break that contiguity.
325
+ // * Flushing pending aborted calls here would wedge synthetic results
326
+ // between the assistant turn and a real result that may still arrive
327
+ // inside the current contiguous result window.
328
+ //
329
+ // Drop the orphan silently in that case; the pending calls will be
330
+ // resolved in their own contiguous result window or at the next boundary.
331
+ if (pendingToolCalls.some(tc => !toolCallStatus.has(tc.id)) || pendingAbortedToolCalls.size > 0) {
332
+ continue;
333
+ }
334
+ // No pending tool-call window: safe to preserve the text payload so the
335
+ // model still sees what the tool returned.
336
+ //
337
+ // The note is emitted with `role: "user"` rather than `role: "developer"`
338
+ // because the developer role is elevated by some providers:
339
+ //
340
+ // * Ollama maps `developer` -> `system` (highest instruction priority).
341
+ // * OpenAI chat-completions reasoning models forward `developer` as
342
+ // `developer` (above-user instruction priority).
343
+ //
344
+ // Stale, model-untrusted tool output must not gain instruction priority
345
+ // above user/developer messages it lived alongside before compaction.
346
+ // `user` role is mapped to plain user content by every provider, so the
347
+ // content survives without ever being treated as an instruction the
348
+ // model should obey.
349
+ const textParts: string[] = [];
350
+ for (const part of msg.content) {
351
+ if (part.type === "text" && part.text.trim() !== "") textParts.push(part.text);
352
+ }
353
+ if (textParts.length > 0) {
354
+ const errorAttr = msg.isError ? ' is-error="true"' : "";
355
+ result.push({
356
+ role: "user",
357
+ content: `<stale-tool-result tool="${msg.toolName}" id="${msg.toolCallId}"${errorAttr}>\n${textParts.join("\n")}\n</stale-tool-result>`,
358
+ timestamp: messageTimestamp,
359
+ } as UserMessage);
360
+ }
361
+ }
362
+
363
+ // The matching tool_use exists elsewhere, but this result is not in
364
+ // the currently open result window. Emitting it here would break the
365
+ // provider invariant; the first real result is pulled into the correct
366
+ // slot by the pending-call flush instead.
367
+ } else if (msg.role === "user" || msg.role === "developer") {
368
+ flushPendingToolCalls(messageTimestamp);
369
+ flushPendingAbortedToolCalls();
370
+ result.push(msg);
371
+ } else {
372
+ flushPendingToolCalls(messageTimestamp);
373
+ flushPendingAbortedToolCalls();
374
+ result.push(msg);
375
+ }
376
+ }
377
+
378
+ flushPendingToolCalls(Date.now());
379
+ flushPendingAbortedToolCalls();
380
+
381
+ return result;
382
+ }
@@ -0,0 +1,31 @@
1
+ import type { ImageContent, TextContent } from "../types";
2
+
3
+ export const NON_VISION_IMAGE_PLACEHOLDER = "[image omitted: model does not support vision]";
4
+
5
+ export function partitionVisionContent(
6
+ content: ReadonlyArray<TextContent | ImageContent>,
7
+ supportsImages: boolean,
8
+ ): {
9
+ textBlocks: TextContent[];
10
+ imageBlocks: ImageContent[];
11
+ omittedImages: boolean;
12
+ } {
13
+ const textBlocks = content.filter((block): block is TextContent => block.type === "text");
14
+ const imageBlocks = content.filter((block): block is ImageContent => block.type === "image");
15
+ return {
16
+ textBlocks,
17
+ imageBlocks: supportsImages ? imageBlocks : [],
18
+ omittedImages: !supportsImages && imageBlocks.length > 0,
19
+ };
20
+ }
21
+
22
+ export function joinTextWithImagePlaceholder(text: string, omittedImages: boolean): string {
23
+ const parts: string[] = [];
24
+ if (text.length > 0) {
25
+ parts.push(text);
26
+ }
27
+ if (omittedImages) {
28
+ parts.push(NON_VISION_IMAGE_PLACEHOLDER);
29
+ }
30
+ return parts.join("\n");
31
+ }
@@ -0,0 +1,82 @@
1
+ // Ported from NousResearch/hermes-agent (MIT) — agent/transports/codex.py:182-193,
2
+ // agent/codex_responses_adapter.py:247-311, agent/model_metadata.py:263-285.
3
+ // Logic EXTRACTED into a dedicated xAI adapter so the generic OpenAI Responses
4
+ // path stays provider-agnostic and the OpenAI Codex Responses path is unaffected.
5
+
6
+ import type { Context, Model, StreamFunction } from "../types";
7
+ import {
8
+ getOpenAIResponsesCacheSessionId,
9
+ type OpenAIResponsesOptions,
10
+ streamOpenAIResponses,
11
+ } from "./openai-responses";
12
+
13
+ // xAI rejects `reasoning.effort` on grok-4 / grok-4-fast / grok-3 /
14
+ // grok-code-fast / grok-4.20-0309-* / grok-build with HTTP 400 ("Model X does
15
+ // not support parameter reasoningEffort") even though those models reason
16
+ // natively (hermes-agent/agent/transports/codex.py:127-133). Only send the
17
+ // effort dial when the target model is on this allowlist; otherwise suppress
18
+ // it via OpenAIResponsesOptions.omitReasoningEffort and let the model reason
19
+ // on its own. grok-build was previously on this list per user spec; the live
20
+ // xAI server contradicts that assumption (HTTP 400 confirmed against
21
+ // api.x.ai/v1/responses on 2026-05-17).
22
+ const GROK_EFFORT_CAPABLE_PREFIXES = ["grok-3-mini", "grok-4.20-multi-agent", "grok-4.3"] as const;
23
+
24
+ function grokSupportsReasoningEffort(modelId: string): boolean {
25
+ const name = (modelId || "").trim().toLowerCase();
26
+ if (!name) return false;
27
+ // Strip common aggregator prefixes (x-ai/, openrouter/x-ai/, xai/, ...) before matching.
28
+ const bare = name.includes("/") ? name.slice(name.lastIndexOf("/") + 1) : name;
29
+ return GROK_EFFORT_CAPABLE_PREFIXES.some(prefix => bare.startsWith(prefix));
30
+ }
31
+
32
+ /**
33
+ * xAI Grok Responses adapter (SuperGrok OAuth path).
34
+ *
35
+ * Three xAI-specific behaviors vs the generic OpenAI Responses adapter:
36
+ *
37
+ * 1. `x-grok-conv-id` header + body `prompt_cache_key` route prompt-cache
38
+ * hits on xAI's edge. Hermes uses both (agent/transports/codex.py:182-193).
39
+ * The header is undocumented by xAI; `previous_response_id` is the
40
+ * documented alternative — switch if xAI deprecates the header.
41
+ * 2. includeEncryptedReasoning=false — xAI's /v1/responses rejects replayed
42
+ * `encrypted_content` blobs minted under SuperGrok OAuth.
43
+ * 3. filterReasoningHistory=true — strip `type: "reasoning"` items from
44
+ * replayed conversation history; the blob inside is non-replayable under
45
+ * OAuth and the wrapper item 404s without it (store=false; server cannot
46
+ * resolve by id).
47
+ *
48
+ * Everything else is the generic OpenAI Responses transport. The xAI bearer
49
+ * token arrives in `options.apiKey` via AuthStorage.getApiKey() upstream, and
50
+ * the xAI base URL (`https://api.x.ai/v1`) arrives via `model.baseUrl` from
51
+ * the provider registry — not routed through this wrapper.
52
+ */
53
+ export const streamXAIResponses: StreamFunction<"openai-responses"> = (
54
+ model: Model<"openai-responses">,
55
+ context: Context,
56
+ options: OpenAIResponsesOptions = {},
57
+ ) => {
58
+ const cacheSessionId = getOpenAIResponsesCacheSessionId(options);
59
+
60
+ const xaiHeaders: Record<string, string> = { ...options?.headers };
61
+ if (cacheSessionId) {
62
+ xaiHeaders["x-grok-conv-id"] = cacheSessionId;
63
+ }
64
+
65
+ const xaiBody: Record<string, unknown> = { ...(options?.extraBody ?? {}) };
66
+ if (cacheSessionId) {
67
+ xaiBody.prompt_cache_key = cacheSessionId;
68
+ }
69
+
70
+ const xaiOptions: OpenAIResponsesOptions = {
71
+ ...options,
72
+ headers: xaiHeaders,
73
+ extraBody: xaiBody,
74
+ includeEncryptedReasoning: false,
75
+ filterReasoningHistory: true,
76
+ // Caller-passed value always wins (escape hatch for future xAI behavior
77
+ // changes); otherwise gate the effort dial on the allowlist.
78
+ omitReasoningEffort: options?.omitReasoningEffort ?? !grokSupportsReasoningEffort(model.id),
79
+ };
80
+
81
+ return streamOpenAIResponses(model, context, xaiOptions);
82
+ };
@@ -0,0 +1,84 @@
1
+ /**
2
+ * Rate limit reason classification and backoff calculation utilities.
3
+ * Ported from opencode-antigravity-auth plugin for consistency.
4
+ */
5
+
6
+ export type RateLimitReason =
7
+ | "QUOTA_EXHAUSTED"
8
+ | "RATE_LIMIT_EXCEEDED"
9
+ | "MODEL_CAPACITY_EXHAUSTED"
10
+ | "SERVER_ERROR"
11
+ | "UNKNOWN";
12
+
13
+ const QUOTA_EXHAUSTED_BACKOFF_MS = 30 * 60 * 1000; // 30 min
14
+ const RATE_LIMIT_EXCEEDED_BACKOFF_MS = 30 * 1000; // 30s
15
+ const MODEL_CAPACITY_BASE_MS = 45 * 1000; // 45s base
16
+ const MODEL_CAPACITY_JITTER_MS = 30 * 1000; // ±15s
17
+ const SERVER_ERROR_BACKOFF_MS = 20 * 1000; // 20s
18
+
19
+ /**
20
+ * Classify a rate-limit error message into a reason category.
21
+ * Priority order: MODEL_CAPACITY > RATE_LIMIT > QUOTA > SERVER_ERROR > UNKNOWN.
22
+ *
23
+ * "resource exhausted" maps to MODEL_CAPACITY (transient, short wait)
24
+ * "quota exceeded" maps to QUOTA_EXHAUSTED (long wait, switch account)
25
+ */
26
+ export function parseRateLimitReason(errorMessage: string): RateLimitReason {
27
+ const lower = errorMessage.toLowerCase();
28
+
29
+ if (
30
+ lower.includes("capacity") ||
31
+ lower.includes("overloaded") ||
32
+ lower.includes("529") ||
33
+ lower.includes("503") ||
34
+ lower.includes("resource exhausted")
35
+ ) {
36
+ return "MODEL_CAPACITY_EXHAUSTED";
37
+ }
38
+
39
+ if (
40
+ lower.includes("per minute") ||
41
+ lower.includes("rate limit") ||
42
+ lower.includes("too many requests") ||
43
+ lower.includes("presque")
44
+ ) {
45
+ return "RATE_LIMIT_EXCEEDED";
46
+ }
47
+
48
+ if (lower.includes("exhausted") || lower.includes("quota") || lower.includes("usage limit")) {
49
+ return "QUOTA_EXHAUSTED";
50
+ }
51
+
52
+ if (lower.includes("500") || lower.includes("internal error") || lower.includes("internal server error")) {
53
+ return "SERVER_ERROR";
54
+ }
55
+
56
+ return "UNKNOWN";
57
+ }
58
+
59
+ /**
60
+ * Calculate backoff delay in ms for a given rate limit reason.
61
+ * MODEL_CAPACITY gets jitter to prevent thundering herd.
62
+ */
63
+ export function calculateRateLimitBackoffMs(reason: RateLimitReason): number {
64
+ switch (reason) {
65
+ case "QUOTA_EXHAUSTED":
66
+ return QUOTA_EXHAUSTED_BACKOFF_MS;
67
+ case "RATE_LIMIT_EXCEEDED":
68
+ return RATE_LIMIT_EXCEEDED_BACKOFF_MS;
69
+ case "MODEL_CAPACITY_EXHAUSTED":
70
+ return MODEL_CAPACITY_BASE_MS + Math.random() * MODEL_CAPACITY_JITTER_MS;
71
+ case "SERVER_ERROR":
72
+ return SERVER_ERROR_BACKOFF_MS;
73
+ default:
74
+ return QUOTA_EXHAUSTED_BACKOFF_MS; // conservative default
75
+ }
76
+ }
77
+
78
+ /** Detect usage/quota limit errors in error messages (persistent, requires credential switch). */
79
+ const USAGE_LIMIT_PATTERN =
80
+ /usage.?limit|usage_limit_reached|usage_not_included|limit_reached|quota.?exceeded|resource.?exhausted/i;
81
+
82
+ export function isUsageLimitError(errorMessage: string): boolean {
83
+ return USAGE_LIMIT_PATTERN.test(errorMessage);
84
+ }