@draht/ai 2026.10.20 → 2026.10.23

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 (269) hide show
  1. package/dist/api/anthropic-messages.d.ts.map +1 -1
  2. package/dist/api/anthropic-messages.js +155 -111
  3. package/dist/api/anthropic-messages.js.map +1 -1
  4. package/dist/api/azure-openai-config.d.ts +15 -0
  5. package/dist/api/azure-openai-config.d.ts.map +1 -0
  6. package/dist/api/azure-openai-config.js +76 -0
  7. package/dist/api/azure-openai-config.js.map +1 -0
  8. package/dist/api/azure-openai-responses.d.ts +3 -6
  9. package/dist/api/azure-openai-responses.d.ts.map +1 -1
  10. package/dist/api/azure-openai-responses.js +19 -83
  11. package/dist/api/azure-openai-responses.js.map +1 -1
  12. package/dist/api/bedrock-converse-stream.d.ts.map +1 -1
  13. package/dist/api/bedrock-converse-stream.js +86 -22
  14. package/dist/api/bedrock-converse-stream.js.map +1 -1
  15. package/dist/api/classifier-shared.d.ts +21 -0
  16. package/dist/api/classifier-shared.d.ts.map +1 -0
  17. package/dist/api/classifier-shared.js +99 -0
  18. package/dist/api/classifier-shared.js.map +1 -0
  19. package/dist/api/cloudflare-workers-ai-system-one.d.ts.map +1 -1
  20. package/dist/api/cloudflare-workers-ai-system-one.js +15 -9
  21. package/dist/api/cloudflare-workers-ai-system-one.js.map +1 -1
  22. package/dist/api/google-generative-ai.js +4 -4
  23. package/dist/api/google-generative-ai.js.map +1 -1
  24. package/dist/api/google-shared.d.ts.map +1 -1
  25. package/dist/api/google-shared.js +3 -1
  26. package/dist/api/google-shared.js.map +1 -1
  27. package/dist/api/google-vertex.d.ts.map +1 -1
  28. package/dist/api/google-vertex.js +9 -9
  29. package/dist/api/google-vertex.js.map +1 -1
  30. package/dist/api/lazy.d.ts +7 -2
  31. package/dist/api/lazy.d.ts.map +1 -1
  32. package/dist/api/lazy.js +28 -8
  33. package/dist/api/lazy.js.map +1 -1
  34. package/dist/api/llama-cpp-classify.d.ts.map +1 -1
  35. package/dist/api/llama-cpp-classify.js +4 -1
  36. package/dist/api/llama-cpp-classify.js.map +1 -1
  37. package/dist/api/mistral-conversations.d.ts.map +1 -1
  38. package/dist/api/mistral-conversations.js +38 -18
  39. package/dist/api/mistral-conversations.js.map +1 -1
  40. package/dist/api/openai-codex-responses.d.ts.map +1 -1
  41. package/dist/api/openai-codex-responses.js +21 -13
  42. package/dist/api/openai-codex-responses.js.map +1 -1
  43. package/dist/api/openai-completions.d.ts.map +1 -1
  44. package/dist/api/openai-completions.js +49 -38
  45. package/dist/api/openai-completions.js.map +1 -1
  46. package/dist/api/openai-decisions.d.ts +4 -0
  47. package/dist/api/openai-decisions.d.ts.map +1 -0
  48. package/dist/api/openai-decisions.js +162 -0
  49. package/dist/api/openai-decisions.js.map +1 -0
  50. package/dist/api/openai-decisions.lazy.d.ts +3 -0
  51. package/dist/api/openai-decisions.lazy.d.ts.map +1 -0
  52. package/dist/api/openai-decisions.lazy.js +4 -0
  53. package/dist/api/openai-decisions.lazy.js.map +1 -0
  54. package/dist/api/openai-responses-shared.d.ts +26 -0
  55. package/dist/api/openai-responses-shared.d.ts.map +1 -1
  56. package/dist/api/openai-responses-shared.js +82 -16
  57. package/dist/api/openai-responses-shared.js.map +1 -1
  58. package/dist/api/openai-responses.d.ts.map +1 -1
  59. package/dist/api/openai-responses.js +47 -18
  60. package/dist/api/openai-responses.js.map +1 -1
  61. package/dist/api/openrouter-images.d.ts.map +1 -1
  62. package/dist/api/openrouter-images.js +4 -3
  63. package/dist/api/openrouter-images.js.map +1 -1
  64. package/dist/api/pi-messages.d.ts.map +1 -1
  65. package/dist/api/pi-messages.js +3 -3
  66. package/dist/api/pi-messages.js.map +1 -1
  67. package/dist/api/simple-options.d.ts +2 -1
  68. package/dist/api/simple-options.d.ts.map +1 -1
  69. package/dist/api/simple-options.js +12 -2
  70. package/dist/api/simple-options.js.map +1 -1
  71. package/dist/api/system-one-shared.d.ts +0 -1
  72. package/dist/api/system-one-shared.d.ts.map +1 -1
  73. package/dist/api/system-one-shared.js +5 -89
  74. package/dist/api/system-one-shared.js.map +1 -1
  75. package/dist/api/transform-messages.d.ts +16 -1
  76. package/dist/api/transform-messages.d.ts.map +1 -1
  77. package/dist/api/transform-messages.js +28 -15
  78. package/dist/api/transform-messages.js.map +1 -1
  79. package/dist/api/typesafe-system-one.d.ts.map +1 -1
  80. package/dist/api/typesafe-system-one.js +2 -1
  81. package/dist/api/typesafe-system-one.js.map +1 -1
  82. package/dist/auth/oauth/anthropic.d.ts.map +1 -1
  83. package/dist/auth/oauth/anthropic.js +65 -7
  84. package/dist/auth/oauth/anthropic.js.map +1 -1
  85. package/dist/auth/oauth/load.d.ts +2 -0
  86. package/dist/auth/oauth/load.d.ts.map +1 -1
  87. package/dist/auth/oauth/load.js +5 -0
  88. package/dist/auth/oauth/load.js.map +1 -1
  89. package/dist/auth/oauth/openai-chatgpt.d.ts.map +1 -1
  90. package/dist/auth/oauth/openai-chatgpt.js +11 -14
  91. package/dist/auth/oauth/openai-chatgpt.js.map +1 -1
  92. package/dist/auth/oauth/openai-codex.d.ts.map +1 -1
  93. package/dist/auth/oauth/openai-codex.js +7 -5
  94. package/dist/auth/oauth/openai-codex.js.map +1 -1
  95. package/dist/auth/oauth/opencode-go.d.ts +3 -12
  96. package/dist/auth/oauth/opencode-go.d.ts.map +1 -1
  97. package/dist/auth/oauth/opencode-go.js +229 -34
  98. package/dist/auth/oauth/opencode-go.js.map +1 -1
  99. package/dist/auth/resolve.d.ts +16 -1
  100. package/dist/auth/resolve.d.ts.map +1 -1
  101. package/dist/auth/resolve.js +51 -27
  102. package/dist/auth/resolve.js.map +1 -1
  103. package/dist/auth/types.d.ts +5 -0
  104. package/dist/auth/types.d.ts.map +1 -1
  105. package/dist/auth/types.js.map +1 -1
  106. package/dist/bun-oauth.d.ts.map +1 -1
  107. package/dist/bun-oauth.js +2 -1
  108. package/dist/bun-oauth.js.map +1 -1
  109. package/dist/env-api-keys.js +1 -1
  110. package/dist/env-api-keys.js.map +1 -1
  111. package/dist/index.d.ts +2 -1
  112. package/dist/index.d.ts.map +1 -1
  113. package/dist/index.js +2 -1
  114. package/dist/index.js.map +1 -1
  115. package/dist/models.d.ts +9 -0
  116. package/dist/models.d.ts.map +1 -1
  117. package/dist/models.generated.d.ts +4 -4
  118. package/dist/models.generated.d.ts.map +1 -1
  119. package/dist/models.generated.js +4 -4
  120. package/dist/models.generated.js.map +1 -1
  121. package/dist/models.js +26 -14
  122. package/dist/models.js.map +1 -1
  123. package/dist/providers/all.js +2 -2
  124. package/dist/providers/all.js.map +1 -1
  125. package/dist/providers/azure.d.ts +3 -0
  126. package/dist/providers/azure.d.ts.map +1 -0
  127. package/dist/providers/azure.js +46 -0
  128. package/dist/providers/azure.js.map +1 -0
  129. package/dist/providers/azure.models.d.ts +6 -0
  130. package/dist/providers/azure.models.d.ts.map +1 -0
  131. package/dist/providers/azure.models.js +8 -0
  132. package/dist/providers/azure.models.js.map +1 -0
  133. package/dist/providers/compat-schema.d.ts +491 -0
  134. package/dist/providers/compat-schema.d.ts.map +1 -0
  135. package/dist/providers/compat-schema.js +386 -0
  136. package/dist/providers/compat-schema.js.map +1 -0
  137. package/dist/providers/data/.manifest.json +1 -1
  138. package/dist/providers/data/amazon-bedrock.json +1 -1
  139. package/dist/providers/data/anthropic.json +1 -1
  140. package/dist/providers/data/azure.json +1 -0
  141. package/dist/providers/data/baseten.json +1 -1
  142. package/dist/providers/data/cerebras.json +1 -1
  143. package/dist/providers/data/cloudflare-ai-gateway.json +1 -1
  144. package/dist/providers/data/cloudflare-workers-ai.json +1 -1
  145. package/dist/providers/data/fireworks.json +1 -1
  146. package/dist/providers/data/github-copilot.json +1 -1
  147. package/dist/providers/data/google-vertex.json +1 -1
  148. package/dist/providers/data/google.json +1 -1
  149. package/dist/providers/data/groq.json +1 -1
  150. package/dist/providers/data/huggingface.json +1 -1
  151. package/dist/providers/data/kimi-coding.json +1 -1
  152. package/dist/providers/data/meta.json +1 -1
  153. package/dist/providers/data/minimax-cn.json +1 -1
  154. package/dist/providers/data/minimax.json +1 -1
  155. package/dist/providers/data/mistral.json +1 -1
  156. package/dist/providers/data/moonshotai-cn.json +1 -1
  157. package/dist/providers/data/nvidia.json +1 -1
  158. package/dist/providers/data/openai-codex.json +1 -1
  159. package/dist/providers/data/openai.json +1 -1
  160. package/dist/providers/data/opencode-go.json +1 -1
  161. package/dist/providers/data/opencode.json +1 -1
  162. package/dist/providers/data/openrouter.json +1 -1
  163. package/dist/providers/data/qwen-token-plan-cn.json +1 -1
  164. package/dist/providers/data/qwen-token-plan.json +1 -1
  165. package/dist/providers/data/radius.json +1 -1
  166. package/dist/providers/data/together.json +1 -1
  167. package/dist/providers/data/vercel-ai-gateway.json +1 -1
  168. package/dist/providers/data/xai.json +1 -1
  169. package/dist/providers/data/xiaomi-token-plan-ams.json +1 -1
  170. package/dist/providers/data/xiaomi-token-plan-cn.json +1 -1
  171. package/dist/providers/data/xiaomi-token-plan-sgp.json +1 -1
  172. package/dist/providers/data/xiaomi.json +1 -1
  173. package/dist/providers/data/zai-coding-cn.json +1 -1
  174. package/dist/providers/faux.d.ts.map +1 -1
  175. package/dist/providers/faux.js +25 -8
  176. package/dist/providers/faux.js.map +1 -1
  177. package/dist/providers/kimi-coding.d.ts +1 -1
  178. package/dist/providers/kimi-coding.js +3 -3
  179. package/dist/providers/kimi-coding.js.map +1 -1
  180. package/dist/providers/model-schema.d.ts +91 -0
  181. package/dist/providers/model-schema.d.ts.map +1 -0
  182. package/dist/providers/model-schema.js +57 -0
  183. package/dist/providers/model-schema.js.map +1 -0
  184. package/dist/providers/openai.d.ts.map +1 -1
  185. package/dist/providers/openai.js +12 -3
  186. package/dist/providers/openai.js.map +1 -1
  187. package/dist/providers/opencode-go.d.ts +3 -1
  188. package/dist/providers/opencode-go.d.ts.map +1 -1
  189. package/dist/providers/opencode-go.js +4 -3
  190. package/dist/providers/opencode-go.js.map +1 -1
  191. package/dist/providers/opencode.d.ts.map +1 -1
  192. package/dist/providers/opencode.js +10 -2
  193. package/dist/providers/opencode.js.map +1 -1
  194. package/dist/providers/openrouter-api.d.ts +40 -0
  195. package/dist/providers/openrouter-api.d.ts.map +1 -0
  196. package/dist/providers/openrouter-api.js +41 -0
  197. package/dist/providers/openrouter-api.js.map +1 -0
  198. package/dist/providers/openrouter.d.ts +11 -1
  199. package/dist/providers/openrouter.d.ts.map +1 -1
  200. package/dist/providers/openrouter.js +52 -1
  201. package/dist/providers/openrouter.js.map +1 -1
  202. package/dist/providers/radius.d.ts.map +1 -1
  203. package/dist/providers/radius.js +4 -12
  204. package/dist/providers/radius.js.map +1 -1
  205. package/dist/telemetry.d.ts +288 -0
  206. package/dist/telemetry.d.ts.map +1 -0
  207. package/dist/telemetry.js +191 -0
  208. package/dist/telemetry.js.map +1 -0
  209. package/dist/types.d.ts +42 -310
  210. package/dist/types.d.ts.map +1 -1
  211. package/dist/types.js.map +1 -1
  212. package/dist/utils/estimate.js +1 -1
  213. package/dist/utils/estimate.js.map +1 -1
  214. package/dist/utils/event-stream.d.ts +10 -1
  215. package/dist/utils/event-stream.d.ts.map +1 -1
  216. package/dist/utils/event-stream.js +25 -0
  217. package/dist/utils/event-stream.js.map +1 -1
  218. package/dist/utils/identity.d.ts +65 -0
  219. package/dist/utils/identity.d.ts.map +1 -0
  220. package/dist/utils/identity.js +145 -0
  221. package/dist/utils/identity.js.map +1 -0
  222. package/dist/utils/json-parse.d.ts +10 -0
  223. package/dist/utils/json-parse.d.ts.map +1 -1
  224. package/dist/utils/json-parse.js +26 -1
  225. package/dist/utils/json-parse.js.map +1 -1
  226. package/dist/utils/model-operations.d.ts +3 -1
  227. package/dist/utils/model-operations.d.ts.map +1 -1
  228. package/dist/utils/model-operations.js +6 -0
  229. package/dist/utils/model-operations.js.map +1 -1
  230. package/dist/utils/overflow.d.ts +1 -0
  231. package/dist/utils/overflow.d.ts.map +1 -1
  232. package/dist/utils/overflow.js +6 -1
  233. package/dist/utils/overflow.js.map +1 -1
  234. package/dist/utils/provider-retry.d.ts +2 -0
  235. package/dist/utils/provider-retry.d.ts.map +1 -1
  236. package/dist/utils/provider-retry.js +2 -0
  237. package/dist/utils/provider-retry.js.map +1 -1
  238. package/dist/utils/retry.d.ts.map +1 -1
  239. package/dist/utils/retry.js +30 -10
  240. package/dist/utils/retry.js.map +1 -1
  241. package/dist/utils/transcript.d.ts +27 -10
  242. package/dist/utils/transcript.d.ts.map +1 -1
  243. package/dist/utils/transcript.js +34 -20
  244. package/dist/utils/transcript.js.map +1 -1
  245. package/dist/utils/validation.d.ts.map +1 -1
  246. package/dist/utils/validation.js +3 -0
  247. package/dist/utils/validation.js.map +1 -1
  248. package/dist/utils/version.d.ts +2 -0
  249. package/dist/utils/version.d.ts.map +1 -0
  250. package/dist/utils/version.js +5 -0
  251. package/dist/utils/version.js.map +1 -0
  252. package/package.json +5 -5
  253. package/dist/providers/azure-openai-responses.d.ts +0 -3
  254. package/dist/providers/azure-openai-responses.d.ts.map +0 -1
  255. package/dist/providers/azure-openai-responses.js +0 -14
  256. package/dist/providers/azure-openai-responses.js.map +0 -1
  257. package/dist/providers/azure-openai-responses.models.d.ts +0 -6
  258. package/dist/providers/azure-openai-responses.models.d.ts.map +0 -1
  259. package/dist/providers/azure-openai-responses.models.js +0 -8
  260. package/dist/providers/azure-openai-responses.models.js.map +0 -1
  261. package/dist/providers/data/azure-openai-responses.json +0 -1
  262. package/dist/utils/assistant-message-frame.d.ts +0 -77
  263. package/dist/utils/assistant-message-frame.d.ts.map +0 -1
  264. package/dist/utils/assistant-message-frame.js +0 -425
  265. package/dist/utils/assistant-message-frame.js.map +0 -1
  266. package/dist/utils/draht-user-agent.d.ts +0 -2
  267. package/dist/utils/draht-user-agent.d.ts.map +0 -1
  268. package/dist/utils/draht-user-agent.js +0 -12
  269. package/dist/utils/draht-user-agent.js.map +0 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"identity.js","sourceRoot":"","sources":["../../src/utils/identity.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Models, MutableModels } from \"../models.ts\";\nimport { DRAHT_AI_VERSION } from \"./version.ts\";\n\n/**\n * The `User-Agent` sent to providers:\n * - `\"detailed\"`: `draht/<version> (<platform>; <runtime>/<runtime version>; <arch>)`, or `draht/<version> (browser)`\n * outside Node.js and Bun.\n * - `\"short\"`: `draht`.\n * - `{ custom }`: the given value.\n */\nexport type UserAgentMode = \"short\" | \"detailed\" | { custom: string };\n\n/** Who requests are attributed to. `getAttributionHeaders()` maps the fields to each provider's headers. */\nexport interface ClientAttribution {\n\t/** App name: OpenRouter app title, NVIDIA invoke origin (first letter upper-cased), OpenCode client. */\n\tname: string;\n\t/** App URL: OpenRouter `HTTP-Referer`, the app's identity in OpenRouter's rankings, grouped by origin. */\n\turl: string;\n\t/** App categories, e.g. `[\"cli-agent\"]`: OpenRouter `X-OpenRouter-Categories`, comma-separated. */\n\tcategories?: string[];\n}\n\n/** draht's attribution, used by @draht/agent-core and the coding agent. */\nexport const DRAHT_ATTRIBUTION: ClientAttribution = {\n\tname: \"draht\",\n\turl: \"https://github.com/draht-dev/draht\",\n\tcategories: [\"cli-agent\"],\n};\n\n/**\n * How @draht/ai identifies the calling app to providers. Every field is optional; absent fields use @draht/ai's defaults:\n * a detailed `User-Agent` with @draht/ai's version and no attribution headers. Plain JSON, so it can be persisted.\n */\nexport interface ClientIdentity {\n\t/** Default: `\"detailed\"`. */\n\tuserAgent?: UserAgentMode;\n\t/** Provider attribution headers. Absent or `null`: none; `null` also turns off attribution set by a merged identity. */\n\tattribution?: ClientAttribution | null;\n\t/** Version in the detailed `User-Agent`. Default: the @draht/ai version. */\n\tversion?: string;\n}\n\n/** The provider and endpoint a request goes to. */\nexport interface ClientIdentityTarget {\n\treadonly provider: string;\n\treadonly baseUrl: string;\n}\n\nconst NVIDIA_NIM_HOST = \"integrate.api.nvidia.com\";\nconst OPENCODE_HOST = \"opencode.ai\";\n\n/** Merge identities field by field; later sources win and undefined fields are skipped. */\nexport function mergeClientIdentity(...sources: (ClientIdentity | undefined)[]): ClientIdentity | undefined {\n\tlet merged: ClientIdentity | undefined;\n\tfor (const source of sources) {\n\t\tif (!source) continue;\n\t\tmerged = { ...merged };\n\t\tif (source.userAgent !== undefined) merged.userAgent = source.userAgent;\n\t\tif (source.attribution !== undefined) merged.attribution = source.attribution;\n\t\tif (source.version !== undefined) merged.version = source.version;\n\t}\n\treturn merged;\n}\n\n/** The version in the detailed `User-Agent`. */\nexport function getClientVersion(identity?: ClientIdentity): string {\n\treturn identity?.version ?? DRAHT_AI_VERSION;\n}\n\nfunction runtimeDescription(): string | undefined {\n\tif (typeof process === \"undefined\") return undefined;\n\tconst versions = process.versions;\n\tif (versions?.bun) return `bun/${versions.bun}`;\n\tif (versions?.node) return `node/${process.version}`;\n\treturn undefined;\n}\n\n/** The `User-Agent` value of an identity. */\nexport function formatUserAgent(identity?: ClientIdentity): string {\n\tconst mode = identity?.userAgent ?? \"detailed\";\n\tif (typeof mode === \"object\") return mode.custom;\n\tif (mode === \"short\") return \"draht\";\n\tconst product = `draht/${getClientVersion(identity)}`;\n\tconst runtime = runtimeDescription();\n\treturn runtime ? `${product} (${process.platform}; ${runtime}; ${process.arch})` : `${product} (browser)`;\n}\n\nfunction matchesHost(baseUrl: string, expectedHost: string): boolean {\n\ttry {\n\t\treturn new URL(baseUrl).hostname === expectedHost;\n\t} catch {\n\t\treturn false;\n\t}\n}\n\nfunction isOpenRouter(target: ClientIdentityTarget): boolean {\n\treturn target.provider === \"openrouter\" || target.baseUrl.includes(\"openrouter.ai\");\n}\n\nfunction isNvidiaNim(target: ClientIdentityTarget): boolean {\n\treturn target.provider === \"nvidia\" || matchesHost(target.baseUrl, NVIDIA_NIM_HOST);\n}\n\nfunction isOpenCode(target: ClientIdentityTarget): boolean {\n\treturn (\n\t\ttarget.provider === \"opencode\" || target.provider === \"opencode-go\" || matchesHost(target.baseUrl, OPENCODE_HOST)\n\t);\n}\n\n/** Provider attribution headers of an identity for a request target. Empty when attribution is off. */\nexport function getAttributionHeaders(target: ClientIdentityTarget, identity?: ClientIdentity): Record<string, string> {\n\tconst attribution = identity?.attribution;\n\tif (!attribution) return {};\n\tconst { name, url, categories } = attribution;\n\tif (isOpenRouter(target)) {\n\t\treturn {\n\t\t\t\"HTTP-Referer\": url,\n\t\t\t\"X-OpenRouter-Title\": name,\n\t\t\t...(categories === undefined || categories.length === 0\n\t\t\t\t? {}\n\t\t\t\t: { \"X-OpenRouter-Categories\": categories.join(\",\") }),\n\t\t};\n\t}\n\t// NVIDIA expects a capitalized origin, such as `Draht`.\n\tif (isNvidiaNim(target)) return { \"X-BILLING-INVOKE-ORIGIN\": name.charAt(0).toUpperCase() + name.slice(1) };\n\tif (isOpenCode(target)) return { \"x-opencode-client\": name };\n\treturn {};\n}\n\n/**\n * OpenCode's per-conversation routing header for requests to OpenCode, including custom providers pointing at it.\n * Undefined without a session or for other targets.\n */\nexport function getOpenCodeSessionHeaders(\n\ttarget: ClientIdentityTarget,\n\tsessionId: string | undefined,\n): Record<string, string> | undefined {\n\treturn sessionId && isOpenCode(target) ? { \"x-opencode-session\": sessionId } : undefined;\n}\n\n/**\n * Default headers an identity adds to a request: `User-Agent` and provider attribution. APIs apply them below model,\n * provider, and request headers, so configured headers override them and `null` request headers remove them.\n */\nexport function getIdentityHeaders(target: ClientIdentityTarget, identity?: ClientIdentity): Record<string, string> {\n\treturn { \"User-Agent\": formatUserAgent(identity), ...getAttributionHeaders(target, identity) };\n}\n\nfunction isMutableModels(models: Models): models is MutableModels {\n\treturn \"setProvider\" in models && \"deleteProvider\" in models && \"clearProviders\" in models;\n}\n\n/**\n * `models` with `identity` as the default identity of its requests: a request's `identity` overrides it field by\n * field, and it overrides the identity `models` was created with. Everything else is delegated unchanged; a\n * `MutableModels` stays mutable.\n */\nexport function withClientIdentity(models: MutableModels, identity: ClientIdentity): MutableModels;\nexport function withClientIdentity(models: Models, identity: ClientIdentity): Models;\nexport function withClientIdentity(models: Models, identity: ClientIdentity): Models {\n\tconst withIdentity = <T extends { identity?: ClientIdentity }>(options: T | undefined): T =>\n\t\t({ ...options, identity: mergeClientIdentity(identity, options?.identity) }) as T;\n\tconst wrapped: Models = {\n\t\tgetProviders: () => models.getProviders(),\n\t\tgetProvider: (id) => models.getProvider(id),\n\t\tgetModels: (provider) => models.getModels(provider),\n\t\tgetModel: (provider, id) => models.getModel(provider, id),\n\t\tgetModelsOfType: (type, provider) => models.getModelsOfType(type, provider),\n\t\tgetModelOfType: (type, provider, id) => models.getModelOfType(type, provider, id),\n\t\tgetAllModels: (provider) => models.getAllModels(provider),\n\t\trefresh: (options) => models.refresh(options),\n\t\tcheckAuth: (providerId, options) => models.checkAuth(providerId, options),\n\t\tgetAvailable: (providerId, options) => models.getAvailable(providerId, options),\n\t\tgetAvailableOfType: (type, providerId, options) => models.getAvailableOfType(type, providerId, options),\n\t\tgetAllAvailable: (providerId, options) => models.getAllAvailable(providerId, options),\n\t\tgetAuth: models.getAuth.bind(models),\n\t\tlogin: (providerId, type, interaction, options) => models.login(providerId, type, interaction, options),\n\t\tlogout: (providerId, options) => models.logout(providerId, options),\n\t\tstream: (model, context, options) => models.stream(model, context, withIdentity(options)),\n\t\tcomplete: (model, context, options) => models.complete(model, context, withIdentity(options)),\n\t\tstreamSimple: (model, context, options) => models.streamSimple(model, context, withIdentity(options)),\n\t\tcompleteSimple: (model, context, options) => models.completeSimple(model, context, withIdentity(options)),\n\t\tstreamDeferred: (model, handle, options) => models.streamDeferred(model, handle, withIdentity(options)),\n\t\tfetchDeferred: (model, handle, options) => models.fetchDeferred(model, handle, withIdentity(options)),\n\t\tcancelDeferred: (model, handle, options) => models.cancelDeferred(model, handle, withIdentity(options)),\n\t\tgenerateImages: (model, context, options) => models.generateImages(model, context, withIdentity(options)),\n\t\tclassify: (model, context, options) => models.classify(model, context, withIdentity(options)),\n\t};\n\tif (!isMutableModels(models)) return wrapped;\n\tconst mutable: MutableModels = {\n\t\t...wrapped,\n\t\tsetProvider: (provider) => models.setProvider(provider),\n\t\tdeleteProvider: (id) => models.deleteProvider(id),\n\t\tclearProviders: () => models.clearProviders(),\n\t};\n\treturn mutable;\n}\n"]}
