@sayknow-cli/ai 0.2.2

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 (361) hide show
  1. package/CHANGELOG.md +2788 -0
  2. package/README.md +1183 -0
  3. package/dist/types/api-registry.d.ts +30 -0
  4. package/dist/types/auth-broker/client.d.ts +66 -0
  5. package/dist/types/auth-broker/index.d.ts +5 -0
  6. package/dist/types/auth-broker/refresher.d.ts +25 -0
  7. package/dist/types/auth-broker/remote-store.d.ts +96 -0
  8. package/dist/types/auth-broker/server.d.ts +32 -0
  9. package/dist/types/auth-broker/types.d.ts +105 -0
  10. package/dist/types/auth-broker/wire-schemas.d.ts +412 -0
  11. package/dist/types/auth-gateway/http.d.ts +39 -0
  12. package/dist/types/auth-gateway/index.d.ts +3 -0
  13. package/dist/types/auth-gateway/server.d.ts +17 -0
  14. package/dist/types/auth-gateway/types.d.ts +115 -0
  15. package/dist/types/auth-storage.d.ts +660 -0
  16. package/dist/types/cli.d.ts +2 -0
  17. package/dist/types/index.d.ts +51 -0
  18. package/dist/types/model-cache.d.ts +17 -0
  19. package/dist/types/model-manager.d.ts +62 -0
  20. package/dist/types/model-thinking.d.ts +74 -0
  21. package/dist/types/models.d.ts +12 -0
  22. package/dist/types/provider-details.d.ts +24 -0
  23. package/dist/types/provider-models/bundled-references.d.ts +4 -0
  24. package/dist/types/provider-models/descriptors.d.ts +48 -0
  25. package/dist/types/provider-models/google.d.ts +20 -0
  26. package/dist/types/provider-models/index.d.ts +5 -0
  27. package/dist/types/provider-models/ollama.d.ts +7 -0
  28. package/dist/types/provider-models/openai-compat.d.ts +244 -0
  29. package/dist/types/provider-models/special.d.ts +16 -0
  30. package/dist/types/providers/amazon-bedrock.d.ts +60 -0
  31. package/dist/types/providers/anthropic-messages-server-schema.d.ts +450 -0
  32. package/dist/types/providers/anthropic-messages-server.d.ts +17 -0
  33. package/dist/types/providers/anthropic.d.ts +198 -0
  34. package/dist/types/providers/aws-credentials.d.ts +43 -0
  35. package/dist/types/providers/aws-eventstream.d.ts +38 -0
  36. package/dist/types/providers/aws-sigv4.d.ts +55 -0
  37. package/dist/types/providers/azure-openai-responses.d.ts +15 -0
  38. package/dist/types/providers/composer-discipline.d.ts +26 -0
  39. package/dist/types/providers/cursor/gen/agent_pb.d.ts +13022 -0
  40. package/dist/types/providers/cursor.d.ts +44 -0
  41. package/dist/types/providers/error-message.d.ts +27 -0
  42. package/dist/types/providers/github-copilot-headers.d.ts +40 -0
  43. package/dist/types/providers/gitlab-duo.d.ts +27 -0
  44. package/dist/types/providers/google-auth.d.ts +24 -0
  45. package/dist/types/providers/google-gemini-cli.d.ts +72 -0
  46. package/dist/types/providers/google-gemini-headers.d.ts +18 -0
  47. package/dist/types/providers/google-shared.d.ts +173 -0
  48. package/dist/types/providers/google-types.d.ts +138 -0
  49. package/dist/types/providers/google-vertex.d.ts +7 -0
  50. package/dist/types/providers/google.d.ts +4 -0
  51. package/dist/types/providers/grammar.d.ts +1 -0
  52. package/dist/types/providers/kimi.d.ts +27 -0
  53. package/dist/types/providers/mock.d.ts +175 -0
  54. package/dist/types/providers/ollama.d.ts +41 -0
  55. package/dist/types/providers/openai-anthropic-shim.d.ts +31 -0
  56. package/dist/types/providers/openai-chat-server-schema.d.ts +815 -0
  57. package/dist/types/providers/openai-chat-server.d.ts +16 -0
  58. package/dist/types/providers/openai-codex/constants.d.ts +26 -0
  59. package/dist/types/providers/openai-codex/request-transformer.d.ts +49 -0
  60. package/dist/types/providers/openai-codex/response-handler.d.ts +17 -0
  61. package/dist/types/providers/openai-codex-responses.d.ts +67 -0
  62. package/dist/types/providers/openai-completions-compat.d.ts +27 -0
  63. package/dist/types/providers/openai-completions.d.ts +33 -0
  64. package/dist/types/providers/openai-request-transform.d.ts +4 -0
  65. package/dist/types/providers/openai-responses-server-schema.d.ts +392 -0
  66. package/dist/types/providers/openai-responses-server.d.ts +17 -0
  67. package/dist/types/providers/openai-responses-shared.d.ts +89 -0
  68. package/dist/types/providers/openai-responses.d.ts +32 -0
  69. package/dist/types/providers/pi-native-client.d.ts +13 -0
  70. package/dist/types/providers/pi-native-server.d.ts +68 -0
  71. package/dist/types/providers/register-builtins.d.ts +31 -0
  72. package/dist/types/providers/synthetic.d.ts +26 -0
  73. package/dist/types/providers/transform-messages.d.ts +14 -0
  74. package/dist/types/providers/vision-guard.d.ts +8 -0
  75. package/dist/types/rate-limit-utils.d.ts +19 -0
  76. package/dist/types/stream.d.ts +43 -0
  77. package/dist/types/types.d.ts +811 -0
  78. package/dist/types/usage/claude.d.ts +3 -0
  79. package/dist/types/usage/gemini.d.ts +2 -0
  80. package/dist/types/usage/github-copilot.d.ts +7 -0
  81. package/dist/types/usage/google-antigravity.d.ts +2 -0
  82. package/dist/types/usage/grok-cli.d.ts +10 -0
  83. package/dist/types/usage/kimi.d.ts +2 -0
  84. package/dist/types/usage/minimax-code.d.ts +2 -0
  85. package/dist/types/usage/openai-codex.d.ts +3 -0
  86. package/dist/types/usage/shared.d.ts +1 -0
  87. package/dist/types/usage/zai.d.ts +2 -0
  88. package/dist/types/usage.d.ts +258 -0
  89. package/dist/types/utils/abort.d.ts +19 -0
  90. package/dist/types/utils/anthropic-auth.d.ts +31 -0
  91. package/dist/types/utils/discovery/antigravity.d.ts +61 -0
  92. package/dist/types/utils/discovery/codex.d.ts +38 -0
  93. package/dist/types/utils/discovery/cursor.d.ts +23 -0
  94. package/dist/types/utils/discovery/gemini.d.ts +25 -0
  95. package/dist/types/utils/discovery/index.d.ts +4 -0
  96. package/dist/types/utils/discovery/openai-compatible.d.ts +74 -0
  97. package/dist/types/utils/event-stream.d.ts +33 -0
  98. package/dist/types/utils/fireworks-model-id.d.ts +10 -0
  99. package/dist/types/utils/foundry.d.ts +1 -0
  100. package/dist/types/utils/h2-fetch.d.ts +22 -0
  101. package/dist/types/utils/http-inspector.d.ts +35 -0
  102. package/dist/types/utils/idle-iterator.d.ts +67 -0
  103. package/dist/types/utils/json-parse.d.ts +10 -0
  104. package/dist/types/utils/oauth/alibaba-coding-plan.d.ts +18 -0
  105. package/dist/types/utils/oauth/anthropic.d.ts +22 -0
  106. package/dist/types/utils/oauth/api-key-login.d.ts +35 -0
  107. package/dist/types/utils/oauth/api-key-validation.d.ts +27 -0
  108. package/dist/types/utils/oauth/callback-server.d.ts +60 -0
  109. package/dist/types/utils/oauth/cerebras.d.ts +1 -0
  110. package/dist/types/utils/oauth/cloudflare-ai-gateway.d.ts +18 -0
  111. package/dist/types/utils/oauth/cursor.d.ts +15 -0
  112. package/dist/types/utils/oauth/deepseek.d.ts +10 -0
  113. package/dist/types/utils/oauth/firepass.d.ts +1 -0
  114. package/dist/types/utils/oauth/fireworks.d.ts +1 -0
  115. package/dist/types/utils/oauth/github-copilot.d.ts +38 -0
  116. package/dist/types/utils/oauth/gitlab-duo.d.ts +3 -0
  117. package/dist/types/utils/oauth/google-antigravity.d.ts +11 -0
  118. package/dist/types/utils/oauth/google-gemini-cli.d.ts +10 -0
  119. package/dist/types/utils/oauth/google-oauth-shared.d.ts +28 -0
  120. package/dist/types/utils/oauth/huggingface.d.ts +19 -0
  121. package/dist/types/utils/oauth/index.d.ts +38 -0
  122. package/dist/types/utils/oauth/kagi.d.ts +17 -0
  123. package/dist/types/utils/oauth/kilo.d.ts +5 -0
  124. package/dist/types/utils/oauth/kimi.d.ts +21 -0
  125. package/dist/types/utils/oauth/litellm.d.ts +18 -0
  126. package/dist/types/utils/oauth/lm-studio.d.ts +17 -0
  127. package/dist/types/utils/oauth/minimax-code.d.ts +28 -0
  128. package/dist/types/utils/oauth/moonshot.d.ts +1 -0
  129. package/dist/types/utils/oauth/nanogpt.d.ts +1 -0
  130. package/dist/types/utils/oauth/nvidia.d.ts +18 -0
  131. package/dist/types/utils/oauth/ollama-cloud.d.ts +2 -0
  132. package/dist/types/utils/oauth/ollama.d.ts +18 -0
  133. package/dist/types/utils/oauth/openai-codex.d.ts +21 -0
  134. package/dist/types/utils/oauth/opencode.d.ts +18 -0
  135. package/dist/types/utils/oauth/parallel.d.ts +17 -0
  136. package/dist/types/utils/oauth/perplexity.d.ts +9 -0
  137. package/dist/types/utils/oauth/pkce.d.ts +8 -0
  138. package/dist/types/utils/oauth/qianfan.d.ts +17 -0
  139. package/dist/types/utils/oauth/qwen-portal.d.ts +19 -0
  140. package/dist/types/utils/oauth/synthetic.d.ts +1 -0
  141. package/dist/types/utils/oauth/tavily.d.ts +17 -0
  142. package/dist/types/utils/oauth/together.d.ts +1 -0
  143. package/dist/types/utils/oauth/types.d.ts +45 -0
  144. package/dist/types/utils/oauth/venice.d.ts +18 -0
  145. package/dist/types/utils/oauth/vercel-ai-gateway.d.ts +18 -0
  146. package/dist/types/utils/oauth/vllm.d.ts +16 -0
  147. package/dist/types/utils/oauth/xai.d.ts +30 -0
  148. package/dist/types/utils/oauth/xiaomi.d.ts +25 -0
  149. package/dist/types/utils/oauth/zai.d.ts +18 -0
  150. package/dist/types/utils/oauth/zenmux.d.ts +1 -0
  151. package/dist/types/utils/overflow.d.ts +54 -0
  152. package/dist/types/utils/parse-bind.d.ts +23 -0
  153. package/dist/types/utils/provider-response.d.ts +3 -0
  154. package/dist/types/utils/retry-after.d.ts +3 -0
  155. package/dist/types/utils/retry-budget.d.ts +1 -0
  156. package/dist/types/utils/retry.d.ts +26 -0
  157. package/dist/types/utils/schema/adapt.d.ts +24 -0
  158. package/dist/types/utils/schema/compatibility.d.ts +30 -0
  159. package/dist/types/utils/schema/dereference.d.ts +11 -0
  160. package/dist/types/utils/schema/draft.d.ts +10 -0
  161. package/dist/types/utils/schema/equality.d.ts +4 -0
  162. package/dist/types/utils/schema/fields.d.ts +49 -0
  163. package/dist/types/utils/schema/index.d.ts +13 -0
  164. package/dist/types/utils/schema/json-schema-validator.d.ts +12 -0
  165. package/dist/types/utils/schema/meta-validator.d.ts +2 -0
  166. package/dist/types/utils/schema/normalize.d.ts +93 -0
  167. package/dist/types/utils/schema/spill.d.ts +8 -0
  168. package/dist/types/utils/schema/stamps.d.ts +25 -0
  169. package/dist/types/utils/schema/types.d.ts +4 -0
  170. package/dist/types/utils/schema/wire.d.ts +54 -0
  171. package/dist/types/utils/schema/zod-decontaminate.d.ts +31 -0
  172. package/dist/types/utils/sse-debug.d.ts +10 -0
  173. package/dist/types/utils/tool-call-healing.d.ts +71 -0
  174. package/dist/types/utils/tool-choice-capability.d.ts +41 -0
  175. package/dist/types/utils/tool-choice.d.ts +50 -0
  176. package/dist/types/utils/validation.d.ts +17 -0
  177. package/dist/types/utils.d.ts +34 -0
  178. package/package.json +146 -0
  179. package/src/api-registry.ts +96 -0
  180. package/src/auth-broker/client.ts +358 -0
  181. package/src/auth-broker/index.ts +5 -0
  182. package/src/auth-broker/refresher.ts +127 -0
  183. package/src/auth-broker/remote-store.ts +623 -0
  184. package/src/auth-broker/server.ts +644 -0
  185. package/src/auth-broker/types.ts +127 -0
  186. package/src/auth-broker/wire-schemas.ts +200 -0
  187. package/src/auth-gateway/http.ts +194 -0
  188. package/src/auth-gateway/index.ts +3 -0
  189. package/src/auth-gateway/server.ts +717 -0
  190. package/src/auth-gateway/types.ts +134 -0
  191. package/src/auth-storage.ts +4179 -0
  192. package/src/cli.ts +263 -0
  193. package/src/index.ts +56 -0
  194. package/src/model-cache.ts +129 -0
  195. package/src/model-manager.ts +486 -0
  196. package/src/model-thinking.ts +772 -0
  197. package/src/models.json +75437 -0
  198. package/src/models.json.d.ts +9 -0
  199. package/src/models.ts +82 -0
  200. package/src/prompts/turn-aborted-guidance.md +4 -0
  201. package/src/provider-details.ts +90 -0
  202. package/src/provider-models/bundled-references.ts +38 -0
  203. package/src/provider-models/descriptors.ts +327 -0
  204. package/src/provider-models/google.ts +91 -0
  205. package/src/provider-models/index.ts +5 -0
  206. package/src/provider-models/ollama.ts +153 -0
  207. package/src/provider-models/openai-compat.ts +2352 -0
  208. package/src/provider-models/special.ts +67 -0
  209. package/src/providers/amazon-bedrock.ts +937 -0
  210. package/src/providers/anthropic-messages-server-schema.ts +229 -0
  211. package/src/providers/anthropic-messages-server.ts +677 -0
  212. package/src/providers/anthropic.ts +2940 -0
  213. package/src/providers/aws-credentials.ts +501 -0
  214. package/src/providers/aws-eventstream.ts +185 -0
  215. package/src/providers/aws-sigv4.ts +218 -0
  216. package/src/providers/azure-openai-responses.ts +379 -0
  217. package/src/providers/composer-discipline.ts +41 -0
  218. package/src/providers/cursor/gen/agent_pb.ts +15274 -0
  219. package/src/providers/cursor/proto/agent.proto +3526 -0
  220. package/src/providers/cursor/proto/buf.gen.yaml +6 -0
  221. package/src/providers/cursor/proto/buf.yaml +17 -0
  222. package/src/providers/cursor.ts +2671 -0
  223. package/src/providers/error-message.ts +21 -0
  224. package/src/providers/github-copilot-headers.ts +140 -0
  225. package/src/providers/gitlab-duo.ts +372 -0
  226. package/src/providers/google-auth.ts +252 -0
  227. package/src/providers/google-gemini-cli.ts +856 -0
  228. package/src/providers/google-gemini-headers.ts +41 -0
  229. package/src/providers/google-shared.ts +951 -0
  230. package/src/providers/google-types.ts +167 -0
  231. package/src/providers/google-vertex.ts +88 -0
  232. package/src/providers/google.ts +41 -0
  233. package/src/providers/grammar.ts +70 -0
  234. package/src/providers/kimi.ts +52 -0
  235. package/src/providers/mock.ts +500 -0
  236. package/src/providers/ollama.ts +603 -0
  237. package/src/providers/openai-anthropic-shim.ts +138 -0
  238. package/src/providers/openai-chat-server-schema.ts +243 -0
  239. package/src/providers/openai-chat-server.ts +635 -0
  240. package/src/providers/openai-codex/constants.ts +43 -0
  241. package/src/providers/openai-codex/request-transformer.ts +161 -0
  242. package/src/providers/openai-codex/response-handler.ts +81 -0
  243. package/src/providers/openai-codex-responses.ts +2774 -0
  244. package/src/providers/openai-completions-compat.ts +289 -0
  245. package/src/providers/openai-completions.ts +1935 -0
  246. package/src/providers/openai-request-transform.ts +136 -0
  247. package/src/providers/openai-responses-server-schema.ts +290 -0
  248. package/src/providers/openai-responses-server.ts +1190 -0
  249. package/src/providers/openai-responses-shared.ts +800 -0
  250. package/src/providers/openai-responses.ts +738 -0
  251. package/src/providers/pi-native-client.ts +227 -0
  252. package/src/providers/pi-native-server.ts +210 -0
  253. package/src/providers/register-builtins.ts +411 -0
  254. package/src/providers/synthetic.ts +50 -0
  255. package/src/providers/transform-messages.ts +319 -0
  256. package/src/providers/vision-guard.ts +31 -0
  257. package/src/rate-limit-utils.ts +93 -0
  258. package/src/stream.ts +960 -0
  259. package/src/types.ts +967 -0
  260. package/src/usage/claude.ts +431 -0
  261. package/src/usage/gemini.ts +250 -0
  262. package/src/usage/github-copilot.ts +421 -0
  263. package/src/usage/google-antigravity.ts +201 -0
  264. package/src/usage/grok-cli.ts +163 -0
  265. package/src/usage/kimi.ts +271 -0
  266. package/src/usage/minimax-code.ts +31 -0
  267. package/src/usage/openai-codex.ts +503 -0
  268. package/src/usage/shared.ts +10 -0
  269. package/src/usage/zai.ts +247 -0
  270. package/src/usage.ts +183 -0
  271. package/src/utils/abort.ts +51 -0
  272. package/src/utils/anthropic-auth.ts +87 -0
  273. package/src/utils/discovery/antigravity.ts +261 -0
  274. package/src/utils/discovery/codex.ts +371 -0
  275. package/src/utils/discovery/cursor.ts +306 -0
  276. package/src/utils/discovery/gemini.ts +248 -0
  277. package/src/utils/discovery/index.ts +4 -0
  278. package/src/utils/discovery/openai-compatible.ts +230 -0
  279. package/src/utils/event-stream.ts +172 -0
  280. package/src/utils/fireworks-model-id.ts +30 -0
  281. package/src/utils/foundry.ts +8 -0
  282. package/src/utils/h2-fetch.ts +60 -0
  283. package/src/utils/http-inspector.ts +255 -0
  284. package/src/utils/idle-iterator.ts +257 -0
  285. package/src/utils/json-parse.ts +148 -0
  286. package/src/utils/oauth/alibaba-coding-plan.ts +59 -0
  287. package/src/utils/oauth/anthropic.ts +200 -0
  288. package/src/utils/oauth/api-key-login.ts +87 -0
  289. package/src/utils/oauth/api-key-validation.ts +92 -0
  290. package/src/utils/oauth/callback-server.ts +281 -0
  291. package/src/utils/oauth/cerebras.ts +16 -0
  292. package/src/utils/oauth/cloudflare-ai-gateway.ts +48 -0
  293. package/src/utils/oauth/cursor.ts +157 -0
  294. package/src/utils/oauth/deepseek.ts +53 -0
  295. package/src/utils/oauth/firepass.ts +24 -0
  296. package/src/utils/oauth/fireworks.ts +15 -0
  297. package/src/utils/oauth/github-copilot.ts +362 -0
  298. package/src/utils/oauth/gitlab-duo.ts +123 -0
  299. package/src/utils/oauth/google-antigravity.ts +200 -0
  300. package/src/utils/oauth/google-gemini-cli.ts +256 -0
  301. package/src/utils/oauth/google-oauth-shared.ts +110 -0
  302. package/src/utils/oauth/huggingface.ts +62 -0
  303. package/src/utils/oauth/index.ts +469 -0
  304. package/src/utils/oauth/kagi.ts +47 -0
  305. package/src/utils/oauth/kilo.ts +87 -0
  306. package/src/utils/oauth/kimi.ts +254 -0
  307. package/src/utils/oauth/litellm.ts +47 -0
  308. package/src/utils/oauth/lm-studio.ts +38 -0
  309. package/src/utils/oauth/minimax-code.ts +78 -0
  310. package/src/utils/oauth/moonshot.ts +16 -0
  311. package/src/utils/oauth/nanogpt.ts +15 -0
  312. package/src/utils/oauth/nvidia.ts +70 -0
  313. package/src/utils/oauth/oauth.html +199 -0
  314. package/src/utils/oauth/ollama-cloud.ts +28 -0
  315. package/src/utils/oauth/ollama.ts +47 -0
  316. package/src/utils/oauth/openai-codex.ts +299 -0
  317. package/src/utils/oauth/opencode.ts +49 -0
  318. package/src/utils/oauth/parallel.ts +46 -0
  319. package/src/utils/oauth/perplexity.ts +206 -0
  320. package/src/utils/oauth/pkce.ts +18 -0
  321. package/src/utils/oauth/qianfan.ts +58 -0
  322. package/src/utils/oauth/qwen-portal.ts +60 -0
  323. package/src/utils/oauth/synthetic.ts +16 -0
  324. package/src/utils/oauth/tavily.ts +46 -0
  325. package/src/utils/oauth/together.ts +16 -0
  326. package/src/utils/oauth/types.ts +99 -0
  327. package/src/utils/oauth/venice.ts +59 -0
  328. package/src/utils/oauth/vercel-ai-gateway.ts +47 -0
  329. package/src/utils/oauth/vllm.ts +40 -0
  330. package/src/utils/oauth/xai.ts +246 -0
  331. package/src/utils/oauth/xiaomi.ts +199 -0
  332. package/src/utils/oauth/zai.ts +60 -0
  333. package/src/utils/oauth/zenmux.ts +15 -0
  334. package/src/utils/overflow.ts +137 -0
  335. package/src/utils/parse-bind.ts +54 -0
  336. package/src/utils/provider-response.ts +30 -0
  337. package/src/utils/retry-after.ts +110 -0
  338. package/src/utils/retry-budget.ts +4 -0
  339. package/src/utils/retry.ts +54 -0
  340. package/src/utils/schema/CONSTRAINTS.md +164 -0
  341. package/src/utils/schema/adapt.ts +36 -0
  342. package/src/utils/schema/compatibility.ts +435 -0
  343. package/src/utils/schema/dereference.ts +98 -0
  344. package/src/utils/schema/draft.ts +341 -0
  345. package/src/utils/schema/equality.ts +97 -0
  346. package/src/utils/schema/fields.ts +190 -0
  347. package/src/utils/schema/index.ts +13 -0
  348. package/src/utils/schema/json-schema-validator.ts +577 -0
  349. package/src/utils/schema/meta-validator.ts +167 -0
  350. package/src/utils/schema/normalize.ts +1588 -0
  351. package/src/utils/schema/spill.ts +43 -0
  352. package/src/utils/schema/stamps.ts +97 -0
  353. package/src/utils/schema/types.ts +11 -0
  354. package/src/utils/schema/wire.ts +213 -0
  355. package/src/utils/schema/zod-decontaminate.ts +331 -0
  356. package/src/utils/sse-debug.ts +289 -0
  357. package/src/utils/tool-call-healing.ts +271 -0
  358. package/src/utils/tool-choice-capability.ts +220 -0
  359. package/src/utils/tool-choice.ts +99 -0
  360. package/src/utils/validation.ts +1019 -0
  361. package/src/utils.ts +178 -0