@@ -1,3 +1,4 @@
1
+ import type { ToolCall } from "../types.ts";
1
2
  /**
2
3
  * Repairs malformed JSON string literals by:
3
4
  * - escaping raw control characters inside strings
@@ -5,6 +6,15 @@
5
6
  */
6
7
  export declare function repairJson(json: string): string;
7
8
  export declare function parseJsonWithRepair<T>(json: string): T;
9
+ /**
10
+ * Parses the model's complete arguments text of a finished tool call.
11
+ *
12
+ * Unlike {@link parseStreamingJson}, this never keeps a partially parsed prefix: once the call is
13
+ * finished, text that is not a valid JSON object means the model sent malformed arguments. In that
14
+ * case `arguments` is `{}` and `argumentsError` describes the problem, including the text the
15
+ * model sent, so tool validation can report it back to the model.
16
+ */
17
+ export declare function parseToolCallArguments(json: string | undefined): Pick<ToolCall, "arguments" | "argumentsError">;
8
18
  /**
9
19
  * Attempts to parse potentially incomplete JSON during streaming.
10
20
  * Always returns a valid object, even if the JSON is incomplete.
@@ -1 +1 @@
1
- {"version":3,"file":"json-parse.d.ts","sourceRoot":"","sources":["../../src/utils/json-parse.ts"],"names":[],"mappings":"AA0BA;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAmD/C;AAED,wBAAgB,mBAAmB,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,GAAG,CAAC,CAUtD;AAED;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,WAAW,EAAE,MAAM,GAAG,SAAS,GAAG,CAAC,CAoBlG"}
1
+ {"version":3,"file":"json-parse.d.ts","sourceRoot":"","sources":["../../src/utils/json-parse.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AA0B5C;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAmD/C;AAED,wBAAgB,mBAAmB,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,GAAG,CAAC,CAUtD;AAED;;;;;;;GAOG;AACH,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,CAAC,QAAQ,EAAE,WAAW,GAAG,gBAAgB,CAAC,CAc/G;AAED;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,WAAW,EAAE,MAAM,GAAG,SAAS,GAAG,CAAC,CAoBlG"}
@@ -1,5 +1,5 @@
1
1
  import { parse as partialParse } from "partial-json";
2
- const VALID_JSON_ESCAPES = new Set(['"', "\\", "/", "b", "f", "n", "r", "t", "u"]);
2
+ const VALID_JSON_ESCAPES = new Set(['"', "\\", "/", "b", "f", "n", "r", "t"]);
3
3
  function isControlCharacter(char) {
4
4
  const codePoint = char.codePointAt(0);
5
5
  return codePoint !== undefined && codePoint >= 0x00 && codePoint <= 0x1f;
@@ -80,6 +80,31 @@ export function parseJsonWithRepair(json) {
80
80
  throw error;
81
81
  }
82
82
  }
83
+ /**
84
+ * Parses the model's complete arguments text of a finished tool call.
85
+ *
86
+ * Unlike {@link parseStreamingJson}, this never keeps a partially parsed prefix: once the call is
87
+ * finished, text that is not a valid JSON object means the model sent malformed arguments. In that
88
+ * case `arguments` is `{}` and `argumentsError` describes the problem, including the text the
89
+ * model sent, so tool validation can report it back to the model.
90
+ */
91
+ export function parseToolCallArguments(json) {
92
+ if (!json?.trim())
93
+ return { arguments: {} };
94
+ try {
95
+ const parsed = parseJsonWithRepair(json);
96
+ if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
97
+ throw new Error("Tool arguments must be a JSON object");
98
+ }
99
+ return { arguments: parsed };
100
+ }
101
+ catch (error) {
102
+ return {
103
+ arguments: {},
104
+ argumentsError: `${error instanceof Error ? error.message : String(error)}\n\nReceived arguments:\n${json}`,
105
+ };
106
+ }
107
+ }
83
108
  /**
84
109
  * Attempts to parse potentially incomplete JSON during streaming.
85
110
  * Always returns a valid object, even if the JSON is incomplete.
@@ -1 +1 @@
1
- {"version":3,"file":"json-parse.js","sourceRoot":"","sources":["../../src/utils/json-parse.ts"],"names":[],"mappings":"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","sourcesContent":["import { parse as partialParse } from \"partial-json\";\n\nconst VALID_JSON_ESCAPES = new Set(['\"', \"\\\\\", \"/\", \"b\", \"f\", \"n\", \"r\", \"t\", \"u\"]);\n\nfunction isControlCharacter(char: string): boolean {\n\tconst codePoint = char.codePointAt(0);\n\treturn codePoint !== undefined && codePoint >= 0x00 && codePoint <= 0x1f;\n}\n\nfunction escapeControlCharacter(char: string): string {\n\tswitch (char) {\n\t\tcase \"\\b\":\n\t\t\treturn \"\\\\b\";\n\t\tcase \"\\f\":\n\t\t\treturn \"\\\\f\";\n\t\tcase \"\\n\":\n\t\t\treturn \"\\\\n\";\n\t\tcase \"\\r\":\n\t\t\treturn \"\\\\r\";\n\t\tcase \"\\t\":\n\t\t\treturn \"\\\\t\";\n\t\tdefault:\n\t\t\treturn `\\\\u${char.codePointAt(0)?.toString(16).padStart(4, \"0\") ?? \"0000\"}`;\n\t}\n}\n\n/**\n * Repairs malformed JSON string literals by:\n * - escaping raw control characters inside strings\n * - doubling backslashes before invalid escape characters\n */\nexport function repairJson(json: string): string {\n\tlet repaired = \"\";\n\tlet inString = false;\n\n\tfor (let index = 0; index < json.length; index++) {\n\t\tconst char = json[index];\n\n\t\tif (!inString) {\n\t\t\trepaired += char;\n\t\t\tif (char === '\"') {\n\t\t\t\tinString = true;\n\t\t\t}\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (char === '\"') {\n\t\t\trepaired += char;\n\t\t\tinString = false;\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (char === \"\\\\\") {\n\t\t\tconst nextChar = json[index + 1];\n\t\t\tif (nextChar === undefined) {\n\t\t\t\trepaired += \"\\\\\\\\\";\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tif (nextChar === \"u\") {\n\t\t\t\tconst unicodeDigits = json.slice(index + 2, index + 6);\n\t\t\t\tif (/^[0-9a-fA-F]{4}$/.test(unicodeDigits)) {\n\t\t\t\t\trepaired += `\\\\u${unicodeDigits}`;\n\t\t\t\t\tindex += 5;\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (VALID_JSON_ESCAPES.has(nextChar)) {\n\t\t\t\trepaired += `\\\\${nextChar}`;\n\t\t\t\tindex += 1;\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\trepaired += \"\\\\\\\\\";\n\t\t\tcontinue;\n\t\t}\n\n\t\trepaired += isControlCharacter(char) ? escapeControlCharacter(char) : char;\n\t}\n\n\treturn repaired;\n}\n\nexport function parseJsonWithRepair<T>(json: string): T {\n\ttry {\n\t\treturn JSON.parse(json) as T;\n\t} catch (error) {\n\t\tconst repairedJson = repairJson(json);\n\t\tif (repairedJson !== json) {\n\t\t\treturn JSON.parse(repairedJson) as T;\n\t\t}\n\t\tthrow error;\n\t}\n}\n\n/**\n * Attempts to parse potentially incomplete JSON during streaming.\n * Always returns a valid object, even if the JSON is incomplete.\n *\n * @param partialJson The partial JSON string from streaming\n * @returns Parsed object or empty object if parsing fails\n */\nexport function parseStreamingJson<T = Record<string, unknown>>(partialJson: string | undefined): T {\n\tif (!partialJson || partialJson.trim() === \"\") {\n\t\treturn {} as T;\n\t}\n\n\ttry {\n\t\treturn parseJsonWithRepair<T>(partialJson);\n\t} catch {\n\t\ttry {\n\t\t\tconst result = partialParse(partialJson);\n\t\t\treturn (result ?? {}) as T;\n\t\t} catch {\n\t\t\ttry {\n\t\t\t\tconst result = partialParse(repairJson(partialJson));\n\t\t\t\treturn (result ?? {}) as T;\n\t\t\t} catch {\n\t\t\t\treturn {} as T;\n\t\t\t}\n\t\t}\n\t}\n}\n"]}
1
+ {"version":3,"file":"json-parse.js","sourceRoot":"","sources":["../../src/utils/json-parse.ts"],"names":[],"mappings":"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","sourcesContent":["import { parse as partialParse } from \"partial-json\";\nimport type { ToolCall } from \"../types.ts\";\n\nconst VALID_JSON_ESCAPES = new Set(['\"', \"\\\\\", \"/\", \"b\", \"f\", \"n\", \"r\", \"t\"]);\n\nfunction isControlCharacter(char: string): boolean {\n\tconst codePoint = char.codePointAt(0);\n\treturn codePoint !== undefined && codePoint >= 0x00 && codePoint <= 0x1f;\n}\n\nfunction escapeControlCharacter(char: string): string {\n\tswitch (char) {\n\t\tcase \"\\b\":\n\t\t\treturn \"\\\\b\";\n\t\tcase \"\\f\":\n\t\t\treturn \"\\\\f\";\n\t\tcase \"\\n\":\n\t\t\treturn \"\\\\n\";\n\t\tcase \"\\r\":\n\t\t\treturn \"\\\\r\";\n\t\tcase \"\\t\":\n\t\t\treturn \"\\\\t\";\n\t\tdefault:\n\t\t\treturn `\\\\u${char.codePointAt(0)?.toString(16).padStart(4, \"0\") ?? \"0000\"}`;\n\t}\n}\n\n/**\n * Repairs malformed JSON string literals by:\n * - escaping raw control characters inside strings\n * - doubling backslashes before invalid escape characters\n */\nexport function repairJson(json: string): string {\n\tlet repaired = \"\";\n\tlet inString = false;\n\n\tfor (let index = 0; index < json.length; index++) {\n\t\tconst char = json[index];\n\n\t\tif (!inString) {\n\t\t\trepaired += char;\n\t\t\tif (char === '\"') {\n\t\t\t\tinString = true;\n\t\t\t}\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (char === '\"') {\n\t\t\trepaired += char;\n\t\t\tinString = false;\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (char === \"\\\\\") {\n\t\t\tconst nextChar = json[index + 1];\n\t\t\tif (nextChar === undefined) {\n\t\t\t\trepaired += \"\\\\\\\\\";\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tif (nextChar === \"u\") {\n\t\t\t\tconst unicodeDigits = json.slice(index + 2, index + 6);\n\t\t\t\tif (/^[0-9a-fA-F]{4}$/.test(unicodeDigits)) {\n\t\t\t\t\trepaired += `\\\\u${unicodeDigits}`;\n\t\t\t\t\tindex += 5;\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (VALID_JSON_ESCAPES.has(nextChar)) {\n\t\t\t\trepaired += `\\\\${nextChar}`;\n\t\t\t\tindex += 1;\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\trepaired += \"\\\\\\\\\";\n\t\t\tcontinue;\n\t\t}\n\n\t\trepaired += isControlCharacter(char) ? escapeControlCharacter(char) : char;\n\t}\n\n\treturn repaired;\n}\n\nexport function parseJsonWithRepair<T>(json: string): T {\n\ttry {\n\t\treturn JSON.parse(json) as T;\n\t} catch (error) {\n\t\tconst repairedJson = repairJson(json);\n\t\tif (repairedJson !== json) {\n\t\t\treturn JSON.parse(repairedJson) as T;\n\t\t}\n\t\tthrow error;\n\t}\n}\n\n/**\n * Parses the model's complete arguments text of a finished tool call.\n *\n * Unlike {@link parseStreamingJson}, this never keeps a partially parsed prefix: once the call is\n * finished, text that is not a valid JSON object means the model sent malformed arguments. In that\n * case `arguments` is `{}` and `argumentsError` describes the problem, including the text the\n * model sent, so tool validation can report it back to the model.\n */\nexport function parseToolCallArguments(json: string | undefined): Pick<ToolCall, \"arguments\" | \"argumentsError\"> {\n\tif (!json?.trim()) return { arguments: {} };\n\ttry {\n\t\tconst parsed = parseJsonWithRepair<ToolCall[\"arguments\"] | null>(json);\n\t\tif (typeof parsed !== \"object\" || parsed === null || Array.isArray(parsed)) {\n\t\t\tthrow new Error(\"Tool arguments must be a JSON object\");\n\t\t}\n\t\treturn { arguments: parsed };\n\t} catch (error) {\n\t\treturn {\n\t\t\targuments: {},\n\t\t\targumentsError: `${error instanceof Error ? error.message : String(error)}\\n\\nReceived arguments:\\n${json}`,\n\t\t};\n\t}\n}\n\n/**\n * Attempts to parse potentially incomplete JSON during streaming.\n * Always returns a valid object, even if the JSON is incomplete.\n *\n * @param partialJson The partial JSON string from streaming\n * @returns Parsed object or empty object if parsing fails\n */\nexport function parseStreamingJson<T = Record<string, unknown>>(partialJson: string | undefined): T {\n\tif (!partialJson || partialJson.trim() === \"\") {\n\t\treturn {} as T;\n\t}\n\n\ttry {\n\t\treturn parseJsonWithRepair<T>(partialJson);\n\t} catch {\n\t\ttry {\n\t\t\tconst result = partialParse(partialJson);\n\t\t\treturn (result ?? {}) as T;\n\t\t} catch {\n\t\t\ttry {\n\t\t\t\tconst result = partialParse(repairJson(partialJson));\n\t\t\t\treturn (result ?? {}) as T;\n\t\t\t} catch {\n\t\t\t\treturn {} as T;\n\t\t\t}\n\t\t}\n\t}\n}\n"]}
@@ -1,4 +1,4 @@
1
- import type { AnyModel, Api, AssistantImages, ClassifierApi, ClassifierModel, ClassifierResult, ImageApi, ImageModel, Model, ModelType, ModelTypeMap } from "../types.ts";
1
+ import type { AnyModel, Api, AssistantImages, ClassifierApi, ClassifierContext, ClassifierModel, ClassifierResult, ImageApi, ImageModel, Model, ModelType, ModelTypeMap } from "../types.ts";
2
2
  /** The type of a model. Models without `type` are chat models. */
3
3
  export declare function getModelType(model: AnyModel): ModelType;
4
4
  /** Runtime-checked model type narrowing, including legacy chat models without `type`. */
@@ -6,6 +6,8 @@ export declare function isModelType<TType extends ModelType>(model: AnyModel, ty
6
6
  export declare function assertChatModel(model: AnyModel): asserts model is Model<Api>;
7
7
  export declare function assertImageModel(model: AnyModel): asserts model is ImageModel<ImageApi>;
8
8
  export declare function assertClassifierModel(model: AnyModel): asserts model is ClassifierModel<ClassifierApi>;
9
+ /** Rejects classifier images for models whose catalog entry does not accept image input. */
10
+ export declare function assertClassifierInputSupported(model: ClassifierModel<ClassifierApi>, context: ClassifierContext): void;
9
11
  export declare function imageErrorResult(model: ImageModel<ImageApi>, error: unknown, aborted?: boolean): AssistantImages;
10
12
  export declare function classifierErrorResult(model: ClassifierModel<ClassifierApi>, error: unknown, aborted?: boolean): ClassifierResult;
11
13
  //# sourceMappingURL=model-operations.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"model-operations.d.ts","sourceRoot":"","sources":["../../src/utils/model-operations.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACX,QAAQ,EACR,GAAG,EACH,eAAe,EACf,aAAa,EACb,eAAe,EACf,gBAAgB,EAChB,QAAQ,EACR,UAAU,EACV,KAAK,EACL,SAAS,EACT,YAAY,EACZ,MAAM,aAAa,CAAC;AAGrB,kEAAkE;AAClE,wBAAgB,YAAY,CAAC,KAAK,EAAE,QAAQ,GAAG,SAAS,CAEvD;AAED,yFAAyF;AACzF,wBAAgB,WAAW,CAAC,KAAK,SAAS,SAAS,EAAE,KAAK,EAAE,QAAQ,EAAE,IAAI,EAAE,KAAK,GAAG,KAAK,IAAI,YAAY,CAAC,KAAK,CAAC,CAE/G;AAED,wBAAgB,eAAe,CAAC,KAAK,EAAE,QAAQ,GAAG,OAAO,CAAC,KAAK,IAAI,KAAK,CAAC,GAAG,CAAC,CAI5E;AAED,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,QAAQ,GAAG,OAAO,CAAC,KAAK,IAAI,UAAU,CAAC,QAAQ,CAAC,CAIvF;AAED,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,QAAQ,GAAG,OAAO,CAAC,KAAK,IAAI,eAAe,CAAC,aAAa,CAAC,CAItG;AAED,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,UAAU,CAAC,QAAQ,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,OAAO,UAAQ,GAAG,eAAe,CAU9G;AAED,wBAAgB,qBAAqB,CACpC,KAAK,EAAE,eAAe,CAAC,aAAa,CAAC,EACrC,KAAK,EAAE,OAAO,EACd,OAAO,UAAQ,GACb,gBAAgB,CAUlB"}
1
+ {"version":3,"file":"model-operations.d.ts","sourceRoot":"","sources":["../../src/utils/model-operations.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACX,QAAQ,EACR,GAAG,EACH,eAAe,EACf,aAAa,EACb,iBAAiB,EACjB,eAAe,EACf,gBAAgB,EAChB,QAAQ,EACR,UAAU,EACV,KAAK,EACL,SAAS,EACT,YAAY,EACZ,MAAM,aAAa,CAAC;AAGrB,kEAAkE;AAClE,wBAAgB,YAAY,CAAC,KAAK,EAAE,QAAQ,GAAG,SAAS,CAEvD;AAED,yFAAyF;AACzF,wBAAgB,WAAW,CAAC,KAAK,SAAS,SAAS,EAAE,KAAK,EAAE,QAAQ,EAAE,IAAI,EAAE,KAAK,GAAG,KAAK,IAAI,YAAY,CAAC,KAAK,CAAC,CAE/G;AAED,wBAAgB,eAAe,CAAC,KAAK,EAAE,QAAQ,GAAG,OAAO,CAAC,KAAK,IAAI,KAAK,CAAC,GAAG,CAAC,CAI5E;AAED,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,QAAQ,GAAG,OAAO,CAAC,KAAK,IAAI,UAAU,CAAC,QAAQ,CAAC,CAIvF;AAED,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,QAAQ,GAAG,OAAO,CAAC,KAAK,IAAI,eAAe,CAAC,aAAa,CAAC,CAItG;AAED,4FAA4F;AAC5F,wBAAgB,8BAA8B,CAC7C,KAAK,EAAE,eAAe,CAAC,aAAa,CAAC,EACrC,OAAO,EAAE,iBAAiB,GACxB,IAAI,CAIN;AAED,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,UAAU,CAAC,QAAQ,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,OAAO,UAAQ,GAAG,eAAe,CAU9G;AAED,wBAAgB,qBAAqB,CACpC,KAAK,EAAE,eAAe,CAAC,aAAa,CAAC,EACrC,KAAK,EAAE,OAAO,EACd,OAAO,UAAQ,GACb,gBAAgB,CAUlB"}
@@ -22,6 +22,12 @@ export function assertClassifierModel(model) {
22
22
  throw new ModelsError("provider", `Model ${model.provider}/${model.id} is not a classifier model`);
23
23
  }
24
24
  }
25
+ /** Rejects classifier images for models whose catalog entry does not accept image input. */
26
+ export function assertClassifierInputSupported(model, context) {
27
+ if (context.images?.length && !model.input.includes("image")) {
28
+ throw new ModelsError("provider", `Model ${model.provider}/${model.id} does not accept image input`);
29
+ }
30
+ }
25
31
  export function imageErrorResult(model, error, aborted = false) {
26
32
  return {
27
33
  api: model.api,
@@ -1 +1 @@
1
- {"version":3,"file":"model-operations.js","sourceRoot":"","sources":["../../src/utils/model-operations.ts"],"names":[],"mappings":"AAaA,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAEhD,kEAAkE;AAClE,MAAM,UAAU,YAAY,CAAC,KAAe;IAC3C,OAAO,KAAK,CAAC,IAAI,IAAI,MAAM,CAAC;AAC7B,CAAC;AAED,yFAAyF;AACzF,MAAM,UAAU,WAAW,CAA0B,KAAe,EAAE,IAAW;IAChF,OAAO,YAAY,CAAC,KAAK,CAAC,KAAK,IAAI,CAAC;AACrC,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,KAAe;IAC9C,IAAI,CAAC,WAAW,CAAC,KAAK,EAAE,MAAM,CAAC,EAAE,CAAC;QACjC,MAAM,IAAI,WAAW,CAAC,UAAU,EAAE,SAAS,KAAK,CAAC,QAAQ,IAAI,KAAK,CAAC,EAAE,sBAAsB,CAAC,CAAC;IAC9F,CAAC;AACF,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,KAAe;IAC/C,IAAI,CAAC,WAAW,CAAC,KAAK,EAAE,OAAO,CAAC,EAAE,CAAC;QAClC,MAAM,IAAI,WAAW,CAAC,UAAU,EAAE,SAAS,KAAK,CAAC,QAAQ,IAAI,KAAK,CAAC,EAAE,wBAAwB,CAAC,CAAC;IAChG,CAAC;AACF,CAAC;AAED,MAAM,UAAU,qBAAqB,CAAC,KAAe;IACpD,IAAI,CAAC,WAAW,CAAC,KAAK,EAAE,YAAY,CAAC,EAAE,CAAC;QACvC,MAAM,IAAI,WAAW,CAAC,UAAU,EAAE,SAAS,KAAK,CAAC,QAAQ,IAAI,KAAK,CAAC,EAAE,4BAA4B,CAAC,CAAC;IACpG,CAAC;AACF,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,KAA2B,EAAE,KAAc,EAAE,OAAO,GAAG,KAAK;IAC5F,OAAO;QACN,GAAG,EAAE,KAAK,CAAC,GAAG;QACd,QAAQ,EAAE,KAAK,CAAC,QAAQ;QACxB,KAAK,EAAE,KAAK,CAAC,EAAE;QACf,MAAM,EAAE,EAAE;QACV,UAAU,EAAE,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,OAAO;QACzC,YAAY,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC;QACpE,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE;KACrB,CAAC;AACH,CAAC;AAED,MAAM,UAAU,qBAAqB,CACpC,KAAqC,EACrC,KAAc,EACd,OAAO,GAAG,KAAK;IAEf,OAAO;QACN,GAAG,EAAE,KAAK,CAAC,GAAG;QACd,QAAQ,EAAE,KAAK,CAAC,QAAQ;QACxB,KAAK,EAAE,KAAK,CAAC,EAAE;QACf,OAAO,EAAE,EAAE;QACX,UAAU,EAAE,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,OAAO;QACzC,YAAY,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC;QACpE,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE;KACrB,CAAC;AACH,CAAC","sourcesContent":["import type {\n\tAnyModel,\n\tApi,\n\tAssistantImages,\n\tClassifierApi,\n\tClassifierModel,\n\tClassifierResult,\n\tImageApi,\n\tImageModel,\n\tModel,\n\tModelType,\n\tModelTypeMap,\n} from \"../types.ts\";\nimport { ModelsError } from \"./models-error.ts\";\n\n/** The type of a model. Models without `type` are chat models. */\nexport function getModelType(model: AnyModel): ModelType {\n\treturn model.type ?? \"chat\";\n}\n\n/** Runtime-checked model type narrowing, including legacy chat models without `type`. */\nexport function isModelType<TType extends ModelType>(model: AnyModel, type: TType): model is ModelTypeMap[TType] {\n\treturn getModelType(model) === type;\n}\n\nexport function assertChatModel(model: AnyModel): asserts model is Model<Api> {\n\tif (!isModelType(model, \"chat\")) {\n\t\tthrow new ModelsError(\"provider\", `Model ${model.provider}/${model.id} is not a chat model`);\n\t}\n}\n\nexport function assertImageModel(model: AnyModel): asserts model is ImageModel<ImageApi> {\n\tif (!isModelType(model, \"image\")) {\n\t\tthrow new ModelsError(\"provider\", `Model ${model.provider}/${model.id} is not an image model`);\n\t}\n}\n\nexport function assertClassifierModel(model: AnyModel): asserts model is ClassifierModel<ClassifierApi> {\n\tif (!isModelType(model, \"classifier\")) {\n\t\tthrow new ModelsError(\"provider\", `Model ${model.provider}/${model.id} is not a classifier model`);\n\t}\n}\n\nexport function imageErrorResult(model: ImageModel<ImageApi>, error: unknown, aborted = false): AssistantImages {\n\treturn {\n\t\tapi: model.api,\n\t\tprovider: model.provider,\n\t\tmodel: model.id,\n\t\toutput: [],\n\t\tstopReason: aborted ? \"aborted\" : \"error\",\n\t\terrorMessage: error instanceof Error ? error.message : String(error),\n\t\ttimestamp: Date.now(),\n\t};\n}\n\nexport function classifierErrorResult(\n\tmodel: ClassifierModel<ClassifierApi>,\n\terror: unknown,\n\taborted = false,\n): ClassifierResult {\n\treturn {\n\t\tapi: model.api,\n\t\tprovider: model.provider,\n\t\tmodel: model.id,\n\t\tanswers: {},\n\t\tstopReason: aborted ? \"aborted\" : \"error\",\n\t\terrorMessage: error instanceof Error ? error.message : String(error),\n\t\ttimestamp: Date.now(),\n\t};\n}\n"]}
1
+ {"version":3,"file":"model-operations.js","sourceRoot":"","sources":["../../src/utils/model-operations.ts"],"names":[],"mappings":"AAcA,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAEhD,kEAAkE;AAClE,MAAM,UAAU,YAAY,CAAC,KAAe;IAC3C,OAAO,KAAK,CAAC,IAAI,IAAI,MAAM,CAAC;AAC7B,CAAC;AAED,yFAAyF;AACzF,MAAM,UAAU,WAAW,CAA0B,KAAe,EAAE,IAAW;IAChF,OAAO,YAAY,CAAC,KAAK,CAAC,KAAK,IAAI,CAAC;AACrC,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,KAAe;IAC9C,IAAI,CAAC,WAAW,CAAC,KAAK,EAAE,MAAM,CAAC,EAAE,CAAC;QACjC,MAAM,IAAI,WAAW,CAAC,UAAU,EAAE,SAAS,KAAK,CAAC,QAAQ,IAAI,KAAK,CAAC,EAAE,sBAAsB,CAAC,CAAC;IAC9F,CAAC;AACF,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,KAAe;IAC/C,IAAI,CAAC,WAAW,CAAC,KAAK,EAAE,OAAO,CAAC,EAAE,CAAC;QAClC,MAAM,IAAI,WAAW,CAAC,UAAU,EAAE,SAAS,KAAK,CAAC,QAAQ,IAAI,KAAK,CAAC,EAAE,wBAAwB,CAAC,CAAC;IAChG,CAAC;AACF,CAAC;AAED,MAAM,UAAU,qBAAqB,CAAC,KAAe;IACpD,IAAI,CAAC,WAAW,CAAC,KAAK,EAAE,YAAY,CAAC,EAAE,CAAC;QACvC,MAAM,IAAI,WAAW,CAAC,UAAU,EAAE,SAAS,KAAK,CAAC,QAAQ,IAAI,KAAK,CAAC,EAAE,4BAA4B,CAAC,CAAC;IACpG,CAAC;AACF,CAAC;AAED,4FAA4F;AAC5F,MAAM,UAAU,8BAA8B,CAC7C,KAAqC,EACrC,OAA0B;IAE1B,IAAI,OAAO,CAAC,MAAM,EAAE,MAAM,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC;QAC9D,MAAM,IAAI,WAAW,CAAC,UAAU,EAAE,SAAS,KAAK,CAAC,QAAQ,IAAI,KAAK,CAAC,EAAE,8BAA8B,CAAC,CAAC;IACtG,CAAC;AACF,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,KAA2B,EAAE,KAAc,EAAE,OAAO,GAAG,KAAK;IAC5F,OAAO;QACN,GAAG,EAAE,KAAK,CAAC,GAAG;QACd,QAAQ,EAAE,KAAK,CAAC,QAAQ;QACxB,KAAK,EAAE,KAAK,CAAC,EAAE;QACf,MAAM,EAAE,EAAE;QACV,UAAU,EAAE,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,OAAO;QACzC,YAAY,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC;QACpE,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE;KACrB,CAAC;AACH,CAAC;AAED,MAAM,UAAU,qBAAqB,CACpC,KAAqC,EACrC,KAAc,EACd,OAAO,GAAG,KAAK;IAEf,OAAO;QACN,GAAG,EAAE,KAAK,CAAC,GAAG;QACd,QAAQ,EAAE,KAAK,CAAC,QAAQ;QACxB,KAAK,EAAE,KAAK,CAAC,EAAE;QACf,OAAO,EAAE,EAAE;QACX,UAAU,EAAE,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,OAAO;QACzC,YAAY,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC;QACpE,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE;KACrB,CAAC;AACH,CAAC","sourcesContent":["import type {\n\tAnyModel,\n\tApi,\n\tAssistantImages,\n\tClassifierApi,\n\tClassifierContext,\n\tClassifierModel,\n\tClassifierResult,\n\tImageApi,\n\tImageModel,\n\tModel,\n\tModelType,\n\tModelTypeMap,\n} from \"../types.ts\";\nimport { ModelsError } from \"./models-error.ts\";\n\n/** The type of a model. Models without `type` are chat models. */\nexport function getModelType(model: AnyModel): ModelType {\n\treturn model.type ?? \"chat\";\n}\n\n/** Runtime-checked model type narrowing, including legacy chat models without `type`. */\nexport function isModelType<TType extends ModelType>(model: AnyModel, type: TType): model is ModelTypeMap[TType] {\n\treturn getModelType(model) === type;\n}\n\nexport function assertChatModel(model: AnyModel): asserts model is Model<Api> {\n\tif (!isModelType(model, \"chat\")) {\n\t\tthrow new ModelsError(\"provider\", `Model ${model.provider}/${model.id} is not a chat model`);\n\t}\n}\n\nexport function assertImageModel(model: AnyModel): asserts model is ImageModel<ImageApi> {\n\tif (!isModelType(model, \"image\")) {\n\t\tthrow new ModelsError(\"provider\", `Model ${model.provider}/${model.id} is not an image model`);\n\t}\n}\n\nexport function assertClassifierModel(model: AnyModel): asserts model is ClassifierModel<ClassifierApi> {\n\tif (!isModelType(model, \"classifier\")) {\n\t\tthrow new ModelsError(\"provider\", `Model ${model.provider}/${model.id} is not a classifier model`);\n\t}\n}\n\n/** Rejects classifier images for models whose catalog entry does not accept image input. */\nexport function assertClassifierInputSupported(\n\tmodel: ClassifierModel<ClassifierApi>,\n\tcontext: ClassifierContext,\n): void {\n\tif (context.images?.length && !model.input.includes(\"image\")) {\n\t\tthrow new ModelsError(\"provider\", `Model ${model.provider}/${model.id} does not accept image input`);\n\t}\n}\n\nexport function imageErrorResult(model: ImageModel<ImageApi>, error: unknown, aborted = false): AssistantImages {\n\treturn {\n\t\tapi: model.api,\n\t\tprovider: model.provider,\n\t\tmodel: model.id,\n\t\toutput: [],\n\t\tstopReason: aborted ? \"aborted\" : \"error\",\n\t\terrorMessage: error instanceof Error ? error.message : String(error),\n\t\ttimestamp: Date.now(),\n\t};\n}\n\nexport function classifierErrorResult(\n\tmodel: ClassifierModel<ClassifierApi>,\n\terror: unknown,\n\taborted = false,\n): ClassifierResult {\n\treturn {\n\t\tapi: model.api,\n\t\tprovider: model.provider,\n\t\tmodel: model.id,\n\t\tanswers: {},\n\t\tstopReason: aborted ? \"aborted\" : \"error\",\n\t\terrorMessage: error instanceof Error ? error.message : String(error),\n\t\ttimestamp: Date.now(),\n\t};\n}\n"]}
@@ -18,6 +18,7 @@ import type { AssistantMessage } from "../types.ts";
18
18
  * - Google Gemini: "input token count exceeds the maximum"
19
19
  * - xAI (Grok): "maximum prompt length is X but request contains Y"
20
20
  * - Groq: "reduce the length of the messages"
21
+ * - GitHub Copilot: HTTP 413 "failed to parse request" (request byte-size overflow)
21
22
  * - Cerebras: 400/413 status code (no body)
22
23
  * - Mistral: "Prompt contains X tokens ... too large for model with Y maximum context length"
23
24
  * - OpenRouter (most backends): "maximum context length is X tokens"
@@ -1 +1 @@
1
- {"version":3,"file":"overflow.d.ts","sourceRoot":"","sources":["../../src/utils/overflow.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAiFpD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsDG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,gBAAgB,EAAE,aAAa,CAAC,EAAE,MAAM,GAAG,OAAO,CAkC5F;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,GAAG,OAAO,CAEhG;AAED;;GAEG;AACH,wBAAgB,mBAAmB,IAAI,MAAM,EAAE,CAE9C"}
1
+ {"version":3,"file":"overflow.d.ts","sourceRoot":"","sources":["../../src/utils/overflow.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAqFpD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuDG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,gBAAgB,EAAE,aAAa,CAAC,EAAE,MAAM,GAAG,OAAO,CAkC5F;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,GAAG,OAAO,CAEhG;AAED;;GAEG;AACH,wBAAgB,mBAAmB,IAAI,MAAM,EAAE,CAE9C"}
@@ -2,7 +2,7 @@
2
2
  * Regex patterns to detect context overflow errors from different providers.
3
3
  *
4
4
  * These patterns match error messages returned when the input exceeds
5
- * the model's context window.
5
+ * the model's context window or the provider's request byte-size limit.
6
6
  *
7
7
  * Provider-specific patterns (with example error messages):
8
8
  *
@@ -20,6 +20,7 @@
20
20
  * - llama.cpp: "the request exceeds the available context size, try increasing it"
21
21
  * - LM Studio: "tokens to keep from the initial prompt is greater than the context length"
22
22
  * - GitHub Copilot: "prompt token count of X exceeds the limit of Y"
23
+ * - GitHub Copilot: 'github-copilot API error (413): {"message":"failed to parse request","code":""}'
23
24
  * - MiniMax: "invalid params, context window exceeds limit"
24
25
  * - Kimi For Coding: "Your request exceeded model token limit: X (requested: Y)"
25
26
  * - DS4: "Prompt has X tokens, but the configured context size is Y tokens"
@@ -58,6 +59,9 @@ const OVERFLOW_PATTERNS = [
58
59
  /context[_ ]length[_ ]exceeded/i, // Generic fallback
59
60
  /too many tokens/i, // Generic fallback
60
61
  /token limit exceeded/i, // Generic fallback
62
+ // HTTP 413 means request-size overflow regardless of provider or body text.
63
+ // Match raw SDK errors and formatProviderError's prefix, not a status mentioned in the body.
64
+ /^(?:[\w -]+ API error \(413\):|413(?:\s|:|$))/i,
61
65
  ];
62
66
  const CEREBRAS_BODYLESS_OVERFLOW_PATTERN = /^4(?:00|13)\s*(?:status code)?\s*\(no body\)/i;
63
67
  /**
@@ -93,6 +97,7 @@ const NON_OVERFLOW_PATTERNS = [
93
97
  * - Google Gemini: "input token count exceeds the maximum"
94
98
  * - xAI (Grok): "maximum prompt length is X but request contains Y"
95
99
  * - Groq: "reduce the length of the messages"
100
+ * - GitHub Copilot: HTTP 413 "failed to parse request" (request byte-size overflow)
96
101
  * - Cerebras: 400/413 status code (no body)
97
102
  * - Mistral: "Prompt contains X tokens ... too large for model with Y maximum context length"
98
103
  * - OpenRouter (most backends): "maximum context length is X tokens"
@@ -1 +1 @@
1
- {"version":3,"file":"overflow.js","sourceRoot":"","sources":["../../src/utils/overflow.ts"],"names":[],"mappings":"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","sourcesContent":["import type { AssistantMessage } from \"../types.ts\";\n\n/**\n * Regex patterns to detect context overflow errors from different providers.\n *\n * These patterns match error messages returned when the input exceeds\n * the model's context window.\n *\n * Provider-specific patterns (with example error messages):\n *\n * - Anthropic: \"prompt is too long: 213462 tokens > 200000 maximum\"\n * - Anthropic: \"413 {\\\"error\\\":{\\\"type\\\":\\\"request_too_large\\\",\\\"message\\\":\\\"Request exceeds the maximum size\\\"}}\"\n * - OpenAI: \"Your input exceeds the context window of this model\"\n * - OpenAI/LiteLLM: \"Requested token count exceeds the model's maximum context length of 131072 tokens\"\n * - OpenAI-compatible: \"Input length (265330) exceeds model's maximum context length (262144).\"\n * - Google: \"The input token count (1196265) exceeds the maximum number of tokens allowed (1048575)\"\n * - xAI: \"This model's maximum prompt length is 131072 but the request contains 537812 tokens\"\n * - Groq: \"Please reduce the length of the messages or completion\"\n * - OpenRouter: \"This endpoint's maximum context length is X tokens. However, you requested about Y tokens\"\n * - OpenRouter/Poolside: \"Input length X exceeds the maximum allowed input length of Y tokens.\"\n * - Together AI: \"The input (X tokens) is longer than the model's context length (Y tokens).\"\n * - llama.cpp: \"the request exceeds the available context size, try increasing it\"\n * - LM Studio: \"tokens to keep from the initial prompt is greater than the context length\"\n * - GitHub Copilot: \"prompt token count of X exceeds the limit of Y\"\n * - MiniMax: \"invalid params, context window exceeds limit\"\n * - Kimi For Coding: \"Your request exceeded model token limit: X (requested: Y)\"\n * - DS4: \"Prompt has X tokens, but the configured context size is Y tokens\"\n * - Cerebras: \"400/413 status code (no body)\"\n * - Mistral: \"Prompt contains X tokens ... too large for model with Y maximum context length\"\n * - z.ai: `{\"code\":\"1261\",\"message\":\"Prompt too long\"}`, `{\"code\":\"1261\",\"message\":\"Prompt exceeds max length\"}` (CN endpoint), or silent overflow via usage.input > contextWindow\n * - Xiaomi MiMo: Truncates input to fill contextWindow exactly, then returns finish_reason \"length\"\n * with output=0 (no room left to generate). Detected via stopReason \"length\" + zero output +\n * input filling the context window.\n * - DashScope/Qwen: \"Range of input length should be [1, X]\" (HTTP 400 invalid_parameter_error)\n * - Ollama: Some deployments truncate silently, others return errors like \"prompt too long; exceeded max context length by X tokens\"\n */\nconst OVERFLOW_PATTERNS = [\n\t/prompt (?:is )?too long/i, // Anthropic and z.ai token overflow\n\t/prompt exceeds max length/i, // z.ai CN endpoint token overflow\n\t/request_too_large/i, // Anthropic request byte-size overflow (HTTP 413)\n\t/input is too long for requested model/i, // Amazon Bedrock\n\t/exceeds the context window/i, // OpenAI (Completions & Responses API)\n\t/exceeds (?:the )?(?:model'?s )?maximum context length(?: of [\\d,]+ tokens?|\\s*\\([\\d,]+\\))/i, // OpenAI-compatible proxies (LiteLLM)\n\t/input token count.*exceeds the maximum/i, // Google (Gemini)\n\t/maximum prompt length is \\d+/i, // xAI (Grok)\n\t/reduce the length of the messages/i, // Groq\n\t/maximum context length is \\d+ tokens/i, // OpenRouter (most backends)\n\t/exceeds (?:the )?maximum allowed input length of [\\d,]+ tokens?/i, // OpenRouter/Poolside\n\t/input \\(\\d+ tokens\\) is longer than the model'?s context length \\(\\d+ tokens\\)/i, // Together AI\n\t/exceeds the limit of \\d+/i, // GitHub Copilot\n\t/exceeds the available context size/i, // llama.cpp server\n\t/greater than the context length/i, // LM Studio\n\t/context window exceeds limit/i, // MiniMax\n\t/exceeded model token limit/i, // Kimi For Coding\n\t/too large for model with \\d+ maximum context length/i, // Mistral\n\t/prompt has [\\d,]+ tokens?, but the configured context size is [\\d,]+ tokens?/i, // DS4 server\n\t/model_context_window_exceeded/i, // z.ai non-standard finish_reason surfaced as error text\n\t/prompt too long; exceeded (?:max )?context length/i, // Ollama explicit overflow error\n\t/range of input length should be/i, // DashScope / Qwen Token Plan\n\t/context[_ ]length[_ ]exceeded/i, // Generic fallback\n\t/too many tokens/i, // Generic fallback\n\t/token limit exceeded/i, // Generic fallback\n];\n\nconst CEREBRAS_BODYLESS_OVERFLOW_PATTERN = /^4(?:00|13)\\s*(?:status code)?\\s*\\(no body\\)/i;\n\n/**\n * Patterns that indicate non-overflow errors (e.g. rate limiting, server errors).\n * Error messages matching any of these are excluded from overflow detection\n * even if they also match an OVERFLOW_PATTERN.\n *\n * Example: Bedrock formats throttling errors as \"ThrottlingException: Too many tokens,\n * please wait before trying again.\" which would match the /too many tokens/i overflow\n * pattern without this exclusion.\n */\nconst NON_OVERFLOW_PATTERNS = [\n\t/^(Throttling error|Service unavailable):/i, // AWS Bedrock non-overflow errors (human-readable prefixes from formatBedrockError)\n\t/rate limit/i, // Generic rate limiting\n\t/too many requests/i, // Generic HTTP 429 style\n];\n\n/**\n * Check if an assistant message represents a context overflow error.\n *\n * This handles three cases:\n * 1. Error-based overflow: Most providers return stopReason \"error\" with a\n * specific error message pattern.\n * 2. Silent overflow: Some providers accept overflow requests and return\n * successfully. For these, we check if usage.input exceeds the context window.\n * 3. Length-stop overflow: Xiaomi MiMo can return \"length\" with zero output when\n * the input fills the context window.\n *\n * ## Reliability by Provider\n *\n * **Reliable detection (returns error with detectable message):**\n * - Anthropic: \"prompt is too long: X tokens > Y maximum\" or \"request_too_large\"\n * - OpenAI (Completions & Responses): \"exceeds the context window\", \"exceeds the model's maximum context length of X tokens\", or \"exceeds model's maximum context length (X)\"\n * - Google Gemini: \"input token count exceeds the maximum\"\n * - xAI (Grok): \"maximum prompt length is X but request contains Y\"\n * - Groq: \"reduce the length of the messages\"\n * - Cerebras: 400/413 status code (no body)\n * - Mistral: \"Prompt contains X tokens ... too large for model with Y maximum context length\"\n * - OpenRouter (most backends): \"maximum context length is X tokens\"\n * - OpenRouter/Poolside: \"Input length X exceeds the maximum allowed input length of Y tokens.\"\n * - Together AI: \"The input (X tokens) is longer than the model's context length (Y tokens).\"\n * - llama.cpp: \"exceeds the available context size\"\n * - LM Studio: \"greater than the context length\"\n * - Kimi For Coding: \"exceeded model token limit: X (requested: Y)\"\n * - DS4: \"Prompt has X tokens, but the configured context size is Y tokens\"\n * - DashScope/Qwen: \"Range of input length should be [1, X]\"\n * - z.ai: \"Prompt too long\"\n *\n * **Unreliable detection:**\n * - z.ai: Sometimes accepts overflow silently (detectable via usage.input > contextWindow),\n * sometimes returns rate limit errors. Pass contextWindow param to detect silent overflow.\n * - Xiaomi MiMo: Truncates input to fit contextWindow then returns stopReason \"length\" with\n * output=0. Pass contextWindow param to detect via the \"filled context + zero output\" signal.\n * - Ollama: May truncate input silently for some setups, but may also return explicit\n * overflow errors that match the patterns above. Silent truncation still cannot be\n * detected here because we do not know the expected token count.\n *\n * ## Custom Providers\n *\n * If you've added custom models via settings.json, this function may not detect\n * overflow errors from those providers. To add support:\n *\n * 1. Send a request that exceeds the model's context window\n * 2. Check the errorMessage in the response\n * 3. Create a regex pattern that matches the error\n * 4. The pattern should be added to OVERFLOW_PATTERNS in this file, or\n * check the errorMessage yourself before calling this function\n *\n * @param message - The assistant message to check\n * @param contextWindow - Optional context window size for detecting silent overflow (z.ai)\n * @returns true if the message indicates a context overflow\n */\nexport function isContextOverflow(message: AssistantMessage, contextWindow?: number): boolean {\n\t// Case 1: Check error message patterns\n\tif (message.stopReason === \"error\" && message.errorMessage) {\n\t\t// Skip messages matching known non-overflow patterns (e.g. throttling / rate-limit)\n\t\tconst isNonOverflow = NON_OVERFLOW_PATTERNS.some((p) => p.test(message.errorMessage!));\n\t\tif (!isNonOverflow) {\n\t\t\tif (OVERFLOW_PATTERNS.some((p) => p.test(message.errorMessage!))) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\tif (message.provider === \"cerebras\" && CEREBRAS_BODYLESS_OVERFLOW_PATTERN.test(message.errorMessage)) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t}\n\n\t// Case 2: Silent overflow (z.ai style) - successful but usage exceeds context\n\tif (contextWindow && message.stopReason === \"stop\") {\n\t\tconst inputTokens = message.usage.input + message.usage.cacheRead;\n\t\tif (inputTokens > contextWindow) {\n\t\t\treturn true;\n\t\t}\n\t}\n\n\t// Case 3: Length-stop overflow (Xiaomi MiMo style) - server truncates oversized input\n\t// to fit the context window, leaving no room for output. Returns stopReason \"length\"\n\t// with output=0 and input+cacheRead filling the context window.\n\tif (contextWindow && message.stopReason === \"length\" && message.usage.output === 0) {\n\t\tconst inputTokens = message.usage.input + message.usage.cacheRead;\n\t\tif (inputTokens >= contextWindow * 0.99) {\n\t\t\treturn true;\n\t\t}\n\t}\n\n\treturn false;\n}\n\n/**\n * Check whether a length stop ended below the caller or model's intended output limit.\n * Such responses may be caused by context pressure or provider-side truncation, so callers\n * can make one bounded compact-and-retry attempt. `desiredMaxOutput` must be the original\n * limit before any context-based clamping.\n */\nexport function isRecoverableLength(message: AssistantMessage, desiredMaxOutput: number): boolean {\n\treturn message.stopReason === \"length\" && desiredMaxOutput > 0 && message.usage.output < desiredMaxOutput;\n}\n\n/**\n * Get the overflow patterns for testing purposes.\n */\nexport function getOverflowPatterns(): RegExp[] {\n\treturn [...OVERFLOW_PATTERNS];\n}\n"]}
1
+ {"version":3,"file":"overflow.js","sourceRoot":"","sources":["../../src/utils/overflow.ts"],"names":[],"mappings":"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","sourcesContent":["import type { AssistantMessage } from \"../types.ts\";\n\n/**\n * Regex patterns to detect context overflow errors from different providers.\n *\n * These patterns match error messages returned when the input exceeds\n * the model's context window or the provider's request byte-size limit.\n *\n * Provider-specific patterns (with example error messages):\n *\n * - Anthropic: \"prompt is too long: 213462 tokens > 200000 maximum\"\n * - Anthropic: \"413 {\\\"error\\\":{\\\"type\\\":\\\"request_too_large\\\",\\\"message\\\":\\\"Request exceeds the maximum size\\\"}}\"\n * - OpenAI: \"Your input exceeds the context window of this model\"\n * - OpenAI/LiteLLM: \"Requested token count exceeds the model's maximum context length of 131072 tokens\"\n * - OpenAI-compatible: \"Input length (265330) exceeds model's maximum context length (262144).\"\n * - Google: \"The input token count (1196265) exceeds the maximum number of tokens allowed (1048575)\"\n * - xAI: \"This model's maximum prompt length is 131072 but the request contains 537812 tokens\"\n * - Groq: \"Please reduce the length of the messages or completion\"\n * - OpenRouter: \"This endpoint's maximum context length is X tokens. However, you requested about Y tokens\"\n * - OpenRouter/Poolside: \"Input length X exceeds the maximum allowed input length of Y tokens.\"\n * - Together AI: \"The input (X tokens) is longer than the model's context length (Y tokens).\"\n * - llama.cpp: \"the request exceeds the available context size, try increasing it\"\n * - LM Studio: \"tokens to keep from the initial prompt is greater than the context length\"\n * - GitHub Copilot: \"prompt token count of X exceeds the limit of Y\"\n * - GitHub Copilot: 'github-copilot API error (413): {\"message\":\"failed to parse request\",\"code\":\"\"}'\n * - MiniMax: \"invalid params, context window exceeds limit\"\n * - Kimi For Coding: \"Your request exceeded model token limit: X (requested: Y)\"\n * - DS4: \"Prompt has X tokens, but the configured context size is Y tokens\"\n * - Cerebras: \"400/413 status code (no body)\"\n * - Mistral: \"Prompt contains X tokens ... too large for model with Y maximum context length\"\n * - z.ai: `{\"code\":\"1261\",\"message\":\"Prompt too long\"}`, `{\"code\":\"1261\",\"message\":\"Prompt exceeds max length\"}` (CN endpoint), or silent overflow via usage.input > contextWindow\n * - Xiaomi MiMo: Truncates input to fill contextWindow exactly, then returns finish_reason \"length\"\n * with output=0 (no room left to generate). Detected via stopReason \"length\" + zero output +\n * input filling the context window.\n * - DashScope/Qwen: \"Range of input length should be [1, X]\" (HTTP 400 invalid_parameter_error)\n * - Ollama: Some deployments truncate silently, others return errors like \"prompt too long; exceeded max context length by X tokens\"\n */\nconst OVERFLOW_PATTERNS = [\n\t/prompt (?:is )?too long/i, // Anthropic and z.ai token overflow\n\t/prompt exceeds max length/i, // z.ai CN endpoint token overflow\n\t/request_too_large/i, // Anthropic request byte-size overflow (HTTP 413)\n\t/input is too long for requested model/i, // Amazon Bedrock\n\t/exceeds the context window/i, // OpenAI (Completions & Responses API)\n\t/exceeds (?:the )?(?:model'?s )?maximum context length(?: of [\\d,]+ tokens?|\\s*\\([\\d,]+\\))/i, // OpenAI-compatible proxies (LiteLLM)\n\t/input token count.*exceeds the maximum/i, // Google (Gemini)\n\t/maximum prompt length is \\d+/i, // xAI (Grok)\n\t/reduce the length of the messages/i, // Groq\n\t/maximum context length is \\d+ tokens/i, // OpenRouter (most backends)\n\t/exceeds (?:the )?maximum allowed input length of [\\d,]+ tokens?/i, // OpenRouter/Poolside\n\t/input \\(\\d+ tokens\\) is longer than the model'?s context length \\(\\d+ tokens\\)/i, // Together AI\n\t/exceeds the limit of \\d+/i, // GitHub Copilot\n\t/exceeds the available context size/i, // llama.cpp server\n\t/greater than the context length/i, // LM Studio\n\t/context window exceeds limit/i, // MiniMax\n\t/exceeded model token limit/i, // Kimi For Coding\n\t/too large for model with \\d+ maximum context length/i, // Mistral\n\t/prompt has [\\d,]+ tokens?, but the configured context size is [\\d,]+ tokens?/i, // DS4 server\n\t/model_context_window_exceeded/i, // z.ai non-standard finish_reason surfaced as error text\n\t/prompt too long; exceeded (?:max )?context length/i, // Ollama explicit overflow error\n\t/range of input length should be/i, // DashScope / Qwen Token Plan\n\t/context[_ ]length[_ ]exceeded/i, // Generic fallback\n\t/too many tokens/i, // Generic fallback\n\t/token limit exceeded/i, // Generic fallback\n\t// HTTP 413 means request-size overflow regardless of provider or body text.\n\t// Match raw SDK errors and formatProviderError's prefix, not a status mentioned in the body.\n\t/^(?:[\\w -]+ API error \\(413\\):|413(?:\\s|:|$))/i,\n];\n\nconst CEREBRAS_BODYLESS_OVERFLOW_PATTERN = /^4(?:00|13)\\s*(?:status code)?\\s*\\(no body\\)/i;\n\n/**\n * Patterns that indicate non-overflow errors (e.g. rate limiting, server errors).\n * Error messages matching any of these are excluded from overflow detection\n * even if they also match an OVERFLOW_PATTERN.\n *\n * Example: Bedrock formats throttling errors as \"ThrottlingException: Too many tokens,\n * please wait before trying again.\" which would match the /too many tokens/i overflow\n * pattern without this exclusion.\n */\nconst NON_OVERFLOW_PATTERNS = [\n\t/^(Throttling error|Service unavailable):/i, // AWS Bedrock non-overflow errors (human-readable prefixes from formatBedrockError)\n\t/rate limit/i, // Generic rate limiting\n\t/too many requests/i, // Generic HTTP 429 style\n];\n\n/**\n * Check if an assistant message represents a context overflow error.\n *\n * This handles three cases:\n * 1. Error-based overflow: Most providers return stopReason \"error\" with a\n * specific error message pattern.\n * 2. Silent overflow: Some providers accept overflow requests and return\n * successfully. For these, we check if usage.input exceeds the context window.\n * 3. Length-stop overflow: Xiaomi MiMo can return \"length\" with zero output when\n * the input fills the context window.\n *\n * ## Reliability by Provider\n *\n * **Reliable detection (returns error with detectable message):**\n * - Anthropic: \"prompt is too long: X tokens > Y maximum\" or \"request_too_large\"\n * - OpenAI (Completions & Responses): \"exceeds the context window\", \"exceeds the model's maximum context length of X tokens\", or \"exceeds model's maximum context length (X)\"\n * - Google Gemini: \"input token count exceeds the maximum\"\n * - xAI (Grok): \"maximum prompt length is X but request contains Y\"\n * - Groq: \"reduce the length of the messages\"\n * - GitHub Copilot: HTTP 413 \"failed to parse request\" (request byte-size overflow)\n * - Cerebras: 400/413 status code (no body)\n * - Mistral: \"Prompt contains X tokens ... too large for model with Y maximum context length\"\n * - OpenRouter (most backends): \"maximum context length is X tokens\"\n * - OpenRouter/Poolside: \"Input length X exceeds the maximum allowed input length of Y tokens.\"\n * - Together AI: \"The input (X tokens) is longer than the model's context length (Y tokens).\"\n * - llama.cpp: \"exceeds the available context size\"\n * - LM Studio: \"greater than the context length\"\n * - Kimi For Coding: \"exceeded model token limit: X (requested: Y)\"\n * - DS4: \"Prompt has X tokens, but the configured context size is Y tokens\"\n * - DashScope/Qwen: \"Range of input length should be [1, X]\"\n * - z.ai: \"Prompt too long\"\n *\n * **Unreliable detection:**\n * - z.ai: Sometimes accepts overflow silently (detectable via usage.input > contextWindow),\n * sometimes returns rate limit errors. Pass contextWindow param to detect silent overflow.\n * - Xiaomi MiMo: Truncates input to fit contextWindow then returns stopReason \"length\" with\n * output=0. Pass contextWindow param to detect via the \"filled context + zero output\" signal.\n * - Ollama: May truncate input silently for some setups, but may also return explicit\n * overflow errors that match the patterns above. Silent truncation still cannot be\n * detected here because we do not know the expected token count.\n *\n * ## Custom Providers\n *\n * If you've added custom models via settings.json, this function may not detect\n * overflow errors from those providers. To add support:\n *\n * 1. Send a request that exceeds the model's context window\n * 2. Check the errorMessage in the response\n * 3. Create a regex pattern that matches the error\n * 4. The pattern should be added to OVERFLOW_PATTERNS in this file, or\n * check the errorMessage yourself before calling this function\n *\n * @param message - The assistant message to check\n * @param contextWindow - Optional context window size for detecting silent overflow (z.ai)\n * @returns true if the message indicates a context overflow\n */\nexport function isContextOverflow(message: AssistantMessage, contextWindow?: number): boolean {\n\t// Case 1: Check error message patterns\n\tif (message.stopReason === \"error\" && message.errorMessage) {\n\t\t// Skip messages matching known non-overflow patterns (e.g. throttling / rate-limit)\n\t\tconst isNonOverflow = NON_OVERFLOW_PATTERNS.some((p) => p.test(message.errorMessage!));\n\t\tif (!isNonOverflow) {\n\t\t\tif (OVERFLOW_PATTERNS.some((p) => p.test(message.errorMessage!))) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\tif (message.provider === \"cerebras\" && CEREBRAS_BODYLESS_OVERFLOW_PATTERN.test(message.errorMessage)) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t}\n\n\t// Case 2: Silent overflow (z.ai style) - successful but usage exceeds context\n\tif (contextWindow && message.stopReason === \"stop\") {\n\t\tconst inputTokens = message.usage.input + message.usage.cacheRead;\n\t\tif (inputTokens > contextWindow) {\n\t\t\treturn true;\n\t\t}\n\t}\n\n\t// Case 3: Length-stop overflow (Xiaomi MiMo style) - server truncates oversized input\n\t// to fit the context window, leaving no room for output. Returns stopReason \"length\"\n\t// with output=0 and input+cacheRead filling the context window.\n\tif (contextWindow && message.stopReason === \"length\" && message.usage.output === 0) {\n\t\tconst inputTokens = message.usage.input + message.usage.cacheRead;\n\t\tif (inputTokens >= contextWindow * 0.99) {\n\t\t\treturn true;\n\t\t}\n\t}\n\n\treturn false;\n}\n\n/**\n * Check whether a length stop ended below the caller or model's intended output limit.\n * Such responses may be caused by context pressure or provider-side truncation, so callers\n * can make one bounded compact-and-retry attempt. `desiredMaxOutput` must be the original\n * limit before any context-based clamping.\n */\nexport function isRecoverableLength(message: AssistantMessage, desiredMaxOutput: number): boolean {\n\treturn message.stopReason === \"length\" && desiredMaxOutput > 0 && message.usage.output < desiredMaxOutput;\n}\n\n/**\n * Get the overflow patterns for testing purposes.\n */\nexport function getOverflowPatterns(): RegExp[] {\n\treturn [...OVERFLOW_PATTERNS];\n}\n"]}
@@ -2,6 +2,8 @@ interface ProviderRetryOptions {
2
2
  maxRetries?: number;
3
3
  maxRetryDelayMs?: number;
4
4
  signal?: AbortSignal;
5
+ /** HTTP statuses that fail at once although the default policy would retry them. */
6
+ noRetryStatuses?: readonly number[];
5
7
  }
6
8
  /**
7
9
  * Reproduce the retry behavior used by the OpenAI and Anthropic SDKs while making
@@ -1 +1 @@
1
- {"version":3,"file":"provider-retry.d.ts","sourceRoot":"","sources":["../../src/utils/provider-retry.ts"],"names":[],"mappings":"AAEA,UAAU,oBAAoB;IAC7B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,MAAM,CAAC,EAAE,WAAW,CAAC;CACrB;AA0FD;;;;;;;GAOG;AACH,wBAAsB,oBAAoB,CAAC,CAAC,EAC3C,OAAO,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,EACzB,OAAO,GAAE,oBAAyB,GAChC,OAAO,CAAC,CAAC,CAAC,CAiBZ"}
1
+ {"version":3,"file":"provider-retry.d.ts","sourceRoot":"","sources":["../../src/utils/provider-retry.ts"],"names":[],"mappings":"AAEA,UAAU,oBAAoB;IAC7B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,oFAAoF;IACpF,eAAe,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CACpC;AA0FD;;;;;;;GAOG;AACH,wBAAsB,oBAAoB,CAAC,CAAC,EAC3C,OAAO,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,EACzB,OAAO,GAAE,oBAAyB,GAChC,OAAO,CAAC,CAAC,CAAC,CAkBZ"}
@@ -86,6 +86,8 @@ export async function retryProviderRequest(request, options = {}) {
86
86
  throw createAbortError();
87
87
  if (retriesRemaining <= 0 || !isProviderError(error) || !isRetryableProviderError(error))
88
88
  throw error;
89
+ if (error.status !== undefined && options.noRetryStatuses?.includes(error.status))
90
+ throw error;
89
91
  const retryIndex = maxRetries - retriesRemaining;
90
92
  retriesRemaining--;
91
93
  await abortableSleep(getRetryDelayMs(error, retryIndex, options.maxRetryDelayMs), options.signal);
@@ -1 +1 @@
1
- {"version":3,"file":"provider-retry.js","sourceRoot":"","sources":["../../src/utils/provider-retry.ts"],"names":[],"mappings":"AAAA,MAAM,0BAA0B,GAAG,MAAM,CAAC;AAa1C,SAAS,eAAe,CAAC,KAAc;IACtC,IAAI,CAAC,CAAC,KAAK,YAAY,KAAK,CAAC,IAAI,CAAC,CAAC,QAAQ,IAAI,KAAK,CAAC,IAAI,CAAC,CAAC,SAAS,IAAI,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC;IAC7F,OAAO,CACN,CAAC,KAAK,CAAC,MAAM,KAAK,SAAS,IAAI,OAAO,KAAK,CAAC,MAAM,KAAK,QAAQ,CAAC;QAChE,CAAC,KAAK,CAAC,OAAO,KAAK,SAAS,IAAI,KAAK,CAAC,OAAO,YAAY,OAAO,CAAC,CACjE,CAAC;AACH,CAAC;AAED,gGAAgG;AAChG,SAAS,wBAAwB,CAAC,KAAoB;IACrD,MAAM,WAAW,GAAG,KAAK,CAAC,OAAO,EAAE,GAAG,CAAC,gBAAgB,CAAC,CAAC;IACzD,IAAI,WAAW,KAAK,MAAM;QAAE,OAAO,IAAI,CAAC;IACxC,IAAI,WAAW,KAAK,OAAO;QAAE,OAAO,KAAK,CAAC;IAE1C,IAAI,KAAK,CAAC,MAAM,KAAK,SAAS;QAAE,OAAO,IAAI,CAAC;IAC5C,OAAO,CACN,KAAK,CAAC,MAAM,KAAK,GAAG;QACpB,KAAK,CAAC,MAAM,KAAK,GAAG;QACpB,KAAK,CAAC,MAAM,KAAK,GAAG;QACpB,CAAC,OAAO,KAAK,CAAC,MAAM,KAAK,QAAQ,IAAI,KAAK,CAAC,MAAM,IAAI,GAAG,CAAC,CACzD,CAAC;AACH,CAAC;AAED,SAAS,0BAA0B,CAClC,OAAe,EACf,eAAmC,EACnC,oBAA4B;IAE5B,MAAM,UAAU,GAAG,eAAe,IAAI,0BAA0B,CAAC;IACjE,IAAI,UAAU,GAAG,CAAC,IAAI,OAAO,GAAG,UAAU,EAAE,CAAC;QAC5C,MAAM,IAAI,KAAK,CACd,oBAAoB,IAAI,CAAC,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC,uBAAuB,IAAI,CAAC,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC,OAAO,oBAAoB,EAAE,CAC7H,CAAC;IACH,CAAC;IACD,OAAO,OAAO,CAAC;AAChB,CAAC;AAED,SAAS,eAAe,CAAC,KAAoB,EAAE,UAAkB,EAAE,eAAmC;IACrG,MAAM,YAAY,GAAG,KAAK,CAAC,OAAO,EAAE,GAAG,CAAC,gBAAgB,CAAC,CAAC;IAC1D,IAAI,YAAY,EAAE,CAAC;QAClB,MAAM,KAAK,GAAG,MAAM,CAAC,UAAU,CAAC,YAAY,CAAC,CAAC;QAC9C,IAAI,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC;YAAE,OAAO,0BAA0B,CAAC,KAAK,EAAE,eAAe,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC;IACtG,CAAC;IAED,MAAM,UAAU,GAAG,KAAK,CAAC,OAAO,EAAE,GAAG,CAAC,aAAa,CAAC,CAAC;IACrD,IAAI,UAAU,EAAE,CAAC;QAChB,MAAM,OAAO,GAAG,MAAM,CAAC,UAAU,CAAC,UAAU,CAAC,CAAC;QAC9C,MAAM,OAAO,GAAG,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,OAAO,GAAG,IAAI,CAAC;QAC7F,IAAI,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC;YAAE,OAAO,0BAA0B,CAAC,OAAO,EAAE,eAAe,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC;IAC1G,CAAC;IAED,MAAM,gBAAgB,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,GAAG,CAAC,IAAI,UAAU,EAAE,CAAC,CAAC,GAAG,IAAI,CAAC;IACnE,OAAO,gBAAgB,GAAG,CAAC,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,GAAG,IAAI,CAAC,CAAC;AACtD,CAAC;AAED,SAAS,gBAAgB;IACxB,MAAM,KAAK,GAAG,IAAI,KAAK,CAAC,iBAAiB,CAAC,CAAC;IAC3C,KAAK,CAAC,IAAI,GAAG,YAAY,CAAC;IAC1B,OAAO,KAAK,CAAC;AACd,CAAC;AAED,SAAS,cAAc,CAAC,EAAU,EAAE,MAAoB;IACvD,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QACtC,IAAI,MAAM,EAAE,OAAO,EAAE,CAAC;YACrB,MAAM,CAAC,gBAAgB,EAAE,CAAC,CAAC;YAC3B,OAAO;QACR,CAAC;QAED,MAAM,OAAO,GAAG,GAAG,EAAE;YACpB,YAAY,CAAC,OAAO,CAAC,CAAC;YACtB,MAAM,CAAC,gBAAgB,EAAE,CAAC,CAAC;QAC5B,CAAC,CAAC;QACF,MAAM,OAAO,GAAG,UAAU,CACzB,GAAG,EAAE;YACJ,MAAM,EAAE,mBAAmB,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;YAC9C,OAAO,EAAE,CAAC;QACX,CAAC,EACD,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,CAAC,CACf,CAAC;QACF,MAAM,EAAE,gBAAgB,CAAC,OAAO,EAAE,OAAO,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;IAC5D,CAAC,CAAC,CAAC;AACJ,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,CAAC,KAAK,UAAU,oBAAoB,CACzC,OAAyB,EACzB,OAAO,GAAyB,EAAE;IAElC,MAAM,UAAU,GAAG,OAAO,CAAC,UAAU,IAAI,CAAC,CAAC;IAC3C,IAAI,gBAAgB,GAAG,UAAU,CAAC;IAElC,SAAS,CAAC;QACT,IAAI,CAAC;YACJ,8EAA8E;YAC9E,OAAO,MAAM,OAAO,EAAE,CAAC;QACxB,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YAChB,IAAI,OAAO,CAAC,MAAM,EAAE,OAAO;gBAAE,MAAM,gBAAgB,EAAE,CAAC;YACtD,IAAI,gBAAgB,IAAI,CAAC,IAAI,CAAC,eAAe,CAAC,KAAK,CAAC,IAAI,CAAC,wBAAwB,CAAC,KAAK,CAAC;gBAAE,MAAM,KAAK,CAAC;YAEtG,MAAM,UAAU,GAAG,UAAU,GAAG,gBAAgB,CAAC;YACjD,gBAAgB,EAAE,CAAC;YACnB,MAAM,cAAc,CAAC,eAAe,CAAC,KAAK,EAAE,UAAU,EAAE,OAAO,CAAC,eAAe,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC;QACnG,CAAC;IACF,CAAC;AACF,CAAC","sourcesContent":["const DEFAULT_MAX_RETRY_DELAY_MS = 60_000;\n\ninterface ProviderRetryOptions {\n\tmaxRetries?: number;\n\tmaxRetryDelayMs?: number;\n\tsignal?: AbortSignal;\n}\n\ninterface ProviderError extends Error {\n\tstatus: number | undefined;\n\theaders: Headers | undefined;\n}\n\nfunction isProviderError(error: unknown): error is ProviderError {\n\tif (!(error instanceof Error) || !(\"status\" in error) || !(\"headers\" in error)) return false;\n\treturn (\n\t\t(error.status === undefined || typeof error.status === \"number\") &&\n\t\t(error.headers === undefined || error.headers instanceof Headers)\n\t);\n}\n\n/** Mirrors the pinned OpenAI/Anthropic SDK retry policy; review when either SDK is upgraded. */\nfunction isRetryableProviderError(error: ProviderError): boolean {\n\tconst shouldRetry = error.headers?.get(\"x-should-retry\");\n\tif (shouldRetry === \"true\") return true;\n\tif (shouldRetry === \"false\") return false;\n\n\tif (error.status === undefined) return true;\n\treturn (\n\t\terror.status === 408 ||\n\t\terror.status === 409 ||\n\t\terror.status === 429 ||\n\t\t(typeof error.status === \"number\" && error.status >= 500)\n\t);\n}\n\nfunction validateServerRetryDelayMs(\n\tdelayMs: number,\n\tmaxRetryDelayMs: number | undefined,\n\tproviderErrorMessage: string,\n): number {\n\tconst maxDelayMs = maxRetryDelayMs ?? DEFAULT_MAX_RETRY_DELAY_MS;\n\tif (maxDelayMs > 0 && delayMs > maxDelayMs) {\n\t\tthrow new Error(\n\t\t\t`Server requested ${Math.ceil(delayMs / 1000)}s retry delay (max: ${Math.ceil(maxDelayMs / 1000)}s). ${providerErrorMessage}`,\n\t\t);\n\t}\n\treturn delayMs;\n}\n\nfunction getRetryDelayMs(error: ProviderError, retryIndex: number, maxRetryDelayMs: number | undefined): number {\n\tconst retryAfterMs = error.headers?.get(\"retry-after-ms\");\n\tif (retryAfterMs) {\n\t\tconst value = Number.parseFloat(retryAfterMs);\n\t\tif (Number.isFinite(value)) return validateServerRetryDelayMs(value, maxRetryDelayMs, error.message);\n\t}\n\n\tconst retryAfter = error.headers?.get(\"retry-after\");\n\tif (retryAfter) {\n\t\tconst seconds = Number.parseFloat(retryAfter);\n\t\tconst delayMs = Number.isNaN(seconds) ? Date.parse(retryAfter) - Date.now() : seconds * 1000;\n\t\tif (Number.isFinite(delayMs)) return validateServerRetryDelayMs(delayMs, maxRetryDelayMs, error.message);\n\t}\n\n\tconst exponentialDelay = Math.min(0.5 * 2 ** retryIndex, 8) * 1000;\n\treturn exponentialDelay * (1 - Math.random() * 0.25);\n}\n\nfunction createAbortError(): Error {\n\tconst error = new Error(\"Request aborted\");\n\terror.name = \"AbortError\";\n\treturn error;\n}\n\nfunction abortableSleep(ms: number, signal?: AbortSignal): Promise<void> {\n\treturn new Promise((resolve, reject) => {\n\t\tif (signal?.aborted) {\n\t\t\treject(createAbortError());\n\t\t\treturn;\n\t\t}\n\n\t\tconst onAbort = () => {\n\t\t\tclearTimeout(timeout);\n\t\t\treject(createAbortError());\n\t\t};\n\t\tconst timeout = setTimeout(\n\t\t\t() => {\n\t\t\t\tsignal?.removeEventListener(\"abort\", onAbort);\n\t\t\t\tresolve();\n\t\t\t},\n\t\t\tMath.max(0, ms),\n\t\t);\n\t\tsignal?.addEventListener(\"abort\", onAbort, { once: true });\n\t});\n}\n\n/**\n * Reproduce the retry behavior used by the OpenAI and Anthropic SDKs while making\n * their backoff sleep interruptible. Their built-in retry timers ignore the\n * request AbortSignal, so callers must invoke the SDK with `maxRetries: 0` and\n * wrap the request with this helper. Provider-requested delays above\n * `maxRetryDelayMs` fail immediately (60 seconds by default); set it to zero to\n * disable the limit.\n */\nexport async function retryProviderRequest<T>(\n\trequest: () => Promise<T>,\n\toptions: ProviderRetryOptions = {},\n): Promise<T> {\n\tconst maxRetries = options.maxRetries ?? 0;\n\tlet retriesRemaining = maxRetries;\n\n\tfor (;;) {\n\t\ttry {\n\t\t\t// Each retry is a fresh SDK request, so X-Stainless-Retry-Count remains zero.\n\t\t\treturn await request();\n\t\t} catch (error) {\n\t\t\tif (options.signal?.aborted) throw createAbortError();\n\t\t\tif (retriesRemaining <= 0 || !isProviderError(error) || !isRetryableProviderError(error)) throw error;\n\n\t\t\tconst retryIndex = maxRetries - retriesRemaining;\n\t\t\tretriesRemaining--;\n\t\t\tawait abortableSleep(getRetryDelayMs(error, retryIndex, options.maxRetryDelayMs), options.signal);\n\t\t}\n\t}\n}\n"]}
1
+ {"version":3,"file":"provider-retry.js","sourceRoot":"","sources":["../../src/utils/provider-retry.ts"],"names":[],"mappings":"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","sourcesContent":["const DEFAULT_MAX_RETRY_DELAY_MS = 60_000;\n\ninterface ProviderRetryOptions {\n\tmaxRetries?: number;\n\tmaxRetryDelayMs?: number;\n\tsignal?: AbortSignal;\n\t/** HTTP statuses that fail at once although the default policy would retry them. */\n\tnoRetryStatuses?: readonly number[];\n}\n\ninterface ProviderError extends Error {\n\tstatus: number | undefined;\n\theaders: Headers | undefined;\n}\n\nfunction isProviderError(error: unknown): error is ProviderError {\n\tif (!(error instanceof Error) || !(\"status\" in error) || !(\"headers\" in error)) return false;\n\treturn (\n\t\t(error.status === undefined || typeof error.status === \"number\") &&\n\t\t(error.headers === undefined || error.headers instanceof Headers)\n\t);\n}\n\n/** Mirrors the pinned OpenAI/Anthropic SDK retry policy; review when either SDK is upgraded. */\nfunction isRetryableProviderError(error: ProviderError): boolean {\n\tconst shouldRetry = error.headers?.get(\"x-should-retry\");\n\tif (shouldRetry === \"true\") return true;\n\tif (shouldRetry === \"false\") return false;\n\n\tif (error.status === undefined) return true;\n\treturn (\n\t\terror.status === 408 ||\n\t\terror.status === 409 ||\n\t\terror.status === 429 ||\n\t\t(typeof error.status === \"number\" && error.status >= 500)\n\t);\n}\n\nfunction validateServerRetryDelayMs(\n\tdelayMs: number,\n\tmaxRetryDelayMs: number | undefined,\n\tproviderErrorMessage: string,\n): number {\n\tconst maxDelayMs = maxRetryDelayMs ?? DEFAULT_MAX_RETRY_DELAY_MS;\n\tif (maxDelayMs > 0 && delayMs > maxDelayMs) {\n\t\tthrow new Error(\n\t\t\t`Server requested ${Math.ceil(delayMs / 1000)}s retry delay (max: ${Math.ceil(maxDelayMs / 1000)}s). ${providerErrorMessage}`,\n\t\t);\n\t}\n\treturn delayMs;\n}\n\nfunction getRetryDelayMs(error: ProviderError, retryIndex: number, maxRetryDelayMs: number | undefined): number {\n\tconst retryAfterMs = error.headers?.get(\"retry-after-ms\");\n\tif (retryAfterMs) {\n\t\tconst value = Number.parseFloat(retryAfterMs);\n\t\tif (Number.isFinite(value)) return validateServerRetryDelayMs(value, maxRetryDelayMs, error.message);\n\t}\n\n\tconst retryAfter = error.headers?.get(\"retry-after\");\n\tif (retryAfter) {\n\t\tconst seconds = Number.parseFloat(retryAfter);\n\t\tconst delayMs = Number.isNaN(seconds) ? Date.parse(retryAfter) - Date.now() : seconds * 1000;\n\t\tif (Number.isFinite(delayMs)) return validateServerRetryDelayMs(delayMs, maxRetryDelayMs, error.message);\n\t}\n\n\tconst exponentialDelay = Math.min(0.5 * 2 ** retryIndex, 8) * 1000;\n\treturn exponentialDelay * (1 - Math.random() * 0.25);\n}\n\nfunction createAbortError(): Error {\n\tconst error = new Error(\"Request aborted\");\n\terror.name = \"AbortError\";\n\treturn error;\n}\n\nfunction abortableSleep(ms: number, signal?: AbortSignal): Promise<void> {\n\treturn new Promise((resolve, reject) => {\n\t\tif (signal?.aborted) {\n\t\t\treject(createAbortError());\n\t\t\treturn;\n\t\t}\n\n\t\tconst onAbort = () => {\n\t\t\tclearTimeout(timeout);\n\t\t\treject(createAbortError());\n\t\t};\n\t\tconst timeout = setTimeout(\n\t\t\t() => {\n\t\t\t\tsignal?.removeEventListener(\"abort\", onAbort);\n\t\t\t\tresolve();\n\t\t\t},\n\t\t\tMath.max(0, ms),\n\t\t);\n\t\tsignal?.addEventListener(\"abort\", onAbort, { once: true });\n\t});\n}\n\n/**\n * Reproduce the retry behavior used by the OpenAI and Anthropic SDKs while making\n * their backoff sleep interruptible. Their built-in retry timers ignore the\n * request AbortSignal, so callers must invoke the SDK with `maxRetries: 0` and\n * wrap the request with this helper. Provider-requested delays above\n * `maxRetryDelayMs` fail immediately (60 seconds by default); set it to zero to\n * disable the limit.\n */\nexport async function retryProviderRequest<T>(\n\trequest: () => Promise<T>,\n\toptions: ProviderRetryOptions = {},\n): Promise<T> {\n\tconst maxRetries = options.maxRetries ?? 0;\n\tlet retriesRemaining = maxRetries;\n\n\tfor (;;) {\n\t\ttry {\n\t\t\t// Each retry is a fresh SDK request, so X-Stainless-Retry-Count remains zero.\n\t\t\treturn await request();\n\t\t} catch (error) {\n\t\t\tif (options.signal?.aborted) throw createAbortError();\n\t\t\tif (retriesRemaining <= 0 || !isProviderError(error) || !isRetryableProviderError(error)) throw error;\n\t\t\tif (error.status !== undefined && options.noRetryStatuses?.includes(error.status)) throw error;\n\n\t\t\tconst retryIndex = maxRetries - retriesRemaining;\n\t\t\tretriesRemaining--;\n\t\t\tawait abortableSleep(getRetryDelayMs(error, retryIndex, options.maxRetryDelayMs), options.signal);\n\t\t}\n\t}\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"retry.d.ts","sourceRoot":"","sources":["../../src/utils/retry.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAsGpD;;;;;;GAMG;AACH,MAAM,WAAW,WAAW;IAC3B,OAAO,EAAE,OAAO,CAAC;IACjB,qFAAqF;IACrF,UAAU,EAAE,MAAM,CAAC;IACnB,0FAA0F;IAC1F,WAAW,EAAE,MAAM,CAAC;IACpB,+EAA+E;IAC/E,eAAe,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,eAAO,MAAM,gCAAgC,QAAS,CAAC;AAEvD,wBAAgB,YAAY,CAAC,MAAM,EAAE,IAAI,CAAC,WAAW,EAAE,aAAa,GAAG,iBAAiB,CAAC,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAIlH;AAED,kFAAkF;AAClF,MAAM,WAAW,cAAc;IAC9B,0EAA0E;IAC1E,gBAAgB,CAAC,EAAE,CAClB,OAAO,EAAE,MAAM,EACf,WAAW,EAAE,MAAM,EACnB,OAAO,EAAE,MAAM,EACf,YAAY,EAAE,MAAM,KAChB,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1B,mFAAmF;IACnF,mBAAmB,CAAC,EAAE,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACjD,mFAAmF;IACnF,eAAe,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACnG;AA0BD;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAsB,kBAAkB,CACvC,OAAO,EAAE,MAAM,OAAO,CAAC,gBAAgB,CAAC,EACxC,MAAM,EAAE,WAAW,GAAG,SAAS,EAC/B,MAAM,EAAE,WAAW,GAAG,SAAS,EAC/B,SAAS,CAAC,EAAE,cAAc,GACxB,OAAO,CAAC,gBAAgB,CAAC,CA6C3B;AAED;;;;;;;;GAQG;AACH,wBAAgB,yBAAyB,CAAC,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAK5E"}
1
+ {"version":3,"file":"retry.d.ts","sourceRoot":"","sources":["../../src/utils/retry.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AA0HpD;;;;;;GAMG;AACH,MAAM,WAAW,WAAW;IAC3B,OAAO,EAAE,OAAO,CAAC;IACjB,qFAAqF;IACrF,UAAU,EAAE,MAAM,CAAC;IACnB,0FAA0F;IAC1F,WAAW,EAAE,MAAM,CAAC;IACpB,+EAA+E;IAC/E,eAAe,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,eAAO,MAAM,gCAAgC,QAAS,CAAC;AAEvD,wBAAgB,YAAY,CAAC,MAAM,EAAE,IAAI,CAAC,WAAW,EAAE,aAAa,GAAG,iBAAiB,CAAC,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAIlH;AAED,kFAAkF;AAClF,MAAM,WAAW,cAAc;IAC9B,0EAA0E;IAC1E,gBAAgB,CAAC,EAAE,CAClB,OAAO,EAAE,MAAM,EACf,WAAW,EAAE,MAAM,EACnB,OAAO,EAAE,MAAM,EACf,YAAY,EAAE,MAAM,KAChB,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1B,mFAAmF;IACnF,mBAAmB,CAAC,EAAE,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACjD,mFAAmF;IACnF,eAAe,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACnG;AA0BD;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAsB,kBAAkB,CACvC,OAAO,EAAE,MAAM,OAAO,CAAC,gBAAgB,CAAC,EACxC,MAAM,EAAE,WAAW,GAAG,SAAS,EAC/B,MAAM,EAAE,WAAW,GAAG,SAAS,EAC/B,SAAS,CAAC,EAAE,cAAc,GACxB,OAAO,CAAC,gBAAgB,CAAC,CA6C3B;AAED;;;;;;;;GAQG;AACH,wBAAgB,yBAAyB,CAAC,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAK5E"}
@@ -19,27 +19,38 @@ const NON_RETRYABLE_PROVIDER_LIMIT_ERROR_PATTERN = buildProviderErrorPattern([
19
19
  // Sign in with ChatGPT: the subscription's shared usage limit, which resets
20
20
  // after hours rather than seconds.
21
21
  "subscription_sharing_usage_limit_exceeded",
22
+ // Sign in with ChatGPT on a ChatGPT Business plan: a usage limit set by the
23
+ // workspace admin. Only the admin can raise it.
24
+ "subscription_sharing_workplace_individual_usage_limit_exceeded",
22
25
  ]);
23
26
  const RETRYABLE_PROVIDER_ERROR_PATTERN = buildProviderErrorPattern([
24
27
  // Generic provider load, HTTP status, and server-side transient failures.
25
28
  "overloaded",
29
+ "server_busy",
30
+ "servers are currently busy",
26
31
  "currently experiencing high demand",
32
+ "model is at capacity",
27
33
  "rate.?limit",
28
34
  "too many requests",
29
- "429",
30
- "500",
31
- "502",
32
- "503",
33
- "504",
34
- "520",
35
- "524",
36
- "service.?unavailable",
35
+ // Status codes must not be part of a longer word or number, e.g. a request id or byte count (#11057).
36
+ "(?<![a-z0-9])(?:429|500|502|503|504|520|524)(?![a-z0-9])",
37
+ // Also matches "The service is temporarily unavailable" (#10965).
38
+ "service.?(?:is.?)?(?:temporarily.?)?unavailable",
37
39
  "server.?error",
38
40
  "internal.?error",
39
41
  // Wrapper/provider text for transient upstream failures, including OpenRouter
40
- // "Provider returned error" responses (#2264).
41
- "provider.?returned.?error",
42
+ // "Provider returned error" responses (#2264) and gateway wording such as
43
+ // "The upstream provider returned an error" (#10842).
44
+ "provider.?returned.?(?:an.?)?error",
42
45
  "exceeded request buffer limit while retrying upstream",
46
+ // Gateway failure relaying the upstream response stream, sent as an OpenAI
47
+ // Responses `response.failed` event without an error code (#10920).
48
+ "unable to forward upstream",
49
+ // Other gateway upstream failures relayed as `response.failed` error codes (#10920).
50
+ "upstream.?request.?failed",
51
+ "upstream_stream_read_error",
52
+ "stream was interrupted",
53
+ "http/?2 stream failed",
43
54
  // Network, proxy, and fetch transport failures. This includes OpenAI Codex
44
55
  // raw-fetch failures such as "upstream connect", "connection refused", and
45
56
  // "reset before headers" (#733), plus OpenRouter connection drops (#3317).
@@ -49,6 +60,9 @@ const RETRYABLE_PROVIDER_ERROR_PATTERN = buildProviderErrorPattern([
49
60
  "connection.?lost",
50
61
  "other side closed",
51
62
  "fetch failed",
63
+ // node-fetch system errors, e.g. the Google ADC OAuth token refresh used by
64
+ // google-vertex. The reason is often empty (#10996).
65
+ "request to \\S+ failed, reason:",
52
66
  "getaddrinfo",
53
67
  "ENOTFOUND",
54
68
  "EAI_AGAIN",
@@ -69,6 +83,12 @@ const RETRYABLE_PROVIDER_ERROR_PATTERN = buildProviderErrorPattern([
69
83
  "stream ended before message_stop",
70
84
  "stream ended before a terminal response event",
71
85
  "http2 request did not get a response",
86
+ // Codex CLI mid-stream disconnect, relayed by proxies that wrap the Codex CLI,
87
+ // e.g. "stream disconnected before completion: stream closed before response.completed" (#11057).
88
+ "stream disconnected before completion",
89
+ // Node ERR_HTTP2_STREAM_CANCEL: the HTTP/2 session died before the request was
90
+ // sent, e.g. after the Bedrock SDK's 5-minute session timeout (badlogic/pi-mono#10379).
91
+ "pending stream has been canceled",
72
92
  // Provider-requested retry delay cap failures should flow through the outer
73
93
  // retry policy so callers can surface/abort the backoff (#1123).
74
94
  "retry delay",
@@ -1 +1 @@
1
- {"version":3,"file":"retry.js","sourceRoot":"","sources":["../../src/utils/retry.ts"],"names":[],"mappings":"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","sourcesContent":["import type { AssistantMessage } from \"../types.ts\";\n\nfunction buildProviderErrorPattern(patterns: readonly string[]): RegExp {\n\treturn new RegExp(patterns.join(\"|\"), \"i\");\n}\n\nconst NON_RETRYABLE_PROVIDER_LIMIT_ERROR_PATTERN = buildProviderErrorPattern([\n\t// OpenCode Go/free-tier limits returned as 429 JSON error types by OpenCode's\n\t// Zen API. These are subscription/account limits, not transient throttles.\n\t\"GoUsageLimitError\",\n\t\"FreeUsageLimitError\",\n\n\t// OpenCode Go subscription-limit text asks users to enable available-balance\n\t// usage after rolling/weekly/monthly limits are reached.\n\t\"Monthly usage limit reached\",\n\t\"available balance\",\n\n\t// Generic quota/budget/billing exhaustion. `insufficient_quota` is OpenAI's\n\t// quota/billing error code; the other strings cover common gateway wording.\n\t\"insufficient_quota\",\n\t\"out of budget\",\n\t\"quota exceeded\",\n\t\"billing\",\n\n\t// Sign in with ChatGPT: the subscription's shared usage limit, which resets\n\t// after hours rather than seconds.\n\t\"subscription_sharing_usage_limit_exceeded\",\n]);\n\nconst RETRYABLE_PROVIDER_ERROR_PATTERN = buildProviderErrorPattern([\n\t// Generic provider load, HTTP status, and server-side transient failures.\n\t\"overloaded\",\n\t\"currently experiencing high demand\",\n\t\"rate.?limit\",\n\t\"too many requests\",\n\t\"429\",\n\t\"500\",\n\t\"502\",\n\t\"503\",\n\t\"504\",\n\t\"520\",\n\t\"524\",\n\t\"service.?unavailable\",\n\t\"server.?error\",\n\t\"internal.?error\",\n\n\t// Wrapper/provider text for transient upstream failures, including OpenRouter\n\t// \"Provider returned error\" responses (#2264).\n\t\"provider.?returned.?error\",\n\t\"exceeded request buffer limit while retrying upstream\",\n\n\t// Network, proxy, and fetch transport failures. This includes OpenAI Codex\n\t// raw-fetch failures such as \"upstream connect\", \"connection refused\", and\n\t// \"reset before headers\" (#733), plus OpenRouter connection drops (#3317).\n\t\"network.?error\",\n\t\"connection.?error\",\n\t\"connection.?refused\",\n\t\"connection.?lost\",\n\t\"other side closed\",\n\t\"fetch failed\",\n\t\"getaddrinfo\",\n\t\"ENOTFOUND\",\n\t\"EAI_AGAIN\",\n\t\"upstream.?connect\",\n\t\"reset before headers\",\n\t\"socket hang up\",\n\t\"socket connection was closed\",\n\t\"timed? out\",\n\t\"timeout\",\n\t\"terminated\",\n\n\t// WebSocket transports can report close/error text instead of HTTP/fetch text.\n\t\"websocket.?closed\",\n\t\"websocket.?error\",\n\n\t// Premature stream endings from SDKs and transports. Anthropic can throw\n\t// \"stream ended without ...\" and \"Anthropic stream ended before message_stop\"\n\t// (#4433); Bedrock/Smithy can throw an HTTP/2 no-response error (#3594).\n\t\"ended without\",\n\t\"stream ended before message_stop\",\n\t\"stream ended before a terminal response event\",\n\t\"http2 request did not get a response\",\n\n\t// Provider-requested retry delay cap failures should flow through the outer\n\t// retry policy so callers can surface/abort the backoff (#1123).\n\t\"retry delay\",\n\n\t// Explicit retry guidance emitted mid-stream by OpenAI Responses and Bedrock\n\t// stream exceptions (#6019).\n\t\"you can retry your request\",\n\t\"try your request again\",\n\t\"please retry your request\",\n\n\t// gRPC based providers (e.g. NVIDIA NIM)\n\t\"ResourceExhausted\",\n\n\t// Sign in with ChatGPT: usage or user data temporarily unavailable. Usage\n\t// failures can arrive mid-stream without an HTTP 503 in the message.\n\t\"subscription_sharing_usage_unavailable\",\n\t\"subscription_sharing_user_unavailable\",\n]);\n\n/**\n * Retry policy: bounded attempts with exponential backoff (`baseDelayMs * 2^(attempt-1)`).\n * `maxAgentDelayMs` caps each computed delay and defaults to 60 seconds.\n * Matches `settings.retry` (`enabled`, `maxRetries`, `baseDelayMs`, `maxAgentDelayMs`) in coding-agent; kept\n * here so the classifier and the policy-driven retry loop live together and stay reusable\n * by the SDK and other callers.\n */\nexport interface RetryPolicy {\n\tenabled: boolean;\n\t/** Max retry attempts (0 = no retries). The initial call never counts as a retry. */\n\tmaxRetries: number;\n\t/** Base delay in ms. Per-attempt delay is `baseDelayMs * 2^(attempt-1)` before jitter. */\n\tbaseDelayMs: number;\n\t/** Optional cap for agent-level retry delays in ms. Defaults to 60 seconds. */\n\tmaxAgentDelayMs?: number;\n}\n\nexport const DEFAULT_MAX_AGENT_RETRY_DELAY_MS = 60_000;\n\nexport function retryDelayMs(policy: Pick<RetryPolicy, \"baseDelayMs\" | \"maxAgentDelayMs\">, attempt: number): number {\n\tconst delay = policy.baseDelayMs * 2 ** Math.max(0, attempt - 1);\n\tconst safeDelay = Number.isSafeInteger(delay) ? delay : Number.MAX_SAFE_INTEGER;\n\treturn Math.min(safeDelay, policy.maxAgentDelayMs ?? DEFAULT_MAX_AGENT_RETRY_DELAY_MS);\n}\n\n/** Optional callbacks emitted by {@link retryAssistantCall} around each retry. */\nexport interface RetryCallbacks {\n\t/** Emitted before the backoff sleep of each retry attempt (1-indexed). */\n\tonRetryScheduled?: (\n\t\tattempt: number,\n\t\tmaxAttempts: number,\n\t\tdelayMs: number,\n\t\terrorMessage: string,\n\t) => void | Promise<void>;\n\t/** Emitted after the backoff sleep, immediately before the retried call starts. */\n\tonRetryAttemptStart?: () => void | Promise<void>;\n\t/** Emitted once when the loop ends: success if a later call completed normally. */\n\tonRetryFinished?: (success: boolean, attempt: number, finalError?: string) => void | Promise<void>;\n}\n\nclass RetrySleepAbortError extends Error {\n\tconstructor() {\n\t\tsuper(\"Aborted\");\n\t}\n}\n\nfunction sleep(ms: number, signal?: AbortSignal): Promise<void> {\n\treturn new Promise((resolve, reject) => {\n\t\tif (signal?.aborted) {\n\t\t\treject(new RetrySleepAbortError());\n\t\t\treturn;\n\t\t}\n\t\tconst timeout = setTimeout(resolve, ms);\n\t\tsignal?.addEventListener(\n\t\t\t\"abort\",\n\t\t\t() => {\n\t\t\t\tclearTimeout(timeout);\n\t\t\t\treject(new RetrySleepAbortError());\n\t\t\t},\n\t\t\t{ once: true },\n\t\t);\n\t});\n}\n\n/**\n * Run a single assistant-producing call with bounded retry on transient errors.\n *\n * Behavior:\n * - A successful response is returned immediately. Aborts are terminal and never\n * retried, but reported as unsuccessful if they happen after a retry was scheduled.\n * Aborts during the backoff sleep are normalized to an aborted `AssistantMessage`\n * too, so callers do not need to care when cancellation happened.\n * - A non-retryable error (per {@link isRetryableAssistantError}, including quota/\n * billing exhaustion) is returned immediately so deterministic errors fail fast.\n * - Otherwise retries up to `maxRetries` times with exponential backoff, emitting\n * `onRetryScheduled` before each sleep, `onRetryAttemptStart` after each sleep before\n * the retried call starts, and `onRetryFinished` once at the end (whether the loop\n * ends in success, exhausted retries, or an aborted backoff).\n *\n * When `policy` is undefined or disabled, the first response is returned unchanged\n * (equivalent to calling `produce()` directly).\n */\nexport async function retryAssistantCall(\n\tproduce: () => Promise<AssistantMessage>,\n\tpolicy: RetryPolicy | undefined,\n\tsignal: AbortSignal | undefined,\n\tcallbacks?: RetryCallbacks,\n): Promise<AssistantMessage> {\n\tconst maxAttempts = policy?.enabled ? policy.maxRetries : 0;\n\n\tlet attempt = 0;\n\tlet lastRetry: { attempt: number; errorMessage: string } | undefined;\n\tfor (;;) {\n\t\tconst response = await produce();\n\n\t\t// Abort: terminal but not successful. Never retry an aborted message.\n\t\tif (response.stopReason === \"aborted\") {\n\t\t\tif (lastRetry) await callbacks?.onRetryFinished?.(false, lastRetry.attempt);\n\t\t\treturn response;\n\t\t}\n\n\t\t// Success: non-error, non-abort responses return as-is.\n\t\tif (response.stopReason !== \"error\") {\n\t\t\tif (lastRetry) await callbacks?.onRetryFinished?.(true, lastRetry.attempt);\n\t\t\treturn response;\n\t\t}\n\n\t\t// Non-retryable, or budget exhausted: return the final error message.\n\t\tif (attempt >= maxAttempts || !isRetryableAssistantError(response)) {\n\t\t\tif (lastRetry) await callbacks?.onRetryFinished?.(false, lastRetry.attempt, response.errorMessage);\n\t\t\treturn response;\n\t\t}\n\n\t\tattempt++;\n\t\tlastRetry = { attempt, errorMessage: response.errorMessage || \"Unknown error\" };\n\t\tconst delayMs = retryDelayMs(policy!, attempt);\n\t\tawait callbacks?.onRetryScheduled?.(attempt, maxAttempts, delayMs, lastRetry.errorMessage);\n\n\t\t// Normalize aborts during retry backoff to the same AssistantMessage shape as\n\t\t// provider stream aborts, so callers do not need to care when cancellation happened.\n\t\ttry {\n\t\t\tawait sleep(delayMs, signal);\n\t\t} catch (error) {\n\t\t\tawait callbacks?.onRetryFinished?.(false, attempt, lastRetry.errorMessage);\n\t\t\tif (error instanceof RetrySleepAbortError) {\n\t\t\t\tconst { errorMessage: _errorMessage, ...rest } = response;\n\t\t\t\treturn { ...rest, stopReason: \"aborted\" };\n\t\t\t}\n\t\t\tthrow error;\n\t\t}\n\t\tawait callbacks?.onRetryAttemptStart?.();\n\t}\n}\n\n/**\n * Classifies whether a failed assistant message looks like a transient provider\n * or transport error, so callers can decide if the last assistant turn should be\n * restarted.\n *\n * This does not implement retry policy. Callers should first handle context\n * overflow separately, then apply their own retry budget, backoff, and reporting\n * before restarting the assistant turn.\n */\nexport function isRetryableAssistantError(message: AssistantMessage): boolean {\n\tif (message.stopReason !== \"error\" || !message.errorMessage) return false;\n\tconst errorMessage = message.errorMessage;\n\tif (NON_RETRYABLE_PROVIDER_LIMIT_ERROR_PATTERN.test(errorMessage)) return false;\n\treturn RETRYABLE_PROVIDER_ERROR_PATTERN.test(errorMessage);\n}\n"]}
1
+ {"version":3,"file":"retry.js","sourceRoot":"","sources":["../../src/utils/retry.ts"],"names":[],"mappings":"AAEA,SAAS,yBAAyB,CAAC,QAA2B;IAC7D,OAAO,IAAI,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,GAAG,CAAC,CAAC;AAC5C,CAAC;AAED,MAAM,0CAA0C,GAAG,yBAAyB,CAAC;IAC5E,8EAA8E;IAC9E,2EAA2E;IAC3E,mBAAmB;IACnB,qBAAqB;IAErB,6EAA6E;IAC7E,yDAAyD;IACzD,6BAA6B;IAC7B,mBAAmB;IAEnB,4EAA4E;IAC5E,4EAA4E;IAC5E,oBAAoB;IACpB,eAAe;IACf,gBAAgB;IAChB,SAAS;IAET,4EAA4E;IAC5E,mCAAmC;IACnC,2CAA2C;IAC3C,4EAA4E;IAC5E,gDAAgD;IAChD,gEAAgE;CAChE,CAAC,CAAC;AAEH,MAAM,gCAAgC,GAAG,yBAAyB,CAAC;IAClE,0EAA0E;IAC1E,YAAY;IACZ,aAAa;IACb,4BAA4B;IAC5B,oCAAoC;IACpC,sBAAsB;IACtB,aAAa;IACb,mBAAmB;IACnB,sGAAsG;IACtG,0DAA0D;IAC1D,kEAAkE;IAClE,iDAAiD;IACjD,eAAe;IACf,iBAAiB;IAEjB,8EAA8E;IAC9E,0EAA0E;IAC1E,sDAAsD;IACtD,oCAAoC;IACpC,uDAAuD;IACvD,2EAA2E;IAC3E,oEAAoE;IACpE,4BAA4B;IAC5B,qFAAqF;IACrF,2BAA2B;IAC3B,4BAA4B;IAC5B,wBAAwB;IACxB,uBAAuB;IAEvB,2EAA2E;IAC3E,2EAA2E;IAC3E,2EAA2E;IAC3E,gBAAgB;IAChB,mBAAmB;IACnB,qBAAqB;IACrB,kBAAkB;IAClB,mBAAmB;IACnB,cAAc;IACd,4EAA4E;IAC5E,qDAAqD;IACrD,iCAAiC;IACjC,aAAa;IACb,WAAW;IACX,WAAW;IACX,mBAAmB;IACnB,sBAAsB;IACtB,gBAAgB;IAChB,8BAA8B;IAC9B,YAAY;IACZ,SAAS;IACT,YAAY;IAEZ,+EAA+E;IAC/E,mBAAmB;IACnB,kBAAkB;IAElB,yEAAyE;IACzE,8EAA8E;IAC9E,yEAAyE;IACzE,eAAe;IACf,kCAAkC;IAClC,+CAA+C;IAC/C,sCAAsC;IACtC,+EAA+E;IAC/E,kGAAkG;IAClG,uCAAuC;IACvC,+EAA+E;IAC/E,wFAAwF;IACxF,kCAAkC;IAElC,4EAA4E;IAC5E,iEAAiE;IACjE,aAAa;IAEb,6EAA6E;IAC7E,6BAA6B;IAC7B,4BAA4B;IAC5B,wBAAwB;IACxB,2BAA2B;IAE3B,yCAAyC;IACzC,mBAAmB;IAEnB,0EAA0E;IAC1E,qEAAqE;IACrE,wCAAwC;IACxC,uCAAuC;CACvC,CAAC,CAAC;AAmBH,MAAM,CAAC,MAAM,gCAAgC,GAAG,MAAM,CAAC;AAEvD,MAAM,UAAU,YAAY,CAAC,MAA4D,EAAE,OAAe;IACzG,MAAM,KAAK,GAAG,MAAM,CAAC,WAAW,GAAG,CAAC,IAAI,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,OAAO,GAAG,CAAC,CAAC,CAAC;IACjE,MAAM,SAAS,GAAG,MAAM,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,gBAAgB,CAAC;IAChF,OAAO,IAAI,CAAC,GAAG,CAAC,SAAS,EAAE,MAAM,CAAC,eAAe,IAAI,gCAAgC,CAAC,CAAC;AACxF,CAAC;AAiBD,MAAM,oBAAqB,SAAQ,KAAK;IACvC;QACC,KAAK,CAAC,SAAS,CAAC,CAAC;IAClB,CAAC;CACD;AAED,SAAS,KAAK,CAAC,EAAU,EAAE,MAAoB;IAC9C,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QACtC,IAAI,MAAM,EAAE,OAAO,EAAE,CAAC;YACrB,MAAM,CAAC,IAAI,oBAAoB,EAAE,CAAC,CAAC;YACnC,OAAO;QACR,CAAC;QACD,MAAM,OAAO,GAAG,UAAU,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;QACxC,MAAM,EAAE,gBAAgB,CACvB,OAAO,EACP,GAAG,EAAE;YACJ,YAAY,CAAC,OAAO,CAAC,CAAC;YACtB,MAAM,CAAC,IAAI,oBAAoB,EAAE,CAAC,CAAC;QACpC,CAAC,EACD,EAAE,IAAI,EAAE,IAAI,EAAE,CACd,CAAC;IACH,CAAC,CAAC,CAAC;AACJ,CAAC;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,CAAC,KAAK,UAAU,kBAAkB,CACvC,OAAwC,EACxC,MAA+B,EAC/B,MAA+B,EAC/B,SAA0B;IAE1B,MAAM,WAAW,GAAG,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC;IAE5D,IAAI,OAAO,GAAG,CAAC,CAAC;IAChB,IAAI,SAAgE,CAAC;IACrE,SAAS,CAAC;QACT,MAAM,QAAQ,GAAG,MAAM,OAAO,EAAE,CAAC;QAEjC,sEAAsE;QACtE,IAAI,QAAQ,CAAC,UAAU,KAAK,SAAS,EAAE,CAAC;YACvC,IAAI,SAAS;gBAAE,MAAM,SAAS,EAAE,eAAe,EAAE,CAAC,KAAK,EAAE,SAAS,CAAC,OAAO,CAAC,CAAC;YAC5E,OAAO,QAAQ,CAAC;QACjB,CAAC;QAED,wDAAwD;QACxD,IAAI,QAAQ,CAAC,UAAU,KAAK,OAAO,EAAE,CAAC;YACrC,IAAI,SAAS;gBAAE,MAAM,SAAS,EAAE,eAAe,EAAE,CAAC,IAAI,EAAE,SAAS,CAAC,OAAO,CAAC,CAAC;YAC3E,OAAO,QAAQ,CAAC;QACjB,CAAC;QAED,sEAAsE;QACtE,IAAI,OAAO,IAAI,WAAW,IAAI,CAAC,yBAAyB,CAAC,QAAQ,CAAC,EAAE,CAAC;YACpE,IAAI,SAAS;gBAAE,MAAM,SAAS,EAAE,eAAe,EAAE,CAAC,KAAK,EAAE,SAAS,CAAC,OAAO,EAAE,QAAQ,CAAC,YAAY,CAAC,CAAC;YACnG,OAAO,QAAQ,CAAC;QACjB,CAAC;QAED,OAAO,EAAE,CAAC;QACV,SAAS,GAAG,EAAE,OAAO,EAAE,YAAY,EAAE,QAAQ,CAAC,YAAY,IAAI,eAAe,EAAE,CAAC;QAChF,MAAM,OAAO,GAAG,YAAY,CAAC,MAAO,EAAE,OAAO,CAAC,CAAC;QAC/C,MAAM,SAAS,EAAE,gBAAgB,EAAE,CAAC,OAAO,EAAE,WAAW,EAAE,OAAO,EAAE,SAAS,CAAC,YAAY,CAAC,CAAC;QAE3F,8EAA8E;QAC9E,qFAAqF;QACrF,IAAI,CAAC;YACJ,MAAM,KAAK,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;QAC9B,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YAChB,MAAM,SAAS,EAAE,eAAe,EAAE,CAAC,KAAK,EAAE,OAAO,EAAE,SAAS,CAAC,YAAY,CAAC,CAAC;YAC3E,IAAI,KAAK,YAAY,oBAAoB,EAAE,CAAC;gBAC3C,MAAM,EAAE,YAAY,EAAE,aAAa,EAAE,GAAG,IAAI,EAAE,GAAG,QAAQ,CAAC;gBAC1D,OAAO,EAAE,GAAG,IAAI,EAAE,UAAU,EAAE,SAAS,EAAE,CAAC;YAC3C,CAAC;YACD,MAAM,KAAK,CAAC;QACb,CAAC;QACD,MAAM,SAAS,EAAE,mBAAmB,EAAE,EAAE,CAAC;IAC1C,CAAC;AACF,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,yBAAyB,CAAC,OAAyB;IAClE,IAAI,OAAO,CAAC,UAAU,KAAK,OAAO,IAAI,CAAC,OAAO,CAAC,YAAY;QAAE,OAAO,KAAK,CAAC;IAC1E,MAAM,YAAY,GAAG,OAAO,CAAC,YAAY,CAAC;IAC1C,IAAI,0CAA0C,CAAC,IAAI,CAAC,YAAY,CAAC;QAAE,OAAO,KAAK,CAAC;IAChF,OAAO,gCAAgC,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;AAC5D,CAAC","sourcesContent":["import type { AssistantMessage } from \"../types.ts\";\n\nfunction buildProviderErrorPattern(patterns: readonly string[]): RegExp {\n\treturn new RegExp(patterns.join(\"|\"), \"i\");\n}\n\nconst NON_RETRYABLE_PROVIDER_LIMIT_ERROR_PATTERN = buildProviderErrorPattern([\n\t// OpenCode Go/free-tier limits returned as 429 JSON error types by OpenCode's\n\t// Zen API. These are subscription/account limits, not transient throttles.\n\t\"GoUsageLimitError\",\n\t\"FreeUsageLimitError\",\n\n\t// OpenCode Go subscription-limit text asks users to enable available-balance\n\t// usage after rolling/weekly/monthly limits are reached.\n\t\"Monthly usage limit reached\",\n\t\"available balance\",\n\n\t// Generic quota/budget/billing exhaustion. `insufficient_quota` is OpenAI's\n\t// quota/billing error code; the other strings cover common gateway wording.\n\t\"insufficient_quota\",\n\t\"out of budget\",\n\t\"quota exceeded\",\n\t\"billing\",\n\n\t// Sign in with ChatGPT: the subscription's shared usage limit, which resets\n\t// after hours rather than seconds.\n\t\"subscription_sharing_usage_limit_exceeded\",\n\t// Sign in with ChatGPT on a ChatGPT Business plan: a usage limit set by the\n\t// workspace admin. Only the admin can raise it.\n\t\"subscription_sharing_workplace_individual_usage_limit_exceeded\",\n]);\n\nconst RETRYABLE_PROVIDER_ERROR_PATTERN = buildProviderErrorPattern([\n\t// Generic provider load, HTTP status, and server-side transient failures.\n\t\"overloaded\",\n\t\"server_busy\",\n\t\"servers are currently busy\",\n\t\"currently experiencing high demand\",\n\t\"model is at capacity\",\n\t\"rate.?limit\",\n\t\"too many requests\",\n\t// Status codes must not be part of a longer word or number, e.g. a request id or byte count (#11057).\n\t\"(?<![a-z0-9])(?:429|500|502|503|504|520|524)(?![a-z0-9])\",\n\t// Also matches \"The service is temporarily unavailable\" (#10965).\n\t\"service.?(?:is.?)?(?:temporarily.?)?unavailable\",\n\t\"server.?error\",\n\t\"internal.?error\",\n\n\t// Wrapper/provider text for transient upstream failures, including OpenRouter\n\t// \"Provider returned error\" responses (#2264) and gateway wording such as\n\t// \"The upstream provider returned an error\" (#10842).\n\t\"provider.?returned.?(?:an.?)?error\",\n\t\"exceeded request buffer limit while retrying upstream\",\n\t// Gateway failure relaying the upstream response stream, sent as an OpenAI\n\t// Responses `response.failed` event without an error code (#10920).\n\t\"unable to forward upstream\",\n\t// Other gateway upstream failures relayed as `response.failed` error codes (#10920).\n\t\"upstream.?request.?failed\",\n\t\"upstream_stream_read_error\",\n\t\"stream was interrupted\",\n\t\"http/?2 stream failed\",\n\n\t// Network, proxy, and fetch transport failures. This includes OpenAI Codex\n\t// raw-fetch failures such as \"upstream connect\", \"connection refused\", and\n\t// \"reset before headers\" (#733), plus OpenRouter connection drops (#3317).\n\t\"network.?error\",\n\t\"connection.?error\",\n\t\"connection.?refused\",\n\t\"connection.?lost\",\n\t\"other side closed\",\n\t\"fetch failed\",\n\t// node-fetch system errors, e.g. the Google ADC OAuth token refresh used by\n\t// google-vertex. The reason is often empty (#10996).\n\t\"request to \\\\S+ failed, reason:\",\n\t\"getaddrinfo\",\n\t\"ENOTFOUND\",\n\t\"EAI_AGAIN\",\n\t\"upstream.?connect\",\n\t\"reset before headers\",\n\t\"socket hang up\",\n\t\"socket connection was closed\",\n\t\"timed? out\",\n\t\"timeout\",\n\t\"terminated\",\n\n\t// WebSocket transports can report close/error text instead of HTTP/fetch text.\n\t\"websocket.?closed\",\n\t\"websocket.?error\",\n\n\t// Premature stream endings from SDKs and transports. Anthropic can throw\n\t// \"stream ended without ...\" and \"Anthropic stream ended before message_stop\"\n\t// (#4433); Bedrock/Smithy can throw an HTTP/2 no-response error (#3594).\n\t\"ended without\",\n\t\"stream ended before message_stop\",\n\t\"stream ended before a terminal response event\",\n\t\"http2 request did not get a response\",\n\t// Codex CLI mid-stream disconnect, relayed by proxies that wrap the Codex CLI,\n\t// e.g. \"stream disconnected before completion: stream closed before response.completed\" (#11057).\n\t\"stream disconnected before completion\",\n\t// Node ERR_HTTP2_STREAM_CANCEL: the HTTP/2 session died before the request was\n\t// sent, e.g. after the Bedrock SDK's 5-minute session timeout (badlogic/pi-mono#10379).\n\t\"pending stream has been canceled\",\n\n\t// Provider-requested retry delay cap failures should flow through the outer\n\t// retry policy so callers can surface/abort the backoff (#1123).\n\t\"retry delay\",\n\n\t// Explicit retry guidance emitted mid-stream by OpenAI Responses and Bedrock\n\t// stream exceptions (#6019).\n\t\"you can retry your request\",\n\t\"try your request again\",\n\t\"please retry your request\",\n\n\t// gRPC based providers (e.g. NVIDIA NIM)\n\t\"ResourceExhausted\",\n\n\t// Sign in with ChatGPT: usage or user data temporarily unavailable. Usage\n\t// failures can arrive mid-stream without an HTTP 503 in the message.\n\t\"subscription_sharing_usage_unavailable\",\n\t\"subscription_sharing_user_unavailable\",\n]);\n\n/**\n * Retry policy: bounded attempts with exponential backoff (`baseDelayMs * 2^(attempt-1)`).\n * `maxAgentDelayMs` caps each computed delay and defaults to 60 seconds.\n * Matches `settings.retry` (`enabled`, `maxRetries`, `baseDelayMs`, `maxAgentDelayMs`) in coding-agent; kept\n * here so the classifier and the policy-driven retry loop live together and stay reusable\n * by the SDK and other callers.\n */\nexport interface RetryPolicy {\n\tenabled: boolean;\n\t/** Max retry attempts (0 = no retries). The initial call never counts as a retry. */\n\tmaxRetries: number;\n\t/** Base delay in ms. Per-attempt delay is `baseDelayMs * 2^(attempt-1)` before jitter. */\n\tbaseDelayMs: number;\n\t/** Optional cap for agent-level retry delays in ms. Defaults to 60 seconds. */\n\tmaxAgentDelayMs?: number;\n}\n\nexport const DEFAULT_MAX_AGENT_RETRY_DELAY_MS = 60_000;\n\nexport function retryDelayMs(policy: Pick<RetryPolicy, \"baseDelayMs\" | \"maxAgentDelayMs\">, attempt: number): number {\n\tconst delay = policy.baseDelayMs * 2 ** Math.max(0, attempt - 1);\n\tconst safeDelay = Number.isSafeInteger(delay) ? delay : Number.MAX_SAFE_INTEGER;\n\treturn Math.min(safeDelay, policy.maxAgentDelayMs ?? DEFAULT_MAX_AGENT_RETRY_DELAY_MS);\n}\n\n/** Optional callbacks emitted by {@link retryAssistantCall} around each retry. */\nexport interface RetryCallbacks {\n\t/** Emitted before the backoff sleep of each retry attempt (1-indexed). */\n\tonRetryScheduled?: (\n\t\tattempt: number,\n\t\tmaxAttempts: number,\n\t\tdelayMs: number,\n\t\terrorMessage: string,\n\t) => void | Promise<void>;\n\t/** Emitted after the backoff sleep, immediately before the retried call starts. */\n\tonRetryAttemptStart?: () => void | Promise<void>;\n\t/** Emitted once when the loop ends: success if a later call completed normally. */\n\tonRetryFinished?: (success: boolean, attempt: number, finalError?: string) => void | Promise<void>;\n}\n\nclass RetrySleepAbortError extends Error {\n\tconstructor() {\n\t\tsuper(\"Aborted\");\n\t}\n}\n\nfunction sleep(ms: number, signal?: AbortSignal): Promise<void> {\n\treturn new Promise((resolve, reject) => {\n\t\tif (signal?.aborted) {\n\t\t\treject(new RetrySleepAbortError());\n\t\t\treturn;\n\t\t}\n\t\tconst timeout = setTimeout(resolve, ms);\n\t\tsignal?.addEventListener(\n\t\t\t\"abort\",\n\t\t\t() => {\n\t\t\t\tclearTimeout(timeout);\n\t\t\t\treject(new RetrySleepAbortError());\n\t\t\t},\n\t\t\t{ once: true },\n\t\t);\n\t});\n}\n\n/**\n * Run a single assistant-producing call with bounded retry on transient errors.\n *\n * Behavior:\n * - A successful response is returned immediately. Aborts are terminal and never\n * retried, but reported as unsuccessful if they happen after a retry was scheduled.\n * Aborts during the backoff sleep are normalized to an aborted `AssistantMessage`\n * too, so callers do not need to care when cancellation happened.\n * - A non-retryable error (per {@link isRetryableAssistantError}, including quota/\n * billing exhaustion) is returned immediately so deterministic errors fail fast.\n * - Otherwise retries up to `maxRetries` times with exponential backoff, emitting\n * `onRetryScheduled` before each sleep, `onRetryAttemptStart` after each sleep before\n * the retried call starts, and `onRetryFinished` once at the end (whether the loop\n * ends in success, exhausted retries, or an aborted backoff).\n *\n * When `policy` is undefined or disabled, the first response is returned unchanged\n * (equivalent to calling `produce()` directly).\n */\nexport async function retryAssistantCall(\n\tproduce: () => Promise<AssistantMessage>,\n\tpolicy: RetryPolicy | undefined,\n\tsignal: AbortSignal | undefined,\n\tcallbacks?: RetryCallbacks,\n): Promise<AssistantMessage> {\n\tconst maxAttempts = policy?.enabled ? policy.maxRetries : 0;\n\n\tlet attempt = 0;\n\tlet lastRetry: { attempt: number; errorMessage: string } | undefined;\n\tfor (;;) {\n\t\tconst response = await produce();\n\n\t\t// Abort: terminal but not successful. Never retry an aborted message.\n\t\tif (response.stopReason === \"aborted\") {\n\t\t\tif (lastRetry) await callbacks?.onRetryFinished?.(false, lastRetry.attempt);\n\t\t\treturn response;\n\t\t}\n\n\t\t// Success: non-error, non-abort responses return as-is.\n\t\tif (response.stopReason !== \"error\") {\n\t\t\tif (lastRetry) await callbacks?.onRetryFinished?.(true, lastRetry.attempt);\n\t\t\treturn response;\n\t\t}\n\n\t\t// Non-retryable, or budget exhausted: return the final error message.\n\t\tif (attempt >= maxAttempts || !isRetryableAssistantError(response)) {\n\t\t\tif (lastRetry) await callbacks?.onRetryFinished?.(false, lastRetry.attempt, response.errorMessage);\n\t\t\treturn response;\n\t\t}\n\n\t\tattempt++;\n\t\tlastRetry = { attempt, errorMessage: response.errorMessage || \"Unknown error\" };\n\t\tconst delayMs = retryDelayMs(policy!, attempt);\n\t\tawait callbacks?.onRetryScheduled?.(attempt, maxAttempts, delayMs, lastRetry.errorMessage);\n\n\t\t// Normalize aborts during retry backoff to the same AssistantMessage shape as\n\t\t// provider stream aborts, so callers do not need to care when cancellation happened.\n\t\ttry {\n\t\t\tawait sleep(delayMs, signal);\n\t\t} catch (error) {\n\t\t\tawait callbacks?.onRetryFinished?.(false, attempt, lastRetry.errorMessage);\n\t\t\tif (error instanceof RetrySleepAbortError) {\n\t\t\t\tconst { errorMessage: _errorMessage, ...rest } = response;\n\t\t\t\treturn { ...rest, stopReason: \"aborted\" };\n\t\t\t}\n\t\t\tthrow error;\n\t\t}\n\t\tawait callbacks?.onRetryAttemptStart?.();\n\t}\n}\n\n/**\n * Classifies whether a failed assistant message looks like a transient provider\n * or transport error, so callers can decide if the last assistant turn should be\n * restarted.\n *\n * This does not implement retry policy. Callers should first handle context\n * overflow separately, then apply their own retry budget, backoff, and reporting\n * before restarting the assistant turn.\n */\nexport function isRetryableAssistantError(message: AssistantMessage): boolean {\n\tif (message.stopReason !== \"error\" || !message.errorMessage) return false;\n\tconst errorMessage = message.errorMessage;\n\tif (NON_RETRYABLE_PROVIDER_LIMIT_ERROR_PATTERN.test(errorMessage)) return false;\n\treturn RETRYABLE_PROVIDER_ERROR_PATTERN.test(errorMessage);\n}\n"]}