@@ -0,0 +1,246 @@
1
+ /** xAI OAuth flow (Grok account login). */
2
+ import { OAuthCallbackFlow, type OAuthCallbackFlowOptions } from "./callback-server";
3
+ import { generatePKCE } from "./pkce";
4
+ import type { OAuthController, OAuthCredentials } from "./types";
5
+
6
+ const XAI_OAUTH_ISSUER = "https://auth.x.ai";
7
+ export const XAI_OAUTH_DISCOVERY_URL = `${XAI_OAUTH_ISSUER}/.well-known/openid-configuration`;
8
+ export const XAI_OAUTH_CLIENT_ID = "b1a00492-073a-47ea-816f-4c329264a828";
9
+ export const XAI_OAUTH_SCOPE = "openid profile email offline_access grok-cli:access api:access";
10
+ const XAI_OAUTH_CALLBACK_PORT = 56121;
11
+ const XAI_OAUTH_CALLBACK_PATH = "/callback";
12
+ const XAI_OAUTH_REFRESH_SKEW_MS = 2 * 60 * 1000;
13
+ const TOKEN_REQUEST_TIMEOUT_MS = 30_000;
14
+
15
+ interface XaiDiscovery {
16
+ authorizationEndpoint: string;
17
+ tokenEndpoint: string;
18
+ }
19
+
20
+ interface XaiDiscoveryPayload {
21
+ authorization_endpoint?: unknown;
22
+ token_endpoint?: unknown;
23
+ }
24
+
25
+ interface XaiTokenPayload {
26
+ access_token?: unknown;
27
+ refresh_token?: unknown;
28
+ expires_in?: unknown;
29
+ id_token?: unknown;
30
+ token_type?: unknown;
31
+ }
32
+
33
+ export interface XaiOAuthFlowOptions {
34
+ extraAuthorizeParams?: Readonly<Record<string, string>>;
35
+ }
36
+
37
+ export interface XaiOAuthRefreshOptions {
38
+ signal?: AbortSignal;
39
+ extraTokenParams?: Readonly<Record<string, string>>;
40
+ }
41
+
42
+ interface XaiJwtPayload {
43
+ sub?: unknown;
44
+ email?: unknown;
45
+ [key: string]: unknown;
46
+ }
47
+
48
+ function requestSignal(signal: AbortSignal | undefined): AbortSignal {
49
+ const timeoutSignal = AbortSignal.timeout(TOKEN_REQUEST_TIMEOUT_MS);
50
+ return signal ? AbortSignal.any([signal, timeoutSignal]) : timeoutSignal;
51
+ }
52
+
53
+ function addNonOverridingParams(
54
+ target: URLSearchParams | Record<string, string>,
55
+ params: Readonly<Record<string, string>>,
56
+ ): void {
57
+ for (const [key, value] of Object.entries(params)) {
58
+ if (key.length === 0 || value.length === 0) continue;
59
+ if (target instanceof URLSearchParams) {
60
+ if (!target.has(key)) target.set(key, value);
61
+ } else if (!(key in target)) {
62
+ target[key] = value;
63
+ }
64
+ }
65
+ }
66
+
67
+ function isAbortSignal(value: AbortSignal | XaiOAuthRefreshOptions | undefined): value is AbortSignal {
68
+ return value instanceof AbortSignal;
69
+ }
70
+
71
+ function resolveRefreshOptions(options: AbortSignal | XaiOAuthRefreshOptions | undefined): XaiOAuthRefreshOptions {
72
+ return isAbortSignal(options) ? { signal: options } : (options ?? {});
73
+ }
74
+
75
+ function validateXaiEndpoint(rawUrl: string): string {
76
+ const parsed = new URL(rawUrl);
77
+ const host = parsed.hostname.toLowerCase();
78
+ if (parsed.protocol !== "https:" || (host !== "x.ai" && !host.endsWith(".x.ai"))) {
79
+ throw new Error(`xAI OAuth discovery returned an unexpected endpoint: ${rawUrl}`);
80
+ }
81
+ return parsed.toString();
82
+ }
83
+
84
+ export async function discoverXaiOAuthEndpoints(signal?: AbortSignal): Promise<XaiDiscovery> {
85
+ const response = await fetch(XAI_OAUTH_DISCOVERY_URL, {
86
+ headers: { Accept: "application/json" },
87
+ signal: requestSignal(signal),
88
+ });
89
+ if (!response.ok) {
90
+ throw new Error(`xAI OAuth discovery failed: ${response.status} ${await response.text()}`);
91
+ }
92
+
93
+ const payload = (await response.json()) as XaiDiscoveryPayload;
94
+ if (typeof payload.authorization_endpoint !== "string" || typeof payload.token_endpoint !== "string") {
95
+ throw new Error("xAI OAuth discovery response missing authorization/token endpoints");
96
+ }
97
+
98
+ return {
99
+ authorizationEndpoint: validateXaiEndpoint(payload.authorization_endpoint),
100
+ tokenEndpoint: validateXaiEndpoint(payload.token_endpoint),
101
+ };
102
+ }
103
+
104
+ function decodeJwtPayload(token: string): XaiJwtPayload | undefined {
105
+ const parts = token.split(".");
106
+ const payload = parts[1];
107
+ if (parts.length !== 3 || !payload) return undefined;
108
+ try {
109
+ return JSON.parse(Buffer.from(payload, "base64url").toString("utf8")) as XaiJwtPayload;
110
+ } catch {
111
+ return undefined;
112
+ }
113
+ }
114
+
115
+ function getTokenIdentity(accessToken: string, idToken: string | undefined): { accountId?: string; email?: string } {
116
+ const payload = (idToken ? decodeJwtPayload(idToken) : undefined) ?? decodeJwtPayload(accessToken);
117
+ const accountId = typeof payload?.sub === "string" && payload.sub.length > 0 ? payload.sub : undefined;
118
+ const email =
119
+ typeof payload?.email === "string" && payload.email.length > 0 ? payload.email.toLowerCase() : undefined;
120
+ return { accountId, email };
121
+ }
122
+
123
+ async function postXaiToken(
124
+ tokenEndpoint: string,
125
+ body: Record<string, string>,
126
+ signal?: AbortSignal,
127
+ ): Promise<XaiTokenPayload> {
128
+ const response = await fetch(tokenEndpoint, {
129
+ method: "POST",
130
+ headers: {
131
+ Accept: "application/json",
132
+ "Content-Type": "application/x-www-form-urlencoded",
133
+ },
134
+ body: new URLSearchParams(body).toString(),
135
+ signal: requestSignal(signal),
136
+ });
137
+ if (!response.ok) {
138
+ throw new Error(`xAI token request failed: ${response.status} ${await response.text()}`);
139
+ }
140
+ return (await response.json()) as XaiTokenPayload;
141
+ }
142
+
143
+ function credentialsFromTokenPayload(payload: XaiTokenPayload, refreshFallback = ""): OAuthCredentials {
144
+ if (typeof payload.access_token !== "string" || payload.access_token.length === 0) {
145
+ throw new Error("xAI token response did not include an access token");
146
+ }
147
+ const refresh =
148
+ typeof payload.refresh_token === "string" && payload.refresh_token.length > 0
149
+ ? payload.refresh_token
150
+ : refreshFallback;
151
+ if (!refresh) {
152
+ throw new Error("xAI token response did not include a refresh token");
153
+ }
154
+ const expiresIn =
155
+ typeof payload.expires_in === "number" && Number.isFinite(payload.expires_in) ? payload.expires_in : 3600;
156
+ const idToken = typeof payload.id_token === "string" ? payload.id_token : undefined;
157
+ const { accountId, email } = getTokenIdentity(payload.access_token, idToken);
158
+ return {
159
+ refresh,
160
+ access: payload.access_token,
161
+ expires: Date.now() + expiresIn * 1000 - XAI_OAUTH_REFRESH_SKEW_MS,
162
+ accountId,
163
+ email,
164
+ };
165
+ }
166
+
167
+ export class XaiOAuthFlow extends OAuthCallbackFlow {
168
+ #verifier = "";
169
+ #discovery: XaiDiscovery | undefined;
170
+ #extraAuthorizeParams: Readonly<Record<string, string>>;
171
+
172
+ constructor(ctrl: OAuthController, options: XaiOAuthFlowOptions = {}) {
173
+ super(ctrl, {
174
+ preferredPort: XAI_OAUTH_CALLBACK_PORT,
175
+ callbackPath: XAI_OAUTH_CALLBACK_PATH,
176
+ callbackHostname: "127.0.0.1",
177
+ callbackBindHostname: "127.0.0.1",
178
+ redirectUri: `http://127.0.0.1:${XAI_OAUTH_CALLBACK_PORT}${XAI_OAUTH_CALLBACK_PATH}`,
179
+ } satisfies OAuthCallbackFlowOptions);
180
+ this.#extraAuthorizeParams = options.extraAuthorizeParams ?? {};
181
+ }
182
+
183
+ async generateAuthUrl(state: string, redirectUri: string): Promise<{ url: string; instructions?: string }> {
184
+ const pkce = await generatePKCE();
185
+ this.#verifier = pkce.verifier;
186
+ this.#discovery = await discoverXaiOAuthEndpoints(this.ctrl.signal);
187
+ const params = new URLSearchParams({
188
+ response_type: "code",
189
+ client_id: XAI_OAUTH_CLIENT_ID,
190
+ redirect_uri: redirectUri,
191
+ scope: XAI_OAUTH_SCOPE,
192
+ code_challenge: pkce.challenge,
193
+ code_challenge_method: "S256",
194
+ state,
195
+ nonce: crypto.randomUUID(),
196
+ });
197
+ addNonOverridingParams(params, this.#extraAuthorizeParams);
198
+ return {
199
+ url: `${this.#discovery.authorizationEndpoint}?${params.toString()}`,
200
+ instructions:
201
+ "Complete xAI/Grok login in your browser. If the browser cannot reach this machine, paste the final redirect URL or authorization code when prompted.",
202
+ };
203
+ }
204
+
205
+ async exchangeToken(code: string, _state: string, redirectUri: string): Promise<OAuthCredentials> {
206
+ if (!this.#verifier) {
207
+ throw new Error("xAI OAuth PKCE verifier was not initialized");
208
+ }
209
+ const discovery = this.#discovery ?? (await discoverXaiOAuthEndpoints(this.ctrl.signal));
210
+ const tokenPayload = await postXaiToken(
211
+ discovery.tokenEndpoint,
212
+ {
213
+ grant_type: "authorization_code",
214
+ client_id: XAI_OAUTH_CLIENT_ID,
215
+ code,
216
+ redirect_uri: redirectUri,
217
+ code_verifier: this.#verifier,
218
+ },
219
+ this.ctrl.signal,
220
+ );
221
+ return credentialsFromTokenPayload(tokenPayload);
222
+ }
223
+ }
224
+
225
+ export async function loginXai(ctrl: OAuthController, options?: XaiOAuthFlowOptions): Promise<OAuthCredentials> {
226
+ return new XaiOAuthFlow(ctrl, options).login();
227
+ }
228
+
229
+ export async function refreshXaiToken(
230
+ refreshToken: string,
231
+ options?: AbortSignal | XaiOAuthRefreshOptions,
232
+ ): Promise<OAuthCredentials> {
233
+ if (!refreshToken) {
234
+ throw new Error("xAI credentials are expired and do not include a refresh token");
235
+ }
236
+ const { signal, extraTokenParams = {} } = resolveRefreshOptions(options);
237
+ const discovery = await discoverXaiOAuthEndpoints(signal);
238
+ const body = {
239
+ grant_type: "refresh_token",
240
+ client_id: XAI_OAUTH_CLIENT_ID,
241
+ refresh_token: refreshToken,
242
+ };
243
+ addNonOverridingParams(body, extraTokenParams);
244
+ const tokenPayload = await postXaiToken(discovery.tokenEndpoint, body, signal);
245
+ return credentialsFromTokenPayload(tokenPayload, refreshToken);
246
+ }
@@ -0,0 +1,199 @@
1
+ /**
2
+ * Xiaomi MiMo login flow.
3
+ *
4
+ * Xiaomi MiMo provides OpenAI-compatible models via
5
+ * https://api.xiaomimimo.com/v1.
6
+ *
7
+ * Standard Xiaomi login opens the pay-as-you-go API key console. Token Plan
8
+ * login opens plan management so users copy the regional `tp-...` key.
9
+ */
10
+
11
+ import type { FetchImpl } from "../../types";
12
+ import type { OAuthController } from "./types";
13
+
14
+ const PROVIDER_ID = "xiaomi";
15
+ const PROVIDER_NAME = "Xiaomi MiMo";
16
+ const STANDARD_AUTH_URL = "https://platform.xiaomimimo.com/#/console/api-keys";
17
+ const TOKEN_PLAN_AUTH_URL = "https://platform.xiaomimimo.com/console/plan-manage";
18
+ const STANDARD_API_BASE_URL = "https://api.xiaomimimo.com/v1";
19
+ const TOKEN_PLAN_KEY_PREFIX = "tp-";
20
+ const STANDARD_VALIDATION_MODEL = "mimo-v2-flash";
21
+ const TOKEN_PLAN_VALIDATION_MODEL = "mimo-v2.5";
22
+ const TOKEN_PLAN_SGP_API_BASE_URL = "https://token-plan-sgp.xiaomimimo.com/v1";
23
+ const TOKEN_PLAN_AMS_API_BASE_URL = "https://token-plan-ams.xiaomimimo.com/v1";
24
+ const TOKEN_PLAN_CN_API_BASE_URL = "https://token-plan-cn.xiaomimimo.com/v1";
25
+
26
+ /** Region codes accepted by the Xiaomi Token Plan login flow. */
27
+ export type XiaomiTokenPlanRegion = "sgp" | "ams" | "cn";
28
+
29
+ type XiaomiValidationEndpoint = {
30
+ baseUrl: string;
31
+ model: string;
32
+ };
33
+
34
+ const TOKEN_PLAN_VALIDATION_ENDPOINTS: Record<XiaomiTokenPlanRegion, XiaomiValidationEndpoint> = {
35
+ sgp: { baseUrl: TOKEN_PLAN_SGP_API_BASE_URL, model: TOKEN_PLAN_VALIDATION_MODEL },
36
+ ams: { baseUrl: TOKEN_PLAN_AMS_API_BASE_URL, model: TOKEN_PLAN_VALIDATION_MODEL },
37
+ cn: { baseUrl: TOKEN_PLAN_CN_API_BASE_URL, model: TOKEN_PLAN_VALIDATION_MODEL },
38
+ };
39
+
40
+ const TOKEN_PLAN_REGION_NAMES: Record<XiaomiTokenPlanRegion, string> = {
41
+ sgp: "Singapore",
42
+ ams: "Europe",
43
+ cn: "China",
44
+ };
45
+
46
+ function isTokenPlanKey(apiKey: string): boolean {
47
+ return apiKey.startsWith(TOKEN_PLAN_KEY_PREFIX);
48
+ }
49
+
50
+ const VALIDATION_TIMEOUT_MS = 15_000;
51
+
52
+ async function validateXiaomiApiKey(
53
+ apiKey: string,
54
+ tokenPlanRegion: XiaomiTokenPlanRegion | undefined,
55
+ signal?: AbortSignal,
56
+ fetchOverride?: FetchImpl,
57
+ ): Promise<void> {
58
+ const fetchImpl = fetchOverride ?? fetch;
59
+ // Region-specific Token Plan logins must validate against the selected
60
+ // cluster. Generic Xiaomi login keeps the historical SGP → AMS → CN fallback.
61
+ const endpoints = tokenPlanRegion
62
+ ? [TOKEN_PLAN_VALIDATION_ENDPOINTS[tokenPlanRegion]]
63
+ : isTokenPlanKey(apiKey)
64
+ ? [
65
+ TOKEN_PLAN_VALIDATION_ENDPOINTS.sgp,
66
+ TOKEN_PLAN_VALIDATION_ENDPOINTS.ams,
67
+ TOKEN_PLAN_VALIDATION_ENDPOINTS.cn,
68
+ ]
69
+ : [{ baseUrl: STANDARD_API_BASE_URL, model: STANDARD_VALIDATION_MODEL }];
70
+
71
+ let lastError: Error | null = null;
72
+
73
+ for (const ep of endpoints) {
74
+ // Fresh timeout per endpoint so SGP→AMS fallback works after a regional
75
+ // timeout: a shared AbortSignal.timeout would stay aborted and instantly
76
+ // abort the AMS fetch.
77
+ const timeoutSignal = AbortSignal.timeout(VALIDATION_TIMEOUT_MS);
78
+ const requestSignal = signal ? AbortSignal.any([signal, timeoutSignal]) : timeoutSignal;
79
+ try {
80
+ const response = await fetchImpl(`${ep.baseUrl}/chat/completions`, {
81
+ method: "POST",
82
+ headers: {
83
+ "Content-Type": "application/json",
84
+ Authorization: `Bearer ${apiKey}`,
85
+ },
86
+ body: JSON.stringify({
87
+ model: ep.model,
88
+ max_tokens: 1,
89
+ messages: [{ role: "user", content: "ping" }],
90
+ }),
91
+ signal: requestSignal,
92
+ });
93
+
94
+ if (response.ok) {
95
+ return;
96
+ }
97
+
98
+ // 401 means this endpoint didn't accept the key; try the next one
99
+ if (response.status === 401) {
100
+ let details = "";
101
+ try {
102
+ details = (await response.text()).trim();
103
+ } catch {
104
+ // ignore body parse errors, status is enough
105
+ }
106
+ lastError = new Error(
107
+ details
108
+ ? `${PROVIDER_NAME} API key validation failed (${response.status}): ${details}`
109
+ : `${PROVIDER_NAME} API key validation failed (${response.status})`,
110
+ );
111
+ continue;
112
+ }
113
+
114
+ // Non-auth errors are real failures
115
+ let details = "";
116
+ try {
117
+ details = (await response.text()).trim();
118
+ } catch {
119
+ // ignore body parse errors, status is enough
120
+ }
121
+ const message = details
122
+ ? `${PROVIDER_NAME} API key validation failed (${response.status}): ${details}`
123
+ : `${PROVIDER_NAME} API key validation failed (${response.status})`;
124
+ throw new Error(message);
125
+ } catch (e) {
126
+ // Only re-throw AbortError when the caller explicitly cancelled.
127
+ // Timeout aborts (from AbortSignal.timeout) should fall through to
128
+ // the next endpoint so SGP→AMS fallback works during regional outages.
129
+ if (e instanceof DOMException && e.name === "AbortError" && signal?.aborted) {
130
+ throw e;
131
+ }
132
+ lastError = e instanceof Error ? e : new Error(String(e));
133
+ }
134
+ }
135
+ throw lastError ?? new Error(`${PROVIDER_NAME} API key validation failed`);
136
+ }
137
+
138
+ /**
139
+ * Login to Xiaomi MiMo.
140
+ *
141
+ * Opens browser to API keys page, prompts user to paste their API key.
142
+ * Returns the API key directly (not OAuthCredentials - this isn't OAuth).
143
+ */
144
+ export async function loginXiaomi(options: OAuthController): Promise<string> {
145
+ const fetchImpl = options.fetch ?? fetch;
146
+ if (!options.onPrompt) {
147
+ throw new Error(`${PROVIDER_NAME} login requires onPrompt callback`);
148
+ }
149
+ options.onAuth?.({
150
+ url: STANDARD_AUTH_URL,
151
+ instructions: "Copy your API key from the Xiaomi MiMo console",
152
+ });
153
+ const apiKey = await options.onPrompt({
154
+ message: "Paste your Xiaomi API key (sk-... or token-plan tp-...)",
155
+ placeholder: "sk-... or tp-...",
156
+ });
157
+ if (options.signal?.aborted) {
158
+ throw new Error("Login cancelled");
159
+ }
160
+ const trimmed = apiKey.trim();
161
+ if (!trimmed) {
162
+ throw new Error("API key is required");
163
+ }
164
+
165
+ options.onProgress?.(`Validating ${PROVIDER_ID} API key...`);
166
+ await validateXiaomiApiKey(trimmed, undefined, options.signal, fetchImpl);
167
+ return trimmed;
168
+ }
169
+
170
+ /**
171
+ * Login to a regional Xiaomi Token Plan endpoint.
172
+ *
173
+ * Prompts for a token-plan API key and validates it against the selected region.
174
+ */
175
+ export async function loginXiaomiTokenPlan(options: OAuthController, region: XiaomiTokenPlanRegion): Promise<string> {
176
+ const fetchImpl = options.fetch ?? fetch;
177
+ if (!options.onPrompt) {
178
+ throw new Error(`Xiaomi Token Plan (${TOKEN_PLAN_REGION_NAMES[region]}) login requires onPrompt callback`);
179
+ }
180
+ options.onAuth?.({
181
+ url: TOKEN_PLAN_AUTH_URL,
182
+ instructions: `Copy your token-plan API key for the ${TOKEN_PLAN_REGION_NAMES[region]} region`,
183
+ });
184
+ const apiKey = await options.onPrompt({
185
+ message: `Paste your Xiaomi Token Plan ${TOKEN_PLAN_REGION_NAMES[region]} API key (tp-...)`,
186
+ placeholder: "tp-...",
187
+ });
188
+ if (options.signal?.aborted) {
189
+ throw new Error("Login cancelled");
190
+ }
191
+ const trimmed = apiKey.trim();
192
+ if (!trimmed) {
193
+ throw new Error("API key is required");
194
+ }
195
+
196
+ options.onProgress?.(`Validating Xiaomi Token Plan (${TOKEN_PLAN_REGION_NAMES[region]}) API key...`);
197
+ await validateXiaomiApiKey(trimmed, region, options.signal, fetchImpl);
198
+ return trimmed;
199
+ }
@@ -0,0 +1,60 @@
1
+ /**
2
+ * Z.AI login flow.
3
+ *
4
+ * Z.AI is a platform that provides access to GLM models through an OpenAI-compatible API.
5
+ * API docs: https://docs.z.ai/guides/overview/quick-start
6
+ *
7
+ * This is not OAuth - it's a simple API key flow:
8
+ * 1. User gets their API key from https://z.ai/settings/api-keys
9
+ * 2. User pastes the API key into the CLI
10
+ */
11
+
12
+ import { validateOpenAICompatibleApiKey } from "./api-key-validation";
13
+ import type { OAuthController } from "./types";
14
+
15
+ const AUTH_URL = "https://z.ai/manage-apikey/apikey-list";
16
+ const API_BASE_URL = "https://api.z.ai/api/coding/paas/v4";
17
+ const VALIDATION_MODEL = "glm-4.7";
18
+
19
+ /**
20
+ * Login to Z.AI.
21
+ *
22
+ * Opens browser to API keys page, prompts user to paste their API key.
23
+ * Returns the API key directly (not OAuthCredentials - this isn't OAuth).
24
+ */
25
+ export async function loginZai(options: OAuthController): Promise<string> {
26
+ if (!options.onPrompt) {
27
+ throw new Error("Z.AI login requires onPrompt callback");
28
+ }
29
+
30
+ // Open browser to API keys page
31
+ options.onAuth?.({
32
+ url: AUTH_URL,
33
+ instructions: "Copy your API key from the dashboard",
34
+ });
35
+
36
+ // Prompt user to paste their API key
37
+ const apiKey = await options.onPrompt({
38
+ message: "Paste your Z.AI API key",
39
+ placeholder: "sk-...",
40
+ });
41
+
42
+ if (options.signal?.aborted) {
43
+ throw new Error("Login cancelled");
44
+ }
45
+
46
+ const trimmed = apiKey.trim();
47
+ if (!trimmed) {
48
+ throw new Error("API key is required");
49
+ }
50
+
51
+ options.onProgress?.("Validating API key...");
52
+ await validateOpenAICompatibleApiKey({
53
+ provider: "Z.AI",
54
+ apiKey: trimmed,
55
+ baseUrl: API_BASE_URL,
56
+ model: VALIDATION_MODEL,
57
+ signal: options.signal,
58
+ });
59
+ return trimmed;
60
+ }
@@ -0,0 +1,15 @@
1
+ /** ZenMux login flow (API key paste, validated via /models). */
2
+ import { createApiKeyLogin } from "./api-key-login";
3
+
4
+ export const loginZenMux = createApiKeyLogin({
5
+ providerLabel: "ZenMux",
6
+ authUrl: "https://zenmux.ai/settings/keys",
7
+ instructions: "Create or copy your ZenMux API key",
8
+ promptMessage: "Paste your ZenMux API key",
9
+ placeholder: "sk-...",
10
+ validation: {
11
+ kind: "models-endpoint",
12
+ provider: "ZenMux",
13
+ modelsUrl: "https://zenmux.ai/api/v1/models",
14
+ },
15
+ });
@@ -0,0 +1,137 @@
1
+ import type { AssistantMessage } from "../types";
2
+
3
+ /**
4
+ * Regex patterns to detect context overflow errors from different providers.
5
+ *
6
+ * These patterns match error messages returned when the input exceeds
7
+ * the model's context window.
8
+ *
9
+ * Provider-specific patterns (with example error messages):
10
+ *
11
+ * - Anthropic: "prompt is too long: 213462 tokens > 200000 maximum"
12
+ * - OpenAI: "Your input exceeds the context window of this model"
13
+ * - Google: "The input token count (1196265) exceeds the maximum number of tokens allowed (1048575)"
14
+ * - xAI: "This model's maximum prompt length is 131072 but the request contains 537812 tokens"
15
+ * - Groq: "Please reduce the length of the messages or completion"
16
+ * - OpenRouter: "This endpoint's maximum context length is X tokens. However, you requested about Y tokens"
17
+ * - llama.cpp: "the request exceeds the available context size, try increasing it"
18
+ * - LM Studio: "tokens to keep from the initial prompt is greater than the context length"
19
+ * - GitHub Copilot: "prompt token count of X exceeds the limit of Y"
20
+ * - MiniMax: "invalid params, context window exceeds limit"
21
+ * - Kimi For Coding: "Your request exceeded model token limit: X (requested: Y)"
22
+ * - Anthropic 413: "request_too_large" / "Request exceeds the maximum size" (payload too large)
23
+ * - HTTP 413 variants: "Payload Too Large" / "Request Entity Too Large"
24
+ * - z.ai / GLM: Returns finish_reason: "model_context_window_exceeded" mapped to error message
25
+ * - z.ai: Does NOT error, accepts overflow silently - handled via usage.input > contextWindow
26
+ * - Ollama: Silently truncates input - not detectable via error message
27
+ */
28
+ const OVERFLOW_PATTERNS = [
29
+ /prompt is too long/i, // Anthropic
30
+ /input is too long for requested model/i, // Amazon Bedrock
31
+ /exceeds the context window/i, // OpenAI (Completions & Responses API)
32
+ /input token count.*exceeds the maximum/i, // Google (Gemini)
33
+ /maximum prompt length is \d+/i, // xAI (Grok)
34
+ /reduce the length of the messages/i, // Groq
35
+ /maximum context length is \d+ tokens/i, // OpenRouter (all backends)
36
+ /exceeds the limit of \d+/i, // GitHub Copilot
37
+ /exceeds the available context size/i, // llama.cpp server
38
+ /requested tokens?.*exceed.*context (window|length|size)/i, // llama.cpp / OpenAI-compatible local servers
39
+ /context (window|length|size).*(exceeded|overflow|too small)/i, // Generic local server variants
40
+ /(prompt|input).*(too long|too large).*(context|n_ctx)/i, // llama.cpp phrasing variants
41
+ /requested tokens?.*(exceeds?|greater than).*(n_ctx|context)/i, // llama.cpp n_ctx variants
42
+ /greater than the context length/i, // LM Studio
43
+ /context window exceeds limit/i, // MiniMax
44
+ /exceeded model token limit/i, // Kimi For Coding
45
+ /context[_ ]length[_ ]exceeded/i, // Generic fallback
46
+ /too many tokens/i, // Generic fallback
47
+ /token limit exceeded/i, // Generic fallback
48
+ /request_too_large/i, // Anthropic 413 (request body too large)
49
+ /request exceeds the maximum size/i, // Anthropic 413 variant
50
+ /payload too large/i, // Generic HTTP 413 variant
51
+ /entity too large/i, // Generic HTTP 413 variant
52
+ /\b413\b.*\b(request|payload|entity)\b.*\btoo large\b/i, // "413 Request Entity Too Large" variants
53
+ /model_context_window_exceeded/i, // z.ai non-standard finish_reason surfaced as error text
54
+ ];
55
+ /**
56
+ * Check if an assistant message represents a context overflow error.
57
+ *
58
+ * This handles two cases:
59
+ * 1. Error-based overflow: Most providers return stopReason "error" with a
60
+ * specific error message pattern.
61
+ * 2. Silent overflow: Some providers accept overflow requests and return
62
+ * successfully. For these, we check if usage.input exceeds the context window.
63
+ *
64
+ * ## Reliability by Provider
65
+ *
66
+ * **Reliable detection (returns error with detectable message):**
67
+ * - Anthropic: "prompt is too long: X tokens > Y maximum"
68
+ * - OpenAI (Completions & Responses): "exceeds the context window"
69
+ * - Google Gemini: "input token count exceeds the maximum"
70
+ * - xAI (Grok): "maximum prompt length is X but request contains Y"
71
+ * - Groq: "reduce the length of the messages"
72
+ * - Cerebras: 400/413 status code (no body)
73
+ * - Mistral: 400/413 status code (no body)
74
+ * - HTTP 413 payload/entity-too-large variants
75
+ * - OpenRouter (all backends): "maximum context length is X tokens"
76
+ * - llama.cpp: "exceeds the available context size"
77
+ * - LM Studio: "greater than the context length"
78
+ * - Kimi For Coding: "exceeded model token limit: X (requested: Y)"
79
+ * - Anthropic 413: "request_too_large" (request body exceeds size limit)
80
+ * - HTTP 413: "Payload Too Large" / "Request Entity Too Large"
81
+ *
82
+ * **Unreliable detection:**
83
+ * - z.ai: Sometimes accepts overflow silently (detectable via usage.input > contextWindow),
84
+ * sometimes returns rate limit errors. Pass contextWindow param to detect silent overflow.
85
+ * - Ollama: Silently truncates input without error. Cannot be detected via this function.
86
+ * The response will have usage.input < expected, but we don't know the expected value.
87
+ *
88
+ * ## Custom Providers
89
+ *
90
+ * If you've added custom models via settings.json, this function may not detect
91
+ * overflow errors from those providers. To add support:
92
+ *
93
+ * 1. Send a request that exceeds the model's context window
94
+ * 2. Check the errorMessage in the response
95
+ * 3. Create a regex pattern that matches the error
96
+ * 4. The pattern should be added to OVERFLOW_PATTERNS in this file, or
97
+ * check the errorMessage yourself before calling this function
98
+ *
99
+ * @param message - The assistant message to check
100
+ * @param contextWindow - Optional context window size for detecting silent overflow (z.ai)
101
+ * @returns true if the message indicates a context overflow
102
+ */
103
+ export function isContextOverflow(message: AssistantMessage, contextWindow?: number): boolean {
104
+ // Case 1: Check error message patterns
105
+ if (message.stopReason === "error" && message.errorMessage) {
106
+ // Check known patterns
107
+ if (OVERFLOW_PATTERNS.some(p => p.test(message.errorMessage!))) {
108
+ return true;
109
+ }
110
+
111
+ // Cerebras and Mistral return 400/413 with no body for context overflow.
112
+ // Proxy providers (e.g. api.synthetic.new) wrap upstream 400/413 no-body
113
+ // responses in a JSON envelope, so the status code phrase may appear
114
+ // anywhere in the message rather than at its start.
115
+ // Note: 429 is rate limiting (requests/tokens per time), NOT context overflow
116
+ if (/\b4(00|13)\s*(status code)?\s*\(no body\)/i.test(message.errorMessage)) {
117
+ return true;
118
+ }
119
+ }
120
+
121
+ // Case 2: Usage-based overflow (silent or provider-specific)
122
+ if (contextWindow) {
123
+ const inputTokens = message.usage.input + message.usage.cacheRead + message.usage.cacheWrite;
124
+ if (inputTokens > contextWindow) {
125
+ return true;
126
+ }
127
+ }
128
+
129
+ return false;
130
+ }
131
+
132
+ /**
133
+ * Get the overflow patterns for testing purposes.
134
+ */
135
+ export function getOverflowPatterns(): RegExp[] {
136
+ return [...OVERFLOW_PATTERNS];
137
+ }