@iislee/opencodex 2.11.0 → 2.31.0

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 (675) hide show
  1. package/AGENTS_INSTALL.md +109 -0
  2. package/README.md +114 -19
  3. package/bin/ocx.mjs +164 -36
  4. package/bin/package-main.mjs +1 -1
  5. package/gui/dist/assets/index-BF38heuV.js +104 -0
  6. package/gui/dist/assets/index-DMiI18Kv.css +1 -0
  7. package/gui/dist/index.html +2 -2
  8. package/gui/dist/provider-icons/alibaba-color.svg +1 -1
  9. package/gui/dist/provider-icons/antigravity-color.svg +1 -1
  10. package/gui/dist/provider-icons/claude-color.svg +1 -1
  11. package/gui/dist/provider-icons/cline-color.svg +16 -0
  12. package/gui/dist/provider-icons/cloudflare-ai-gateway-color.svg +1 -1
  13. package/gui/dist/provider-icons/commandcode-color.svg +1 -0
  14. package/gui/dist/provider-icons/copilot-color.svg +1 -1
  15. package/gui/dist/provider-icons/cursor-color.svg +1 -1
  16. package/gui/dist/provider-icons/deepseek-color.svg +1 -1
  17. package/gui/dist/provider-icons/firepass-color.svg +1 -1
  18. package/gui/dist/provider-icons/fireworks-color.svg +1 -1
  19. package/gui/dist/provider-icons/gemini-color.svg +1 -1
  20. package/gui/dist/provider-icons/github-copilot-color.svg +1 -1
  21. package/gui/dist/provider-icons/gitlab-duo-color.svg +1 -1
  22. package/gui/dist/provider-icons/grok.svg +1 -1
  23. package/gui/dist/provider-icons/groq-color.svg +1 -1
  24. package/gui/dist/provider-icons/huggingface-color.svg +1 -1
  25. package/gui/dist/provider-icons/kimi-color.svg +1 -1
  26. package/gui/dist/provider-icons/kiro-color.svg +2 -2
  27. package/gui/dist/provider-icons/lm-studio-color.svg +1 -1
  28. package/gui/dist/provider-icons/mistral-color.svg +1 -1
  29. package/gui/dist/provider-icons/moonshot-color.svg +1 -1
  30. package/gui/dist/provider-icons/nvidia-color.svg +1 -1
  31. package/gui/dist/provider-icons/ollama-color.svg +1 -1
  32. package/gui/dist/provider-icons/openai.svg +1 -1
  33. package/gui/dist/provider-icons/opencode.svg +2 -1
  34. package/gui/dist/provider-icons/openrouter-color.svg +1 -1
  35. package/gui/dist/provider-icons/pi.svg +2 -2
  36. package/gui/dist/provider-icons/qianfan-color.svg +1 -1
  37. package/gui/dist/provider-icons/qwen-portal-color.svg +1 -1
  38. package/gui/dist/provider-icons/vercel-ai-gateway-color.svg +1 -1
  39. package/gui/dist/provider-icons/vllm-color.svg +1 -1
  40. package/gui/dist/provider-icons/xiaomi-color.svg +1 -1
  41. package/package.json +19 -10
  42. package/src/adapters/anthropic-output-schema.ts +137 -0
  43. package/src/adapters/anthropic.ts +376 -52
  44. package/src/adapters/base.ts +54 -7
  45. package/src/adapters/client-fingerprint.ts +18 -12
  46. package/src/adapters/cline-pass-deepseek-v4-tool-replay.ts +69 -0
  47. package/src/adapters/command-code.ts +601 -0
  48. package/src/adapters/cursor/checkpoint-store.ts +303 -0
  49. package/src/adapters/cursor/cursor-errors.ts +129 -6
  50. package/src/adapters/cursor/discovery.ts +61 -4
  51. package/src/adapters/cursor/effort-map.ts +27 -3
  52. package/src/adapters/cursor/framing.ts +39 -0
  53. package/src/adapters/cursor/h2-pool.ts +123 -0
  54. package/src/adapters/cursor/http1-bidi.ts +361 -0
  55. package/src/adapters/cursor/images.ts +704 -0
  56. package/src/adapters/cursor/live-models.ts +174 -58
  57. package/src/adapters/cursor/live-transport.ts +609 -170
  58. package/src/adapters/cursor/native-exec-common.ts +23 -2
  59. package/src/adapters/cursor/native-exec-fs.ts +10 -7
  60. package/src/adapters/cursor/native-exec-network.ts +1 -1
  61. package/src/adapters/cursor/native-exec-shell.ts +0 -1
  62. package/src/adapters/cursor/native-exec.ts +101 -14
  63. package/src/adapters/cursor/protobuf-events.ts +829 -11
  64. package/src/adapters/cursor/protobuf-request.ts +383 -65
  65. package/src/adapters/cursor/request-builder.ts +237 -31
  66. package/src/adapters/cursor/tool-definitions.ts +242 -12
  67. package/src/adapters/cursor/tool-result-normalize.ts +92 -0
  68. package/src/adapters/cursor/transport.ts +22 -0
  69. package/src/adapters/cursor/types.ts +28 -1
  70. package/src/adapters/cursor.ts +133 -7
  71. package/src/adapters/google-antigravity-replay.ts +558 -34
  72. package/src/adapters/google-antigravity-wire.ts +43 -10
  73. package/src/adapters/google-http.ts +38 -10
  74. package/src/adapters/google-tool-schema.ts +80 -15
  75. package/src/adapters/google-truncation.ts +11 -0
  76. package/src/adapters/google.ts +618 -74
  77. package/src/adapters/identity.ts +39 -6
  78. package/src/adapters/kiro-errors.ts +11 -0
  79. package/src/adapters/kiro-events.ts +19 -1
  80. package/src/adapters/kiro-thinking.ts +18 -2
  81. package/src/adapters/kiro-tools.ts +10 -1
  82. package/src/adapters/kiro.ts +92 -54
  83. package/src/adapters/mimo-free.ts +17 -0
  84. package/src/adapters/openai-chat-url.ts +11 -0
  85. package/src/adapters/openai-chat.ts +1311 -336
  86. package/src/adapters/openai-responses-url.ts +16 -0
  87. package/src/adapters/openai-responses.ts +830 -56
  88. package/src/adapters/registry.ts +175 -0
  89. package/src/adapters/responses-tool-schema.ts +67 -0
  90. package/src/adapters/tool-call-id.ts +119 -0
  91. package/src/adapters/tool-catalog-nudge.ts +104 -21
  92. package/src/adapters/xai-web-search.ts +185 -0
  93. package/src/bridge.ts +428 -61
  94. package/src/chat/inbound.ts +43 -19
  95. package/src/chat/outbound.ts +82 -26
  96. package/src/claude/agents-inject.ts +32 -9
  97. package/src/claude/context-windows.ts +21 -5
  98. package/src/claude/desktop-3p.ts +243 -9
  99. package/src/claude/gateway-cache.ts +41 -4
  100. package/src/claude/inbound.ts +72 -3
  101. package/src/claude/model-info.ts +38 -15
  102. package/src/claude/outbound.ts +70 -16
  103. package/src/cli/account-api.ts +35 -1
  104. package/src/cli/account-auth.ts +33 -6
  105. package/src/cli/account-catalog-refresh.ts +14 -0
  106. package/src/cli/account-extended.ts +389 -2
  107. package/src/cli/account-main.ts +317 -0
  108. package/src/cli/account.ts +30 -6
  109. package/src/cli/agent.ts +45 -1
  110. package/src/cli/claude-agent-startup-sync.ts +73 -0
  111. package/src/cli/claude-desktop.ts +29 -4
  112. package/src/cli/claude.ts +103 -34
  113. package/src/cli/codex-log-guard-doctor.ts +103 -0
  114. package/src/cli/codex-shim-autorestore.ts +2 -0
  115. package/src/cli/codex-shim-readiness.ts +76 -0
  116. package/src/cli/combo.ts +8 -0
  117. package/src/cli/config-command.ts +74 -10
  118. package/src/cli/dispatch.ts +593 -0
  119. package/src/cli/doctor.ts +315 -43
  120. package/src/cli/ensure-desired-integrations.ts +152 -0
  121. package/src/cli/export-command.ts +46 -20
  122. package/src/cli/help.ts +20 -271
  123. package/src/cli/index.ts +378 -519
  124. package/src/cli/init.ts +4 -17
  125. package/src/cli/integrations.ts +120 -2
  126. package/src/cli/lab.ts +607 -0
  127. package/src/cli/launcher-context.ts +77 -0
  128. package/src/cli/minimax.ts +497 -0
  129. package/src/cli/models-runtime.ts +35 -2
  130. package/src/cli/models.ts +100 -14
  131. package/src/cli/observe.ts +92 -3
  132. package/src/cli/opencode.ts +4 -2
  133. package/src/cli/provider-runtime.ts +18 -1
  134. package/src/cli/provider.ts +24 -3
  135. package/src/cli/ready.ts +301 -0
  136. package/src/cli/registry.ts +437 -0
  137. package/src/cli/root.ts +86 -0
  138. package/src/cli/route-policy.ts +92 -0
  139. package/src/cli/runtime-api.ts +6 -3
  140. package/src/cli/star-prompt.ts +71 -15
  141. package/src/cli/status.ts +10 -3
  142. package/src/cli/system-restart-client.ts +146 -0
  143. package/src/cli/tray-proxy.ts +153 -6
  144. package/src/cli/v2.ts +105 -10
  145. package/src/cli.ts +1 -1
  146. package/src/clients/config-export.ts +1358 -21
  147. package/src/codex/account-label.ts +14 -1
  148. package/src/codex/account-lifecycle.ts +130 -13
  149. package/src/codex/account-namespaces.ts +49 -3
  150. package/src/codex/account-priority.ts +83 -0
  151. package/src/codex/account-store.ts +29 -2
  152. package/src/codex/account-usability.ts +25 -2
  153. package/src/codex/admission.ts +256 -0
  154. package/src/codex/affinity-debug.ts +162 -0
  155. package/src/codex/app-server-processes.ts +493 -106
  156. package/src/codex/app-server-restart-service.ts +232 -0
  157. package/src/codex/auth-api.ts +849 -242
  158. package/src/codex/auth-collision.ts +5 -3
  159. package/src/codex/auth-context.ts +345 -32
  160. package/src/codex/autostart-health.ts +8 -1
  161. package/src/codex/catalog/account-models.ts +67 -0
  162. package/src/codex/catalog/aggregation.ts +68 -10
  163. package/src/codex/catalog/bundled.ts +331 -33
  164. package/src/codex/catalog/effort.ts +121 -30
  165. package/src/codex/catalog/filesystem-evidence.ts +302 -0
  166. package/src/codex/catalog/kinds.ts +2 -0
  167. package/src/codex/catalog/metadata.ts +529 -45
  168. package/src/codex/catalog/native-models.ts +72 -0
  169. package/src/codex/catalog/parsing.ts +224 -30
  170. package/src/codex/catalog/provider-fetch.ts +1460 -134
  171. package/src/codex/catalog/sync.ts +1449 -186
  172. package/src/codex/catalog-admission.ts +199 -0
  173. package/src/codex/catalog-refresh-status.ts +105 -0
  174. package/src/codex/catalog-write-serialization.ts +242 -0
  175. package/src/codex/catalog.ts +6 -3
  176. package/src/codex/codex-write-lock.ts +384 -0
  177. package/src/codex/convergence-types.ts +614 -0
  178. package/src/codex/convergence.ts +651 -0
  179. package/src/codex/coordinator-doctor.ts +332 -0
  180. package/src/codex/custom-model-catalog-migration.ts +176 -0
  181. package/src/codex/desired-state.ts +230 -0
  182. package/src/codex/features.ts +636 -39
  183. package/src/codex/generation.ts +202 -0
  184. package/src/codex/history-job.ts +407 -0
  185. package/src/codex/history-lock.ts +242 -0
  186. package/src/codex/history-migration-guardian.ts +26 -20
  187. package/src/codex/history-provider.ts +231 -28
  188. package/src/codex/history-transition.ts +105 -0
  189. package/src/codex/history-worker.ts +220 -0
  190. package/src/codex/inject-coordination.ts +290 -0
  191. package/src/codex/inject.ts +1073 -152
  192. package/src/codex/injected-marker.ts +37 -3
  193. package/src/codex/integration-record.ts +266 -0
  194. package/src/codex/internal/catalog-writer.ts +203 -0
  195. package/src/codex/internal/history-writer.ts +80 -0
  196. package/src/codex/journal.ts +66 -4
  197. package/src/codex/log-guard/inspect.ts +506 -0
  198. package/src/codex/log-guard/lock.ts +150 -0
  199. package/src/codex/log-guard/maintenance.ts +403 -0
  200. package/src/codex/log-guard/path-safety.ts +88 -0
  201. package/src/codex/log-guard/policy.ts +44 -0
  202. package/src/codex/log-guard/processes.ts +205 -0
  203. package/src/codex/log-guard/protection.ts +489 -0
  204. package/src/codex/log-guard/sqlite-errors.ts +9 -0
  205. package/src/codex/main-account-cache.ts +24 -0
  206. package/src/codex/main-account.ts +29 -1
  207. package/src/codex/management-convergence.ts +167 -0
  208. package/src/codex/model-cache.ts +56 -10
  209. package/src/codex/model-entitlements.ts +353 -0
  210. package/src/codex/native-main-admission.ts +47 -0
  211. package/src/codex/native-main-auth-temp.ts +187 -0
  212. package/src/codex/native-main-claim.ts +178 -0
  213. package/src/codex/native-main-lock-file.ts +162 -0
  214. package/src/codex/native-main-owner.ts +329 -0
  215. package/src/codex/native-profile-api.ts +247 -0
  216. package/src/codex/native-profile-manager.ts +1531 -0
  217. package/src/codex/native-profile-processes.ts +121 -0
  218. package/src/codex/native-profile-recovery.ts +99 -0
  219. package/src/codex/native-profile-stage-store.ts +387 -0
  220. package/src/codex/native-profile-startup.ts +492 -0
  221. package/src/codex/native-profile-store.ts +855 -0
  222. package/src/codex/native-profile-types.ts +120 -0
  223. package/src/codex/native-residue.ts +682 -0
  224. package/src/codex/paths.ts +80 -1
  225. package/src/codex/plan-from-token.ts +140 -0
  226. package/src/codex/plan.ts +40 -0
  227. package/src/codex/plugins-doctor.ts +1 -1
  228. package/src/codex/pool-rotation.ts +74 -4
  229. package/src/codex/project-config-warnings.ts +20 -6
  230. package/src/codex/prompt-journal.ts +352 -0
  231. package/src/codex/prompt-layers.ts +967 -0
  232. package/src/codex/prompt-lock.ts +143 -0
  233. package/src/codex/quota-rejection.ts +298 -0
  234. package/src/codex/quota.ts +175 -13
  235. package/src/codex/refresh.ts +11 -2
  236. package/src/codex/reset-credit-recovery.ts +1044 -0
  237. package/src/codex/routing.ts +505 -94
  238. package/src/codex/runtime.ts +159 -38
  239. package/src/codex/shim.ts +1009 -28
  240. package/src/codex/subagent-model-fallback.ts +350 -35
  241. package/src/codex/sync.ts +191 -2
  242. package/src/codex/transition-state.ts +612 -0
  243. package/src/codex/upstream-host-health.ts +368 -0
  244. package/src/codex/user-identity.ts +557 -0
  245. package/src/codex/warmup.ts +187 -81
  246. package/src/codex/write-coordination.ts +114 -0
  247. package/src/combos/failover.ts +20 -0
  248. package/src/combos/index.ts +4 -0
  249. package/src/combos/request.ts +32 -0
  250. package/src/combos/types.ts +81 -9
  251. package/src/config/provider-name.ts +24 -0
  252. package/src/config.ts +1762 -140
  253. package/src/generated/compatibility-version.json +3164 -0
  254. package/src/generated/{jawcode-model-metadata.ts → model-metadata.ts} +19 -17
  255. package/src/grok/inject.ts +16 -5
  256. package/src/grok/inspect.ts +45 -0
  257. package/src/grok/sync.ts +2 -2
  258. package/src/images/loop.ts +152 -29
  259. package/src/images/plan.ts +23 -13
  260. package/src/integrations/config-io.ts +269 -0
  261. package/src/integrations/journal.ts +315 -0
  262. package/src/integrations/merge.ts +135 -0
  263. package/src/integrations/mutation-flight.ts +71 -0
  264. package/src/integrations/native/ownership-preflight.ts +202 -0
  265. package/src/integrations/omp-yaml-source.ts +358 -0
  266. package/src/integrations/owned-refresh.ts +74 -0
  267. package/src/integrations/ownership.ts +111 -0
  268. package/src/integrations/registry.ts +159 -0
  269. package/src/integrations/serialize.ts +314 -0
  270. package/src/integrations/state.ts +361 -0
  271. package/src/integrations/store.ts +103 -0
  272. package/src/integrations/writer-lock.ts +98 -0
  273. package/src/integrations/writer.ts +691 -0
  274. package/src/lab/artifacts/sanitize.ts +586 -0
  275. package/src/lab/artifacts/secure-fs.ts +475 -0
  276. package/src/lab/artifacts/store.ts +310 -0
  277. package/src/lab/automation/budgets.ts +78 -0
  278. package/src/lab/automation/config-persistence.ts +256 -0
  279. package/src/lab/automation/constants.ts +39 -0
  280. package/src/lab/automation/cooldown.ts +103 -0
  281. package/src/lab/automation/dispatch.ts +211 -0
  282. package/src/lab/automation/index.ts +13 -0
  283. package/src/lab/automation/orchestrator.ts +499 -0
  284. package/src/lab/automation/persistence.ts +512 -0
  285. package/src/lab/automation/planner.ts +371 -0
  286. package/src/lab/automation/policy.ts +136 -0
  287. package/src/lab/automation/queue.ts +191 -0
  288. package/src/lab/automation/recovery.ts +24 -0
  289. package/src/lab/automation/route-context.ts +21 -0
  290. package/src/lab/automation/run-key.ts +44 -0
  291. package/src/lab/automation/runs-query.ts +34 -0
  292. package/src/lab/automation/types.ts +160 -0
  293. package/src/lab/conformance/assertion.ts +325 -0
  294. package/src/lab/conformance/digest.ts +22 -0
  295. package/src/lab/conformance/executor.ts +741 -0
  296. package/src/lab/conformance/fixture-provider.ts +27 -0
  297. package/src/lab/conformance/fixtures/live-v1-cases.json +175 -0
  298. package/src/lab/conformance/fixtures/protocol-v1-cases.json +461 -0
  299. package/src/lab/conformance/harness-budget.ts +47 -0
  300. package/src/lab/conformance/index.ts +5 -0
  301. package/src/lab/conformance/jcs.ts +64 -0
  302. package/src/lab/conformance/json-pointer.ts +39 -0
  303. package/src/lab/conformance/manifest.ts +180 -0
  304. package/src/lab/conformance/mcp-stub.ts +179 -0
  305. package/src/lab/conformance/negative-controls.ts +164 -0
  306. package/src/lab/conformance/observation.ts +355 -0
  307. package/src/lab/conformance/runner.ts +68 -0
  308. package/src/lab/conformance/sse-normalize.ts +59 -0
  309. package/src/lab/conformance/suite-manifest.ts +78 -0
  310. package/src/lab/conformance/types.ts +214 -0
  311. package/src/lab/constants.ts +126 -0
  312. package/src/lab/digest.ts +64 -0
  313. package/src/lab/events/errors.ts +9 -0
  314. package/src/lab/events/limits.ts +117 -0
  315. package/src/lab/events/types.ts +229 -0
  316. package/src/lab/events/validate.ts +781 -0
  317. package/src/lab/fabric/constants.ts +40 -0
  318. package/src/lab/fabric/executor.ts +492 -0
  319. package/src/lab/fabric/index.ts +80 -0
  320. package/src/lab/fabric/manifest.ts +222 -0
  321. package/src/lab/fabric/observe.ts +489 -0
  322. package/src/lab/fabric/patch.ts +79 -0
  323. package/src/lab/fabric/producer-child.ts +139 -0
  324. package/src/lab/fabric/producer-isolate.ts +276 -0
  325. package/src/lab/fabric/producer-protocol.ts +61 -0
  326. package/src/lab/fabric/scratch.ts +439 -0
  327. package/src/lab/fabric/subject.ts +106 -0
  328. package/src/lab/fabric/types.ts +134 -0
  329. package/src/lab/fabric/verifier.ts +98 -0
  330. package/src/lab/index.ts +54 -0
  331. package/src/lab/ledger/artifact-refs.ts +127 -0
  332. package/src/lab/ledger/invalidation.ts +136 -0
  333. package/src/lab/ledger/purge.ts +310 -0
  334. package/src/lab/ledger/store.ts +532 -0
  335. package/src/lab/live/credential-lease.ts +53 -0
  336. package/src/lab/live/destination.ts +155 -0
  337. package/src/lab/live/executor.ts +336 -0
  338. package/src/lab/live/inert-tools.ts +56 -0
  339. package/src/lab/live/manifest.ts +85 -0
  340. package/src/lab/live/mcp-loopback.ts +57 -0
  341. package/src/lab/live/runner.ts +19 -0
  342. package/src/lab/live/sandbox.ts +61 -0
  343. package/src/lab/live/suite-manifest.ts +41 -0
  344. package/src/lab/live/transport.ts +118 -0
  345. package/src/lab/live/types.ts +197 -0
  346. package/src/lab/observe/from-conformance.ts +301 -0
  347. package/src/lab/observe/from-live.ts +117 -0
  348. package/src/lab/paths.ts +153 -0
  349. package/src/lab/projection/rebuild.ts +495 -0
  350. package/src/lab/projection/schema.ts +135 -0
  351. package/src/lab/projection/verdicts.ts +474 -0
  352. package/src/lab/projection/verification.ts +412 -0
  353. package/src/lab/public/bundle.ts +217 -0
  354. package/src/lab/public/community-authority.ts +175 -0
  355. package/src/lab/public/community-files.ts +29 -0
  356. package/src/lab/public/community.ts +479 -0
  357. package/src/lab/public/file-safety.ts +155 -0
  358. package/src/lab/public/ids.ts +26 -0
  359. package/src/lab/public/index.ts +16 -0
  360. package/src/lab/public/mutation-lock.ts +424 -0
  361. package/src/lab/public/operator.ts +353 -0
  362. package/src/lab/public/origin-purge.ts +79 -0
  363. package/src/lab/public/origin.ts +203 -0
  364. package/src/lab/public/privacy.ts +143 -0
  365. package/src/lab/public/private-file.ts +261 -0
  366. package/src/lab/public/project.ts +124 -0
  367. package/src/lab/public/purge-test-fault.ts +21 -0
  368. package/src/lab/public/purge.ts +223 -0
  369. package/src/lab/public/registry.ts +44 -0
  370. package/src/lab/public/revocation.ts +252 -0
  371. package/src/lab/public/signature.ts +243 -0
  372. package/src/lab/public/storage.ts +105 -0
  373. package/src/lab/public/strict-json.ts +206 -0
  374. package/src/lab/public/time.ts +26 -0
  375. package/src/lab/public/types.ts +172 -0
  376. package/src/lab/public/validate.ts +391 -0
  377. package/src/lab/query/catalog.ts +101 -0
  378. package/src/lab/query/connection.ts +107 -0
  379. package/src/lab/query/constants.ts +4 -0
  380. package/src/lab/query/cursor.ts +132 -0
  381. package/src/lab/query/dto-map.ts +277 -0
  382. package/src/lab/query/errors.ts +22 -0
  383. package/src/lab/query/freshness.ts +53 -0
  384. package/src/lab/query/index.ts +45 -0
  385. package/src/lab/query/latest-observation.ts +59 -0
  386. package/src/lab/query/passive-production.ts +159 -0
  387. package/src/lab/query/queries.ts +444 -0
  388. package/src/lab/query/types.ts +266 -0
  389. package/src/lab/subject/behavior-fingerprint.ts +77 -0
  390. package/src/lab/subject/installation-salt.ts +112 -0
  391. package/src/lab/subject/protocol-subject.ts +80 -0
  392. package/src/lab/subject/route-subject.ts +74 -0
  393. package/src/lib/app-owned-memory-stores.ts +22 -0
  394. package/src/lib/bounded-body.ts +153 -9
  395. package/src/lib/bun-runtime.ts +125 -12
  396. package/src/lib/bun-stream-caps.ts +13 -9
  397. package/src/lib/codex-restart-contract.ts +120 -0
  398. package/src/lib/config-ownership.ts +6 -2
  399. package/src/lib/destination-policy.ts +65 -1
  400. package/src/lib/errors.ts +44 -2
  401. package/src/lib/fabric-task-execution-authority.ts +7 -0
  402. package/src/lib/fabric-task-host.ts +29 -0
  403. package/src/lib/lab-activation.ts +223 -0
  404. package/src/lib/lab-live-execution-authority.ts +13 -0
  405. package/src/lib/lab-live-host.ts +30 -0
  406. package/src/lib/lab-live-pinned-sender.ts +56 -0
  407. package/src/lib/lab-live-route-production.ts +130 -0
  408. package/src/lib/lab-passive-linker-registration.ts +26 -0
  409. package/src/lib/local-management-attestation.ts +51 -0
  410. package/src/lib/local-management-capability.ts +100 -0
  411. package/src/lib/local-provider-reload-contract.ts +100 -0
  412. package/src/lib/optional-shutdown-hooks.ts +57 -0
  413. package/src/lib/pinned-http.ts +145 -26
  414. package/src/lib/process-control.ts +4 -1
  415. package/src/lib/provider-outbound.ts +49 -9
  416. package/src/lib/redact.ts +419 -3
  417. package/src/lib/self-launch-argv.ts +15 -0
  418. package/src/lib/server-resource-ownership.ts +71 -0
  419. package/src/lib/shadow-call.ts +35 -4
  420. package/src/lib/sse-decoder.ts +41 -0
  421. package/src/lib/state-store-registrations.ts +10 -2
  422. package/src/lib/system-restart-contract.ts +73 -0
  423. package/src/lib/token-estimate.ts +19 -2
  424. package/src/lib/tool-argument-integers.ts +202 -0
  425. package/src/lib/translator-budget.ts +44 -0
  426. package/src/lib/upstream-http-version.ts +57 -0
  427. package/src/lib/upstream-reachability.ts +95 -0
  428. package/src/lib/upstream-retry.ts +156 -3
  429. package/src/lib/windows-atomic-replace.ts +155 -0
  430. package/src/lib/windows-elevation.ts +70 -2
  431. package/src/lib/windows-secret-acl.ts +409 -69
  432. package/src/lib/windows-service-wrappers.ts +72 -0
  433. package/src/lib/windows-text.ts +106 -0
  434. package/src/lib/windows-user-principal.ts +341 -0
  435. package/src/lib/winsw.ts +33 -5
  436. package/src/oauth/account-import/google-antigravity-adapter.ts +74 -0
  437. package/src/oauth/account-import/index.ts +15 -0
  438. package/src/oauth/account-import/parser.ts +83 -0
  439. package/src/oauth/account-import/registry.ts +18 -0
  440. package/src/oauth/account-import/service.ts +75 -0
  441. package/src/oauth/account-import/types.ts +91 -0
  442. package/src/oauth/anthropic.ts +12 -1
  443. package/src/oauth/callback-server.ts +8 -2
  444. package/src/oauth/chatgpt.ts +12 -1
  445. package/src/oauth/command-code.ts +239 -0
  446. package/src/oauth/cursor.ts +46 -5
  447. package/src/oauth/google-antigravity.ts +35 -3
  448. package/src/oauth/health.ts +20 -12
  449. package/src/oauth/index.ts +398 -66
  450. package/src/oauth/key-providers.ts +16 -0
  451. package/src/oauth/kimi.ts +16 -2
  452. package/src/oauth/kiro.ts +50 -6
  453. package/src/oauth/local-token-detect.ts +11 -2
  454. package/src/oauth/log.ts +3 -1
  455. package/src/oauth/login-cli.ts +88 -28
  456. package/src/oauth/nous.ts +798 -0
  457. package/src/oauth/store.ts +119 -21
  458. package/src/oauth/token-guardian.ts +9 -3
  459. package/src/pi/models.ts +2 -2
  460. package/src/providers/alibaba-region-migration.ts +1 -1
  461. package/src/providers/antigravity-models.ts +521 -31
  462. package/src/providers/base-url-choices.ts +10 -0
  463. package/src/providers/codex-capacity.ts +292 -0
  464. package/src/providers/command-code-efforts.ts +144 -0
  465. package/src/providers/context-cap.ts +22 -5
  466. package/src/providers/cursor-pool.ts +72 -0
  467. package/src/providers/derive.ts +253 -6
  468. package/src/providers/fastwire.ts +501 -0
  469. package/src/providers/free-directory.ts +10 -7
  470. package/src/providers/google-vertex-location.ts +14 -0
  471. package/src/providers/key-failover.ts +71 -3
  472. package/src/providers/label.ts +1 -1
  473. package/src/providers/model-discovery-limits.ts +16 -0
  474. package/src/providers/model-discovery.ts +115 -22
  475. package/src/providers/model-rename-migration.ts +255 -0
  476. package/src/providers/model-rename-startup.ts +28 -0
  477. package/src/providers/openai-sidecar.ts +72 -4
  478. package/src/providers/openai-tier-startup.ts +31 -2
  479. package/src/providers/openai-tiers.ts +119 -4
  480. package/src/providers/openai-virtual-models.ts +1 -0
  481. package/src/providers/opencode-zen-rate-limit.ts +102 -0
  482. package/src/providers/provider-id-rewrite.ts +29 -0
  483. package/src/providers/quota.ts +1319 -38
  484. package/src/providers/registry.ts +1429 -111
  485. package/src/providers/request-pacing.ts +310 -0
  486. package/src/providers/service-tier.ts +277 -0
  487. package/src/providers/slug-codec.ts +42 -6
  488. package/src/providers/static-model-discovery.ts +86 -0
  489. package/src/providers/xai-responses-opt-in.ts +15 -0
  490. package/src/providers/xai-transport.ts +11 -4
  491. package/src/reasoning-effort.ts +49 -1
  492. package/src/responses/compaction.ts +26 -1
  493. package/src/responses/custom-tool-compat.ts +266 -0
  494. package/src/responses/hosted-tool-policy.ts +9 -0
  495. package/src/responses/namespace-tool-compat.ts +355 -0
  496. package/src/responses/parser.ts +220 -38
  497. package/src/responses/provider-continuation.ts +98 -0
  498. package/src/responses/provider-opaque-metadata.ts +73 -0
  499. package/src/responses/reasoning-envelope.ts +9 -1
  500. package/src/responses/reasoning-replay-cache.ts +426 -0
  501. package/src/responses/schema.ts +7 -1
  502. package/src/responses/spill-store.ts +75 -10
  503. package/src/responses/state.ts +565 -27
  504. package/src/responses/thought-signature-replay.ts +347 -0
  505. package/src/responses/tool-search-compat.ts +301 -0
  506. package/src/responses/truncated-stop-reason.ts +60 -0
  507. package/src/router.ts +366 -30
  508. package/src/routing/analytics.ts +378 -0
  509. package/src/routing/capability.ts +244 -0
  510. package/src/routing/compatibility/assemble.ts +73 -0
  511. package/src/routing/compatibility/behavior.ts +278 -0
  512. package/src/routing/compatibility/catalog.ts +99 -0
  513. package/src/routing/compatibility/endpoint.ts +52 -0
  514. package/src/routing/compatibility/lab-evidence-provider.ts +130 -0
  515. package/src/routing/compatibility/policy.ts +181 -0
  516. package/src/routing/compatibility/provider-slot.ts +56 -0
  517. package/src/routing/compatibility/reader.ts +110 -0
  518. package/src/routing/compatibility/subject.ts +191 -0
  519. package/src/routing/compatibility/types.ts +64 -0
  520. package/src/routing/compatibility/version.ts +104 -0
  521. package/src/routing/cost.ts +77 -0
  522. package/src/routing/evaluator.ts +495 -0
  523. package/src/routing/health.ts +412 -0
  524. package/src/routing/history/cursor.ts +43 -0
  525. package/src/routing/history/indexer.ts +605 -0
  526. package/src/routing/history/schema.ts +72 -0
  527. package/src/routing/profile-namespace.ts +15 -0
  528. package/src/routing/profile.ts +547 -0
  529. package/src/routing/quota.ts +145 -0
  530. package/src/routing/request-evidence.ts +45 -0
  531. package/src/routing/trace.ts +776 -0
  532. package/src/server/adapter-resolve.ts +2 -29
  533. package/src/server/auth-cors.ts +267 -46
  534. package/src/server/background-lifecycle.ts +182 -0
  535. package/src/server/chat-completions.ts +130 -56
  536. package/src/server/chat-native-sse.ts +331 -0
  537. package/src/server/chat-native.ts +426 -0
  538. package/src/server/claude-messages.ts +159 -43
  539. package/src/server/direct-local-http.ts +347 -0
  540. package/src/server/effort-policy.ts +18 -0
  541. package/src/server/github-copilot-responses-repair.ts +338 -0
  542. package/src/server/gui-static.ts +39 -10
  543. package/src/server/images.ts +94 -12
  544. package/src/server/index.ts +865 -181
  545. package/src/server/lifecycle.ts +284 -13
  546. package/src/server/live.ts +136 -17
  547. package/src/server/local-management-read-client.ts +90 -0
  548. package/src/server/local-provider-reload-client.ts +137 -0
  549. package/src/server/management/agent-settings-routes.ts +398 -103
  550. package/src/server/management/api-key-usage.ts +31 -5
  551. package/src/server/management/body.ts +6 -0
  552. package/src/server/management/combo-routes.ts +62 -24
  553. package/src/server/management/config-routes.ts +464 -51
  554. package/src/server/management/context.ts +80 -2
  555. package/src/server/management/integration-routes.ts +498 -0
  556. package/src/server/management/lab-automation-routes.ts +206 -0
  557. package/src/server/management/lab-routes.ts +563 -0
  558. package/src/server/management/logs-usage-routes.ts +101 -32
  559. package/src/server/management/model-routes.ts +189 -131
  560. package/src/server/management/model-rows.ts +163 -0
  561. package/src/server/management/native-integration-routes.ts +769 -0
  562. package/src/server/management/oauth-account-routes.ts +80 -4
  563. package/src/server/management/provider-capability-config.ts +48 -0
  564. package/src/server/management/provider-routes.ts +764 -157
  565. package/src/server/management/request-history-routes.ts +191 -0
  566. package/src/server/management/routing-analytics-routes.ts +74 -0
  567. package/src/server/management/routing-profile-routes.ts +380 -0
  568. package/src/server/management/shared.ts +27 -11
  569. package/src/server/management/sidebar-routes.ts +47 -31
  570. package/src/server/management/storage-log-guard-routes.ts +186 -0
  571. package/src/server/management/sync-response.ts +69 -0
  572. package/src/server/management/system-restart.ts +288 -32
  573. package/src/server/management/system-routes.ts +77 -0
  574. package/src/server/management/usage-summary-cache.ts +9 -1
  575. package/src/server/management/vision-sidecar-options.ts +167 -0
  576. package/src/server/management/web-search-sidecar-options.ts +120 -0
  577. package/src/server/management-api.ts +115 -14
  578. package/src/server/management-auth.ts +220 -5
  579. package/src/server/passive-route-linker.ts +66 -0
  580. package/src/server/ports.ts +41 -1
  581. package/src/server/proxy-liveness.ts +132 -5
  582. package/src/server/readiness.ts +99 -0
  583. package/src/server/relay-eager.ts +82 -42
  584. package/src/server/relay.ts +236 -76
  585. package/src/server/request-decompress.ts +113 -6
  586. package/src/server/request-log.ts +235 -22
  587. package/src/server/responses/agent-task-recovery-cache.ts +143 -0
  588. package/src/server/responses/agent-task-recovery.ts +465 -0
  589. package/src/server/responses/collaboration.ts +204 -35
  590. package/src/server/responses/compact.ts +442 -55
  591. package/src/server/responses/core.ts +2872 -331
  592. package/src/server/responses/empty-completion-guard.ts +276 -0
  593. package/src/server/responses/encrypted-payload.ts +62 -39
  594. package/src/server/responses/fetch-helpers.ts +79 -4
  595. package/src/server/responses/input-admission.ts +185 -0
  596. package/src/server/responses/pacing-overload.ts +13 -0
  597. package/src/server/responses/policy-fallback.ts +178 -0
  598. package/src/server/responses/responses-field-backfill.ts +251 -0
  599. package/src/server/responses/terminal-guard.ts +26 -5
  600. package/src/server/responses/upstream-error.ts +5 -0
  601. package/src/server/responses/ws-upstream.ts +308 -0
  602. package/src/server/responses-custom-tool-repair.ts +282 -0
  603. package/src/server/responses-item-id-repair.ts +54 -6
  604. package/src/server/responses-json-events.ts +90 -0
  605. package/src/server/responses-model-rewrite.ts +29 -0
  606. package/src/server/responses-reasoning-summary-rewrite.ts +178 -0
  607. package/src/server/responses-snapshot-repair.ts +621 -0
  608. package/src/server/responses-terminal-repair.ts +342 -0
  609. package/src/server/responses-tool-search-repair.ts +267 -0
  610. package/src/server/responses-undeclared-tool-guard.ts +153 -0
  611. package/src/server/responses.ts +18 -2
  612. package/src/server/search.ts +78 -13
  613. package/src/server/sse-frame-buffer.ts +292 -0
  614. package/src/server/sse-payload-rewrite.ts +110 -22
  615. package/src/server/startup-action-control.ts +8 -1
  616. package/src/server/startup-health-cache.ts +19 -1
  617. package/src/server/system-env.ts +80 -9
  618. package/src/server/ws-bridge.ts +39 -38
  619. package/src/service-manager-probe.ts +892 -0
  620. package/src/service.ts +1111 -90
  621. package/src/sidecar/auth.ts +92 -0
  622. package/src/sidecar/candidates.ts +83 -0
  623. package/src/storage/cleanup.ts +2 -2
  624. package/src/storage/scanner.ts +1 -1
  625. package/src/storage/worker-lifecycle.ts +14 -14
  626. package/src/tray/windows-tray.ps1 +83 -9
  627. package/src/tray/windows.ts +43 -16
  628. package/src/types/accounts.ts +37 -0
  629. package/src/types/config.ts +845 -0
  630. package/src/types/provider.ts +545 -0
  631. package/src/types/request.ts +384 -0
  632. package/src/types/tools.ts +131 -0
  633. package/src/types/wire.ts +80 -0
  634. package/src/types.ts +104 -1236
  635. package/src/update/index.ts +32 -19
  636. package/src/update/job.ts +442 -67
  637. package/src/update/notify.ts +12 -6
  638. package/src/update/npm-cache-preflight.d.mts +47 -0
  639. package/src/update/npm-cache-preflight.mjs +201 -0
  640. package/src/update/transactional-install.d.mts +22 -0
  641. package/src/update/transactional-install.mjs +259 -0
  642. package/src/usage/cost.ts +0 -0
  643. package/src/usage/expected-prices.ts +268 -16
  644. package/src/usage/log.ts +606 -41
  645. package/src/usage/summary.ts +177 -9
  646. package/src/usage/user-cost-overlay-reconciler.ts +313 -0
  647. package/src/usage/user-cost-overlays.ts +314 -0
  648. package/src/vision/anthropic-describe.ts +10 -6
  649. package/src/vision/backends.ts +97 -0
  650. package/src/vision/describe.ts +9 -3
  651. package/src/vision/eligibility.ts +250 -0
  652. package/src/vision/index.ts +238 -24
  653. package/src/vision/reasoning.ts +55 -0
  654. package/src/vision/routed-describe.ts +175 -0
  655. package/src/vision/timeout-bounds.ts +9 -0
  656. package/src/web-search/anthropic-executor.ts +13 -7
  657. package/src/web-search/backends.ts +108 -0
  658. package/src/web-search/exa-executor.ts +88 -0
  659. package/src/web-search/executor.ts +11 -3
  660. package/src/web-search/gemini-executor.ts +141 -0
  661. package/src/web-search/index.ts +150 -15
  662. package/src/web-search/loop.ts +279 -50
  663. package/src/web-search/parse.ts +125 -30
  664. package/src/web-search/sources.ts +60 -0
  665. package/src/web-search/xai-executor.ts +219 -0
  666. package/gui/dist/assets/index-DTpMHS4F.js +0 -67
  667. package/gui/dist/assets/index-ZNVDE3C7.css +0 -1
  668. package/gui/dist/provider-icons/antigravity.svg +0 -1
  669. package/gui/dist/provider-icons/claude.svg +0 -1
  670. package/gui/dist/provider-icons/copilot.svg +0 -1
  671. package/gui/dist/provider-icons/cursor.svg +0 -2
  672. package/gui/dist/provider-icons/gemini.svg +0 -1
  673. package/gui/dist/provider-icons/grok-color.svg +0 -1
  674. package/gui/dist/provider-icons/kiro.svg +0 -14
  675. package/src/cli/internal-dispatch.ts +0 -20
@@ -1,19 +1,24 @@
1
- import type { CodexAccountMode, OcxProviderConfig } from "../types";
1
+ import type { CodexAccountMode, FastWire, OcxProviderConfig } from "../types";
2
+ import { fastWireDeclarationError } from "./fastwire";
2
3
  import { KIRO_MODELS, KIRO_MODEL_CONTEXT_WINDOWS, KIRO_MODEL_REASONING_EFFORTS } from "./kiro-models";
3
- import { ANTIGRAVITY_MODELS, ANTIGRAVITY_MODEL_CONTEXT_WINDOWS, ANTIGRAVITY_MODEL_EFFORTS } from "./antigravity-models";
4
+ import { ANTIGRAVITY_MODELS, ANTIGRAVITY_MODEL_CONTEXT_WINDOWS, ANTIGRAVITY_MODEL_EFFORTS, ANTIGRAVITY_MODEL_INPUT_MODALITIES } from "./antigravity-models";
4
5
  import type { ProviderBaseUrlChoice } from "./base-url-choices";
5
6
  import {
6
7
  QWEN_CLOUD_BASE_URL_CHOICES, QWEN_CLOUD_TOKEN_PLAN_BASE_URL,
7
8
  ALIBABA_INTL_BASE_URL_CHOICES, ALIBABA_INTL_TOKEN_PLAN_BASE_URL,
8
9
  ALIBABA_CODING_BASE_URL_CHOICES, ALIBABA_CODING_INTL_BASE_URL,
10
+ MOONSHOT_BASE_URL_CHOICES, MOONSHOT_INTL_BASE_URL,
9
11
  } from "./base-url-choices";
10
12
  import {
13
+ CURSOR_NO_VISION_MODELS,
11
14
  CURSOR_STATIC_MODELS,
12
15
  cursorModelContextWindows,
13
16
  cursorModelIds,
14
17
  cursorModelInputModalities,
15
18
  cursorModelReasoningEfforts,
16
19
  } from "../adapters/cursor/discovery";
20
+ import { COMMAND_CODE_MODEL_REASONING_EFFORTS } from "./command-code-efforts";
21
+ import { isCanonicalOpenRouterTarget } from "./openrouter-routing";
17
22
 
18
23
  export type ProviderAuthKind = "forward" | "oauth" | "key" | "local";
19
24
  export type MetadataModelIdNormalize = "case-insensitive";
@@ -27,9 +32,20 @@ export type InboundWire = "responses" | "chat" | "anthropic";
27
32
 
28
33
  /**
29
34
  * A per-model wire default: a bare string applies to every inbound, while the object
30
- * form applies only to the listed inbound protocols.
35
+ * form may scope the default to listed inbound protocols and authentication modes.
31
36
  */
32
- export type ModelWireDefault = string | { wire: string; inbound: readonly InboundWire[] };
37
+ export type ModelWireDefault = string | {
38
+ wire: string;
39
+ inbound: readonly InboundWire[];
40
+ authModes?: readonly ProviderAuthKind[];
41
+ /** Whether this registry-selected route may relay a caller-owned service_tier. */
42
+ forwardCallerServiceTier?: boolean;
43
+ };
44
+
45
+ export interface ResponsesTerminalRepairPolicy {
46
+ /** Quiet time after a structurally complete output graph before synthesizing completion. */
47
+ graceMs: number;
48
+ }
33
49
 
34
50
  export type ProviderModelDiscoveryScalar = string | number | boolean;
35
51
 
@@ -73,6 +89,11 @@ interface ProviderModelDiscoverySharedSpec {
73
89
  maxResponseBytes?: number;
74
90
  /** Optional lower raw-row ceiling; the process-wide hard ceiling still wins. */
75
91
  maxModels?: number;
92
+ /**
93
+ * If a valid extracted id starts with this prefix, strip it and re-validate the remainder.
94
+ * Empty/invalid remainders skip that row only.
95
+ */
96
+ stripIdPrefix?: string;
76
97
  }
77
98
 
78
99
  type ProviderModelDiscoveryLocation =
@@ -110,6 +131,11 @@ export interface ProviderRegistryEntry {
110
131
  allowKeyAuthOverride?: boolean;
111
132
  allowPrivateNetworkByDefault?: boolean;
112
133
  keyOptional?: boolean;
134
+ /**
135
+ * Registry-only key-login policy for public model catalogs that cannot authenticate a key.
136
+ * The dashboard flow then reports the key as unverifiable instead of a false positive.
137
+ */
138
+ apiKeyValidation?: "unknown";
113
139
  /**
114
140
  * Free-tier pricing (no paid subscription required). Distinct from `keyOptional`:
115
141
  * free tiers may still require an API key (e.g. NVIDIA NIM free credits).
@@ -148,6 +174,29 @@ export interface ProviderRegistryEntry {
148
174
  * of paying a translation hop.
149
175
  */
150
176
  modelWireDefaults?: Record<string, ModelWireDefault>;
177
+ /** Explicit Fast wire declaration; absence derives from the final model adapter. */
178
+ fastWire?: FastWire | null;
179
+ /**
180
+ * Registry-only per-model override for the upstream request shape used behind a
181
+ * Codex Responses WebSocket turn. `false` keeps the client-facing WebSocket but
182
+ * asks the upstream Responses endpoint for bounded JSON, which the bridge then
183
+ * reframes as Responses events. Use only for upstreams whose streaming response
184
+ * can omit or indefinitely delay the terminal event.
185
+ */
186
+ modelResponsesUpstreamStreaming?: Record<string, boolean>;
187
+ /** Registry-only repair for a model whose native Responses stream may omit its terminal. */
188
+ modelResponsesTerminalRepair?: Record<string, ResponsesTerminalRepairPolicy>;
189
+ /**
190
+ * Registry-only client-facing item-id repair policy (#938), filled onto the
191
+ * runtime provider only when the user has no explicit policy (derive.ts);
192
+ * never seeded into saved config.
193
+ */
194
+ responsesItemIdRepair?: {
195
+ message?: string[];
196
+ reasoning?: string[];
197
+ repairMissingTerminalIds?: boolean;
198
+ repairInvalidIds?: boolean;
199
+ };
151
200
  /**
152
201
  * Responses-API resource path for providers whose route is not `/v1/responses`.
153
202
  * Unlike `modelWireDefaults` above, this IS seeded into saved config: it describes
@@ -161,6 +210,46 @@ export interface ProviderRegistryEntry {
161
210
  * replay miss are repaired rather than forwarded.
162
211
  */
163
212
  statelessResponses?: boolean;
213
+ /**
214
+ * Responses parser requires an unambiguous call batch and its matched result batch
215
+ * to stay contiguous. This is seeded/backfilled like other fixed wire capabilities.
216
+ */
217
+ requiresAdjacentResponsesToolResults?: boolean;
218
+ /**
219
+ * Registry default for the provider's `service_tier` support; see
220
+ * `OcxProviderConfig.supportsServiceTier`. Registry-only: backfilled (never
221
+ * overriding) at enrich/route time and deliberately NOT seeded into saved
222
+ * config, so an explicit user value stays distinguishable from the default
223
+ * (and the canonical openai seed comparison keeps its exact key set).
224
+ */
225
+ supportsServiceTier?: boolean;
226
+ /** Registry default for OpenAI extended hosted web_search field support. */
227
+ supportsOpenAiWebSearchToolFields?: boolean;
228
+ /** Registry default for native Responses custom-tool support. */
229
+ supportsResponsesCustomTools?: boolean;
230
+ /** Registry default for exact model service-tier capability; explicit config keys win. */
231
+ modelSupportsServiceTier?: Record<string, boolean>;
232
+ /**
233
+ * Registry-only service-tier defaults for an OAuth preset's explicit API-key transport.
234
+ * Applied only when `allowKeyAuthOverride` is true and the captured effective auth transport
235
+ * is key-based. Explicit provider config still wins field-by-field, including `false`.
236
+ */
237
+ keyAuthServiceTier?: {
238
+ supportsServiceTier?: boolean;
239
+ modelSupportsServiceTier?: Record<string, boolean>;
240
+ chatServiceTier?: boolean;
241
+ };
242
+ /** Provider-specific copy for the Codex catalog's Fast tier. */
243
+ fastTierDescription?: string;
244
+ /**
245
+ * Registry-only destination guard for `modelSupportsServiceTier`. This scopes vendor evidence
246
+ * without changing provider ownership, routing, authentication, or config validation.
247
+ */
248
+ modelServiceTierCapabilityBaseUrlGuard?: (baseUrl: string) => boolean;
249
+ /** Registry default for plaintext reasoning replay; see `OcxProviderConfig.preserveResponsesReasoningContent`. Registry-only like `supportsServiceTier`. */
250
+ preserveResponsesReasoningContent?: boolean;
251
+ /** Registry defaults for per-model Codex reasoning propagation; explicit user keys win during enrichment. */
252
+ modelSupportsReasoningSummaries?: Record<string, boolean>;
164
253
  modelDiscovery?: ProviderModelDiscoverySpec;
165
254
  contextWindow?: number;
166
255
  modelContextWindows?: Record<string, number>;
@@ -172,6 +261,13 @@ export interface ProviderRegistryEntry {
172
261
  modelDefaultReasoningEfforts?: Record<string, string>;
173
262
  reasoningEffortMap?: Record<string, string>;
174
263
  modelReasoningEffortMap?: Record<string, Record<string, string>>;
264
+ /**
265
+ * Registry-authoritative models that send OpenAI's direct `reasoning_effort` field.
266
+ * Runtime enrichment uses this to repair stale preset metadata that still classifies a model
267
+ * as a thinking-budget/toggle model. This is registry-only and is never persisted as user config.
268
+ */
269
+ directReasoningEffortModels?: string[];
270
+ reasoningWireFormat?: OcxProviderConfig["reasoningWireFormat"];
175
271
  noVisionModels?: string[];
176
272
  noReasoningModels?: string[];
177
273
  noTemperatureModels?: string[];
@@ -181,8 +277,17 @@ export interface ProviderRegistryEntry {
181
277
  parallelToolCalls?: boolean;
182
278
  /** Opt this provider into forwarding prompt_cache_key (OpenAI-specific; strict backends reject it). */
183
279
  promptCacheKey?: boolean;
280
+ /**
281
+ * Opt-in: forward `service_tier` on the `/chat/completions` wire. Same hazard as
282
+ * `promptCacheKey` — an OpenAI-specific extension that strict gateways reject. Distinct from
283
+ * `supportsServiceTier`, which governs the Responses wire.
284
+ */
285
+ chatServiceTier?: boolean;
286
+ /** OpenAI Chat EOF policy for gateways that omit terminal frames after complete tool calls. */
287
+ openaiChatEofTolerance?: boolean;
184
288
  autoToolChoiceOnlyModels?: string[];
185
289
  preserveReasoningContentModels?: string[];
290
+ requiresReasoningPlaceholderModels?: string[];
186
291
  reasoningSplitModels?: string[];
187
292
  thinkingToggleModels?: string[];
188
293
  thinkingBudgetModels?: string[];
@@ -203,9 +308,9 @@ export type ProviderConfigSeed = Pick<
203
308
  "adapter" | "baseUrl" | "apiKeyTransport" | "responsesPath" | "authMode" | "keyOptional" | "freeTier" | "modelSuffixBracketStrip" | "defaultModel" | "models"
204
309
  | "liveModels" | "contextWindow" | "modelContextWindows" | "modelInputModalities"
205
310
  | "modelMaxInputTokens" | "defaultMaxOutputTokens" | "modelMaxOutputTokens"
206
- | "reasoningEfforts" | "modelReasoningEfforts" | "modelDefaultReasoningEfforts" | "reasoningEffortMap" | "modelReasoningEffortMap"
311
+ | "reasoningEfforts" | "modelReasoningEfforts" | "modelDefaultReasoningEfforts" | "reasoningEffortMap" | "modelReasoningEffortMap" | "reasoningWireFormat"
207
312
  | "noVisionModels" | "noReasoningModels" | "noTemperatureModels" | "noTopPModels" | "noPenaltyModels"
208
- | "autoToolChoiceOnlyModels" | "preserveReasoningContentModels" | "reasoningSplitModels" | "thinkingToggleModels" | "thinkingBudgetModels" | "escapeBuiltinToolNames"
313
+ | "autoToolChoiceOnlyModels" | "preserveReasoningContentModels" | "requiresReasoningPlaceholderModels" | "reasoningSplitModels" | "thinkingToggleModels" | "thinkingBudgetModels" | "escapeBuiltinToolNames" | "openaiChatEofTolerance"
209
314
  | "googleMode" | "project" | "location" | "headers"
210
315
  >;
211
316
 
@@ -214,10 +319,32 @@ export type ProviderConfigSeed = Pick<
214
319
  // 260710 context refresh: Tier-2 evidence in
215
320
  // devlog/_plan/260710_provider_hardening/001_research_frontier.md.
216
321
  const ANTHROPIC_MODELS = ["claude-fable-5", "claude-sonnet-5", "claude-opus-5", "claude-opus-4-8", "claude-opus-4-7", "claude-opus-4-6", "claude-sonnet-4-6", "claude-haiku-4-5"];
217
- const ANTHROPIC_MODEL_CONTEXT_WINDOWS: Record<string, number> = { "claude-sonnet-5": 1_000_000, "claude-fable-5": 1_000_000, "claude-opus-5": 1_000_000, "claude-opus-4-8": 1_000_000, "claude-haiku-4-5": 200_000 };
322
+ const ANTHROPIC_MODEL_CONTEXT_WINDOWS: Record<string, number> = { "claude-sonnet-5": 1_000_000, "claude-fable-5": 1_000_000, "claude-opus-5": 1_000_000, "claude-opus-4-8": 1_000_000, "claude-opus-4-7": 1_000_000, "claude-opus-4-6": 1_000_000, "claude-sonnet-4-6": 1_000_000, "claude-haiku-4-5": 200_000 };
218
323
 
324
+ // 260814 GLM-5.3 is registered pre-emptively alongside 5.2 everywhere 5.2 appears. Z.AI's
325
+ // devpack "How to Switch Models" page (docs.z.ai/devpack/latest-model) lists glm-5.3 and
326
+ // glm-5.3[1m] as Coding Plan ids on the unchanged endpoints; the capability and pricing
327
+ // tables were not published yet, so every 5.3 row mirrors its 5.2 sibling until they settle.
328
+ // The non-Z.AI providers below are speculative on purpose: they carry 5.2 today and are
329
+ // expected to pick 5.3 up on their usual lag. Providers whose live /v1/models discovery is
330
+ // enabled self-correct on the next successful fetch; static ones need a follow-up refresh.
331
+ const ZAI_GLM_53_MODELS = ["glm-5.3", "glm-5.3[1m]"];
219
332
  const ZAI_GLM_52_MODELS = ["glm-5.2", "glm-5.2[1m]"];
333
+ const ZAI_GLM_5X_MODELS = [...ZAI_GLM_53_MODELS, ...ZAI_GLM_52_MODELS];
220
334
  const ZAI_GLM_52_REASONING_EFFORTS = ["low", "medium", "high", "xhigh", "max"];
335
+ /**
336
+ * GLM-5.3 does NOT share 5.2's five-tier ladder. docs.z.ai/devpack/latest-model folds every
337
+ * incoming effort into three effective tiers — low/minimal/light -> low, medium/high -> high,
338
+ * xhigh/max/ultra -> max — with max as both the default and the unknown-value fallback.
339
+ * Advertising five levels would publish two picker rows that are indistinguishable on the wire,
340
+ * so only the effective tiers are exposed (same treatment Cursor and Baseten already give GLM).
341
+ */
342
+ const ZAI_GLM_53_REASONING_EFFORTS = ["low", "high", "max"];
343
+ /** Per-model ladders for the Coding Plan rows: 5.3 gets its three effective tiers, 5.2 keeps five. */
344
+ const ZAI_GLM_5X_REASONING_EFFORTS: Record<string, string[]> = {
345
+ ...Object.fromEntries(ZAI_GLM_53_MODELS.map(id => [id, ZAI_GLM_53_REASONING_EFFORTS])),
346
+ ...Object.fromEntries(ZAI_GLM_52_MODELS.map(id => [id, ZAI_GLM_52_REASONING_EFFORTS])),
347
+ };
221
348
  // 260710 MiniMax models and context windows: Tier-2 evidence in
222
349
  // devlog/_plan/260710_provider_hardening/002_research_cn.md.
223
350
  const MINIMAX_MODELS = [
@@ -243,12 +370,6 @@ const MINIMAX_M3_REASONING_EFFORT_MAP: Record<string, string> = {
243
370
  const OPENAI_GPT56_MODELS = ["gpt-5.6", "gpt-5.6-sol", "gpt-5.6-terra", "gpt-5.6-luna"];
244
371
  const OPENAI_GPT56_PRO_MODELS = ["gpt-5.6-sol-pro", "gpt-5.6-terra-pro", "gpt-5.6-luna-pro"];
245
372
  const OPENAI_API_GPT56_CONTEXT_WINDOW = 1_050_000;
246
- const OPENAI_CODEX_GPT56_CONTEXT_WINDOW = 372_000;
247
- const OPENAI_GPT56_CONTEXT_WINDOWS = {
248
- "gpt-5.6-sol": OPENAI_CODEX_GPT56_CONTEXT_WINDOW,
249
- "gpt-5.6-terra": OPENAI_CODEX_GPT56_CONTEXT_WINDOW,
250
- "gpt-5.6-luna": OPENAI_CODEX_GPT56_CONTEXT_WINDOW,
251
- };
252
373
  const OPENAI_API_GPT56_CONTEXT_WINDOWS: Record<string, number> = {
253
374
  ...Object.fromEntries([...OPENAI_GPT56_MODELS, ...OPENAI_GPT56_PRO_MODELS].map(id => [id, OPENAI_API_GPT56_CONTEXT_WINDOW])),
254
375
  "gpt-5.5": OPENAI_API_GPT56_CONTEXT_WINDOW,
@@ -263,6 +384,37 @@ const OPENAI_API_GPT56_VIRTUAL_MODELS: Record<string, { wireModelId: string; rea
263
384
  "gpt-5.6-luna-pro": { wireModelId: "gpt-5.6-luna", reasoningMode: "pro" },
264
385
  };
265
386
  const OPENAI_API_GPT56_REASONING_EFFORTS = ["low", "medium", "high", "xhigh", "max"];
387
+ /**
388
+ * Daybreak program aliases. These `-latest` ids are the stable contract: OpenAI repoints
389
+ * them at newer snapshots over time (red -> gpt-5.6-cyber, blue -> gpt-5.6-sol as of
390
+ * 2026-08-11), so registering the ALIAS inherits future model swaps while a pinned
391
+ * snapshot id would silently go stale. Snapshot ids are deliberately absent here.
392
+ * Responses-only per both published endpoint tables (`v1/chat/completions` is marked
393
+ * Not supported) — never add these to a chat-completions provider. Access needs separate
394
+ * Daybreak approval and provisioning, so neither is ever a default.
395
+ * Verified 2026-08-11: developers.openai.com/api/docs/models/daybreak-red-latest.md
396
+ * and .../daybreak-blue-latest.md
397
+ */
398
+ const OPENAI_DAYBREAK_MODELS = ["daybreak-red-latest", "daybreak-blue-latest"];
399
+ const OPENAI_DAYBREAK_CONTEXT_WINDOWS: Record<string, number> = {
400
+ "daybreak-red-latest": 400_000,
401
+ "daybreak-blue-latest": 1_050_000,
402
+ };
403
+ const OPENAI_DAYBREAK_MAX_INPUT_TOKENS: Record<string, number> = {
404
+ "daybreak-red-latest": 272_000,
405
+ "daybreak-blue-latest": 922_000,
406
+ };
407
+ /**
408
+ * Neither Daybreak page publishes a reasoning-effort ladder. An explicit empty array means
409
+ * "expose no effort control"; OMITTING the key would instead fall back to the full routed
410
+ * ladder (`configuredReasoningEfforts` returns undefined -> `applyReasoningLevels` uses
411
+ * ROUTED_REASONING_LEVELS), which would advertise efforts the models never documented.
412
+ * `noReasoningModels` is wrong here: both pages document reasoning-token support, so these
413
+ * are reasoning models with no *selectable* ladder.
414
+ */
415
+ const OPENAI_DAYBREAK_REASONING_EFFORTS: Record<string, string[]> = Object.fromEntries(
416
+ OPENAI_DAYBREAK_MODELS.map(id => [id, [] as string[]]),
417
+ );
266
418
  const OPENROUTER_GPT56_MODELS = OPENAI_GPT56_MODELS.map(id => `openai/${id}`);
267
419
  // OpenRouter's live /endpoints routes report 1,050,000; keep this separate from the
268
420
  // unverified OpenAI API-key seed. Evidence: devlog/_plan/260710_provider_hardening/003_research_aggregators.md.
@@ -298,47 +450,137 @@ const OPENCODE_GO_THINKING_TOGGLE_MODELS = [
298
450
  * images through the proxy's vision sidecar (src/codex/catalog/provider-fetch.ts), a claim nobody
299
451
  * has verified for BigModel-hosted GLM.
300
452
  */
301
- const ZHIPU_BIGMODEL_TEXT_MODELS = ["glm-4.6", "glm-4.7", "glm-4.7-flash", "glm-5", "glm-5.1"];
453
+ const ZHIPU_BIGMODEL_TEXT_MODELS = ["glm-4.6", "glm-4.7", "glm-4.7-flash", "glm-5", "glm-5.1", "glm-5.2", "glm-5.3"];
302
454
  const ZHIPU_BIGMODEL_MODELS = [...ZHIPU_BIGMODEL_TEXT_MODELS, "glm-4.6v"];
303
455
  const ZHIPU_BIGMODEL_INPUT_MODALITIES: Record<string, string[]> = {
304
456
  ...Object.fromEntries(ZHIPU_BIGMODEL_TEXT_MODELS.map(id => [id, ["text"]])),
305
457
  "glm-4.6v": ["text", "image"],
306
458
  };
307
- const ZHIPU_BIGMODEL_THINKING_TOGGLE_MODELS = ["glm-4.6", "glm-4.7", "glm-5", "glm-5.1"];
459
+ const ZHIPU_BIGMODEL_THINKING_TOGGLE_MODELS = ["glm-4.6", "glm-4.7", "glm-5", "glm-5.1", "glm-5.2", "glm-5.3"];
308
460
  const THINKING_BUDGET_EFFORTS = ["low", "medium", "high", "xhigh", "max"];
461
+ // Qwen3.8-Max is the first Qwen3.x model with official direct `reasoning_effort` support.
462
+ // Evidence: https://qwen.ai/blog?id=qwen3.8
463
+ const QWEN38_REASONING_EFFORTS = ["low", "medium", "xhigh"];
309
464
  const THINKING_BUDGET_MODELS = [
310
465
  "qwen3.5-397b", "qwen3.6-35b",
311
466
  "qwen3.5-plus", "qwen3.6-plus", "qwen3.7-max", "qwen3.7-plus",
312
467
  ];
313
468
  const OPENCODE_GO_THINKING_BUDGET_MODELS = ["qwen3.5-plus", "qwen3.6-plus", "qwen3.7-max", "qwen3.7-plus"];
314
469
  const DEEPSEEK_THINKING_MODELS = ["deepseek-v4-pro", "deepseek-v4-flash"];
470
+ /*
471
+ * DeepSeek's experimental vision preview (released 2026-08-21, api-docs.deepseek.com):
472
+ * text+image input on the V4 Flash base. DeepSeek positions it as a preview id;
473
+ * the expectation is that vision merges into `deepseek-v4-flash` proper later,
474
+ * at which point this id retires the same way deepseek-chat/reasoner did.
475
+ */
476
+ const DEEPSEEK_VISION_PREVIEW_MODEL = "deepseek-v4-flash-vision-exp";
315
477
  const OPENCODE_FREE_DEEPSEEK_MODELS = ["deepseek-v4-flash-free"];
316
- // "max" is advertised too: the wire map routes xhigh->max and max->max, so the picker
317
- // should surface the max tier instead of hiding it behind xhigh.
318
- const DEEPSEEK_THINKING_EFFORTS = ["high", "xhigh", "max"];
319
- const DEEPSEEK_THINKING_REASONING_MAP: Record<string, string> = {
320
- low: "high",
478
+ /*
479
+ * OpenCode Zen's free slug for the OpenRouter stealth model "Ox Alpha"
480
+ * (openrouter.ai/stealth/ox-alpha): 1,048,576-token context, multimodal
481
+ * (text+image+video upstream; Zen serves text+image), mandatory reasoning,
482
+ * free during the stealth window. Zen displays it as "Ox Alpha Free" under
483
+ * this exact id (opencode.ai/docs/zen, verified 2026-08-21).
484
+ */
485
+ const OPENCODE_OX_ALPHA_FREE_MODEL = "x-preview-f-free";
486
+ const OX_ALPHA_CONTEXT_WINDOW = 1_048_576;
487
+ /*
488
+ * Zen free models that reject `image_url` upstream (#1043, and the reproducible
489
+ * half of #1024).
490
+ *
491
+ * Zen publishes NO modality metadata — its `/v1/models` returns only id, object,
492
+ * created, owned_by — so this list is measured, not derived. Each id was probed
493
+ * once against https://opencode.ai/zen/v1 on 2026-08-05 with a text control first
494
+ * and then a 1x1 PNG; the six below failed the image request, four of them with
495
+ * `[404] No endpoints found that support image input` and `big-pickle` with the
496
+ * exact deserialize error quoted in #1043.
497
+ *
498
+ * `mimo-v2.5-free` and `longcat-2.0-free` ACCEPT images and are deliberately
499
+ * absent. Adding them would silently replace a working image with a caption,
500
+ * which is worse than the loud 400 this list exists to prevent — see the negative
501
+ * assertion in tests/provider-registry-parity.test.ts.
502
+ *
503
+ * Zen's roster is discovered live while this list is static, so it is a dated
504
+ * exception list, not a capability model. Re-probe before extending it.
505
+ * Evidence: devlog/_fin/260805_bug_fix_stack/002_zen_modality_probe.md
506
+ */
507
+ const OPENCODE_ZEN_TEXT_ONLY_MODELS = [
508
+ "big-pickle",
509
+ "nemotron-3-ultra-free",
510
+ "ling-3.0-flash-free",
511
+ "north-mini-code-free",
512
+ "laguna-s-2.1-free",
513
+ "deepseek-v4-flash-free",
514
+ ];
515
+ /*
516
+ * DeepSeek's Codex ladder is low/high/max. With the V4 Pro GA release
517
+ * (DeepSeek-V4-Pro-0813) the official thinking-mode table is IDENTICAL for both
518
+ * V4 models (api-docs.deepseek.com/guides/thinking_mode, verified 2026-08-13):
519
+ *
520
+ * requested | v4-flash | v4-pro
521
+ * low | low | low
522
+ * medium | high | high
523
+ * high | high | high
524
+ * xhigh | high | high
525
+ * max | max | max
526
+ *
527
+ * Before GA, Pro silently upgraded low->high and mapped xhigh->max (#1057-era
528
+ * table); the page's footnote about an early-August Pro mapping update landed
529
+ * with this GA, so Pro now advertises the same three real tiers as Flash.
530
+ *
531
+ * Two standing notes (#1057):
532
+ *
533
+ * - `xhigh` is a COMPATIBILITY ALIAS, not a native tier. It stays in the wire maps
534
+ * so existing requests and saved configs keep working, but it is not advertised.
535
+ * - `medium` has no row in the vendor table — mapping it to `high` is OUR
536
+ * compatibility choice for clients that only speak the OpenAI ladder.
537
+ */
538
+ const DEEPSEEK_FLASH_THINKING_EFFORTS = ["low", "high", "max"];
539
+ const DEEPSEEK_PRO_THINKING_EFFORTS = ["low", "high", "max"];
540
+ const DEEPSEEK_PRO_REASONING_MAP: Record<string, string> = {
541
+ low: "low",
321
542
  medium: "high",
322
543
  high: "high",
323
- xhigh: "max",
544
+ xhigh: "high",
324
545
  max: "max",
325
546
  };
547
+ const DEEPSEEK_FLASH_REASONING_MAP: Record<string, string> = {
548
+ low: "low",
549
+ medium: "high",
550
+ high: "high",
551
+ xhigh: "high",
552
+ max: "max",
553
+ };
554
+ /**
555
+ * Flash-versus-Pro classification for DeepSeek V4 model ids, including prefixed
556
+ * (`deepseek/deepseek-v4-pro`) and suffixed (`deepseek-v4-flash-free`) forms.
557
+ * `tests/provider-registry-parity.test.ts` enumerates every id the registry
558
+ * actually passes here, so a future id this substring test would misread cannot
559
+ * land silently.
560
+ */
561
+ const isDeepseekFlashModel = (modelId: string): boolean =>
562
+ modelId.toLowerCase().includes("flash");
563
+ const deepseekThinkingEffortsFor = (modelId: string): string[] =>
564
+ isDeepseekFlashModel(modelId) ? DEEPSEEK_FLASH_THINKING_EFFORTS : DEEPSEEK_PRO_THINKING_EFFORTS;
565
+ const deepseekReasoningMapFor = (modelId: string): Record<string, string> =>
566
+ isDeepseekFlashModel(modelId) ? DEEPSEEK_FLASH_REASONING_MAP : DEEPSEEK_PRO_REASONING_MAP;
326
567
  // 260719 Alibaba Token Plan Personal Edition (China/Beijing). Keep it distinct from
327
568
  // Coding Plan: the products use different exact allowlists and different base URLs.
328
569
  // Evidence: https://help.aliyun.com/en/model-studio/token-plan-personal-overview
329
570
  // https://help.aliyun.com/en/model-studio/token-plan-quickstart
330
571
  const ALIBABA_TOKEN_PLAN_MODELS = [
331
- "qwen3.8-max-preview", "qwen3.7-max", "qwen3.7-plus", "qwen3.6-flash",
332
- "glm-5.2", "deepseek-v4-pro",
572
+ "qwen3.8-max", "qwen3.7-max", "qwen3.7-plus", "qwen3.6-flash",
573
+ "glm-5.3", "glm-5.2", "deepseek-v4-pro",
333
574
  ];
334
575
  const ALIBABA_TOKEN_PLAN_QWEN_MODELS = [
335
- "qwen3.8-max-preview", "qwen3.7-max", "qwen3.7-plus", "qwen3.6-flash",
576
+ "qwen3.8-max", "qwen3.7-max", "qwen3.7-plus", "qwen3.6-flash",
336
577
  ];
337
578
  const ALIBABA_TOKEN_PLAN_INPUT_MODALITIES: Record<string, string[]> = {
338
- "qwen3.8-max-preview": ["text", "image"],
579
+ "qwen3.8-max": ["text", "image"],
339
580
  "qwen3.7-max": ["text", "image"],
340
581
  "qwen3.7-plus": ["text", "image"],
341
582
  "qwen3.6-flash": ["text", "image"],
583
+ "glm-5.3": ["text"],
342
584
  "glm-5.2": ["text"],
343
585
  "deepseek-v4-pro": ["text"],
344
586
  };
@@ -348,14 +590,14 @@ const ALIBABA_TOKEN_PLAN_INPUT_MODALITIES: Record<string, string[]> = {
348
590
  // Evidence: https://www.alibabacloud.com/help/en/model-studio/token-plan-overview
349
591
  // https://qwencloud.com/pricing/token-plan (qwen3.8 metadata)
350
592
  const ALIBABA_INTL_TOKEN_PLAN_MODELS = [
351
- "qwen3.8-max-preview", "qwen3.7-max", "qwen3.7-plus", "qwen3.6-plus", "qwen3.6-flash",
593
+ "qwen3.8-max", "qwen3.7-max", "qwen3.7-plus", "qwen3.6-plus", "qwen3.6-flash",
352
594
  "deepseek-v4-pro", "deepseek-v4-flash", "deepseek-v3.2",
353
595
  "kimi-k2.7-code", "kimi-k2.6", "kimi-k2.5",
354
- "glm-5.2", "glm-5.1", "glm-5",
596
+ "glm-5.3", "glm-5.2", "glm-5.1", "glm-5",
355
597
  "MiniMax-M2.5",
356
598
  ];
357
599
  const ALIBABA_INTL_TOKEN_PLAN_QWEN_MODELS = [
358
- "qwen3.8-max-preview", "qwen3.7-max", "qwen3.7-plus", "qwen3.6-plus", "qwen3.6-flash",
600
+ "qwen3.8-max", "qwen3.7-max", "qwen3.7-plus", "qwen3.6-plus", "qwen3.6-flash",
359
601
  ];
360
602
 
361
603
  // 260722 Tencent Cloud Coding Plan. The plan's model set is explicitly dynamic; these are the
@@ -377,7 +619,7 @@ const TENCENT_CODING_PLAN_MODELS = ["tc-code-latest", "glm-5", "kimi-k2.5", "min
377
619
  // is documented as grounds for suspension — see the `note` on both Plan entries.
378
620
  // Report a break by opening an issue tagging the owner; the three things that rot first are the
379
621
  // static catalogs (liveModels:false cannot self-heal), the base URLs, and those Plan terms.
380
- // Full evidence ledger: devlog/_plan/260801_pr611_volcengine_evidence/000_evidence_ledger.md
622
+ // Full evidence ledger: devlog/_fin/260801_pr611_volcengine_evidence/000_evidence_ledger.md
381
623
  const VOLCENGINE_ARK_MODELS = [
382
624
  "doubao-seed-2-1-pro-260628",
383
625
  "doubao-seed-2-1-turbo-260628",
@@ -385,6 +627,8 @@ const VOLCENGINE_ARK_MODELS = [
385
627
  "deepseek-v4-pro-260425",
386
628
  "deepseek-v4-flash-260425",
387
629
  "deepseek-v3-2-251201",
630
+ // No glm-5-3 row: Ark pins date-stamped snapshot ids (glm-5-2-260617) that cannot be
631
+ // guessed ahead of the vendor publishing them. Add it once /api/v3/models lists one.
388
632
  "glm-5-2-260617",
389
633
  "glm-4-7-251222",
390
634
  ];
@@ -398,6 +642,7 @@ const VOLCENGINE_CODING_PLAN_MODELS = [
398
642
  "doubao-seed-2.0-code",
399
643
  "deepseek-v4-pro",
400
644
  "deepseek-v4-flash",
645
+ "glm-5.3",
401
646
  "glm-5.2",
402
647
  "kimi-k2.6",
403
648
  "minimax-m3",
@@ -405,6 +650,7 @@ const VOLCENGINE_CODING_PLAN_MODELS = [
405
650
  const VOLCENGINE_AGENT_PLAN_MODELS = [
406
651
  "deepseek-v4-pro",
407
652
  "deepseek-v4-flash",
653
+ "glm-5.3",
408
654
  "glm-5.2",
409
655
  "kimi-k2.6",
410
656
  "minimax-m3",
@@ -422,11 +668,12 @@ const VOLCENGINE_PLAN_TEXT_ONLY_MODELS = [
422
668
  "doubao-seed-2.0-code",
423
669
  "deepseek-v4-pro",
424
670
  "deepseek-v4-flash",
671
+ "glm-5.3",
425
672
  "glm-5.2",
426
673
  "doubao-seed-2.0-pro",
427
674
  ];
428
675
  const ALIBABA_INTL_TOKEN_PLAN_INPUT_MODALITIES: Record<string, string[]> = {
429
- "qwen3.8-max-preview": ["text", "image"],
676
+ "qwen3.8-max": ["text", "image"],
430
677
  "qwen3.7-max": ["text", "image"],
431
678
  "qwen3.7-plus": ["text", "image"],
432
679
  "qwen3.6-plus": ["text", "image"],
@@ -437,6 +684,7 @@ const ALIBABA_INTL_TOKEN_PLAN_INPUT_MODALITIES: Record<string, string[]> = {
437
684
  "kimi-k2.7-code": ["text", "image"],
438
685
  "kimi-k2.6": ["text", "image"],
439
686
  "kimi-k2.5": ["text", "image"],
687
+ "glm-5.3": ["text"],
440
688
  "glm-5.2": ["text"],
441
689
  "glm-5.1": ["text"],
442
690
  "glm-5": ["text"],
@@ -496,6 +744,72 @@ const NVIDIA_NIM_KIMI_MODELS = [
496
744
  ...NVIDIA_NIM_KIMI_THINKING_MODELS,
497
745
  "moonshotai/kimi-k2-instruct", "moonshotai/kimi-k2-instruct-0905",
498
746
  ];
747
+ /**
748
+ * 260804 issue #956: NIM publishes no input-modality metadata on `/v1/models`, so the
749
+ * registry is the only source of truth for which models can see images.
750
+ *
751
+ * Two lists, both verified per-model against NVIDIA documentation on 2026-08-04
752
+ * (build.nvidia.com model pages and docs.api.nvidia.com/nim/reference/*). Evidence and
753
+ * the per-id audit: devlog/_fin/260804_stack7_service_vision/011_nim_id_audit.md.
754
+ *
755
+ * Read `noVisionModels` carefully — it lists models that CANNOT see images, which is
756
+ * what routes them through the proxy's vision sidecar (src/vision/index.ts) and makes the
757
+ * catalog advertise image input for them. Membership is wrong in BOTH directions:
758
+ * - a text-only model missing from it keeps issue #956 (images blocked or rejected);
759
+ * - a vision model wrongly IN it gets its image silently replaced by another model's
760
+ * text description — no error, worse answers, extra cost.
761
+ *
762
+ * A new NIM id must be classified DELIBERATELY against its NVIDIA page, never assumed
763
+ * from its name: `google/gemma-4-31b-it` carries no vision marker yet accepts images,
764
+ * `-vl` also appears on embedding/reranking models, and `google/codegemma-7b` is
765
+ * text-only while `google/codegemma-1.1-7b` has no current page at all. An unclassified
766
+ * id is intentionally left alone rather than defaulted, because NIM serves non-chat
767
+ * endpoints (embeddings, rerankers, guards, OCR) that reach the same code path.
768
+ */
769
+ const NVIDIA_NIM_VISION_MODELS = [
770
+ "meta/llama-3.2-11b-vision-instruct", "meta/llama-3.2-90b-vision-instruct",
771
+ "nvidia/llama-3.1-nemotron-nano-vl-8b-v1", "nvidia/nemotron-nano-12b-v2-vl",
772
+ "nvidia/nemotron-3-nano-omni-30b-a3b-reasoning", "nvidia/cosmos3-nano-reasoner",
773
+ "nvidia/ising-calibration-1.5-31b", "nvidia/ising-calibration-1-35b-a3b",
774
+ "google/gemma-4-31b-it", "google/diffusiongemma-26b-a4b-it",
775
+ "minimaxai/minimax-m3", "moonshotai/kimi-k2.6", "moonshotai/kimi-k2.5",
776
+ "stepfun-ai/step-3.7-flash", "thinkingmachines/inkling",
777
+ "mistralai/mistral-medium-3.5-128b",
778
+ ];
779
+ /**
780
+ * The catalog advertises image input only for `noVisionModels` members, so a natively
781
+ * vision-capable model would otherwise be published as text-only and the Codex app would
782
+ * block attachments before the native path ever runs.
783
+ */
784
+ const NVIDIA_NIM_VISION_INPUT_MODALITIES: Record<string, string[]> = Object.fromEntries(
785
+ NVIDIA_NIM_VISION_MODELS.map(id => [id, ["text", "image"]]),
786
+ );
787
+ /**
788
+ * Text-only NIM chat models — 26 ids, each carrying an explicit `Input Modalities: Text`
789
+ * (or equivalent) on its NVIDIA page. PR #964 proposed ~64; six of those are natively
790
+ * image-capable and live in NVIDIA_NIM_VISION_MODELS above, and 32 more had no current
791
+ * NVIDIA page and were dropped rather than assumed.
792
+ *
793
+ * kimi-k2-thinking and kimi-k2-instruct are text-only while k2.5/k2.6 are not — vision
794
+ * and reasoning are independent axes, so all four stay in NVIDIA_NIM_KIMI_MODELS for
795
+ * reasoning suppression regardless of which list they appear in here.
796
+ */
797
+ const NVIDIA_NIM_NO_VISION_MODELS = [
798
+ "deepseek-ai/deepseek-v4-flash", "deepseek-ai/deepseek-v4-pro",
799
+ "google/codegemma-7b",
800
+ "meta/llama-3.1-70b-instruct", "meta/llama-3.1-8b-instruct",
801
+ "meta/llama-3.2-1b-instruct", "meta/llama-3.2-3b-instruct",
802
+ "meta/llama-3.3-70b-instruct", "meta/llama2-70b",
803
+ "mistralai/mistral-7b-instruct-v0.3", "mistralai/mistral-nemotron",
804
+ "moonshotai/kimi-k2-thinking", "moonshotai/kimi-k2-instruct",
805
+ "nvidia/llama-3.1-nemotron-nano-8b-v1", "nvidia/llama-3.1-nemotron-ultra-253b-v1",
806
+ "nvidia/llama-3.3-nemotron-super-49b-v1", "nvidia/llama-3.3-nemotron-super-49b-v1.5",
807
+ "nvidia/nemotron-3-nano-30b-a3b", "nvidia/nemotron-3-super-120b-a12b",
808
+ "nvidia/nemotron-3-ultra-550b-a55b", "nvidia/nemotron-mini-4b-instruct",
809
+ "nvidia/nvidia-nemotron-nano-9b-v2",
810
+ "openai/gpt-oss-120b", "openai/gpt-oss-20b",
811
+ "poolside/laguna-xs-2.1", "z-ai/glm-5.3", "z-ai/glm-5.2",
812
+ ];
499
813
  const KIMI_CODING_MODEL_CONTEXT_WINDOWS: Record<string, number> = Object.fromEntries(
500
814
  KIMI_CODING_MODELS.map(id => [id, id === "k3[1m]" ? KIMI_K3_1M_CONTEXT_WINDOW : KIMI_K3_STANDARD_CONTEXT_WINDOW]),
501
815
  );
@@ -503,6 +817,7 @@ const KIMI_CODING_MODEL_INPUT_MODALITIES = Object.fromEntries(
503
817
  KIMI_CODING_K3_MODELS.map(id => [id, ["text", "image"]]),
504
818
  );
505
819
  const NEURALWATT_REASONING_HISTORY_MODELS = [
820
+ "glm-5.3", "glm-5.3-short",
506
821
  "glm-5.2", "glm-5.2-short",
507
822
  "kimi-k2.6", "kimi-k2.7-code",
508
823
  "qwen3.5-397b", "qwen3.6-35b",
@@ -521,6 +836,9 @@ const BASETEN_MODEL_REASONING_EFFORTS: Record<string, string[]> = {
521
836
  "thinkingmachines/inkling": BASETEN_FULL_REASONING_EFFORTS,
522
837
  "openai/gpt-oss-120b": BASETEN_FULL_REASONING_EFFORTS,
523
838
  "moonshotai/Kimi-K3": ["low", "high", "max"],
839
+ // 260814: GLM-5.3 honours low/high/max upstream, unlike 5.2's high/max on Baseten.
840
+ "zai-org/GLM-5.3": ["low", "high", "max"],
841
+ "zai-org/GLM-5.3-Fast": ["low", "high", "max"],
524
842
  "zai-org/GLM-5.2": ["high", "max"],
525
843
  "zai-org/GLM-5.2-Fast": ["high", "max"],
526
844
  };
@@ -529,6 +847,8 @@ const BASETEN_MODEL_REASONING_EFFORT_MAP: Record<string, Record<string, string>>
529
847
  "thinkingmachines/inkling": { none: "none", minimal: "minimal" },
530
848
  "openai/gpt-oss-120b": { none: "none", minimal: "minimal" },
531
849
  "moonshotai/Kimi-K3": { none: "none" },
850
+ "zai-org/GLM-5.3": { none: "none" },
851
+ "zai-org/GLM-5.3-Fast": { none: "none" },
532
852
  "zai-org/GLM-5.2": { none: "none" },
533
853
  "zai-org/GLM-5.2-Fast": { none: "none" },
534
854
  };
@@ -544,21 +864,84 @@ const BASETEN_MODEL_INPUT_MODALITIES: Record<string, string[]> = {
544
864
  "moonshotai/Kimi-K2.7-Code": ["text", "image"],
545
865
  "moonshotai/Kimi-K3": ["text", "image"],
546
866
  };
867
+
868
+ // 260801 DigitalOcean and Scaleway expose OpenAI-shaped `/v1/models` rows with only
869
+ // id/object/created/owned_by, while their shared serverless catalogs also contain
870
+ // non-chat and endpoint-specific models. Fail closed by intersecting live discovery
871
+ // with ids that the providers' current first-party model tables establish for Chat
872
+ // Completions. A newly listed id therefore needs a docs-backed registry refresh before
873
+ // it can enter the Codex catalog.
874
+ // Evidence: https://docs.digitalocean.com/products/inference/details/models/
875
+ // https://docs.digitalocean.com/reference/api/reference/serverless-inference/
876
+ // https://www.scaleway.com/en/docs/generative-apis/reference-content/supported-models/
877
+ const DIGITALOCEAN_CHAT_COMPLETION_MODELS = [
878
+ "arcee-trinity-large-thinking",
879
+ "openai-gpt-5.6-sol",
880
+ "openai-gpt-5.6-terra",
881
+ "openai-gpt-5.6-luna",
882
+ "qwen3-coder-flash",
883
+ "qwen3.5-397b-a17b",
884
+ "deepseek-v4-pro",
885
+ "deepseek-4-flash",
886
+ "deepseek-3.2",
887
+ "gemma-4-31B-it",
888
+ "minimax-m2.5",
889
+ "kimi-k3",
890
+ "kimi-k2.6",
891
+ "kimi-k2.5",
892
+ "llama3.3-70b-instruct",
893
+ "llama-4-maverick",
894
+ "mistral-3-14B",
895
+ "nemotron-3-ultra-550b",
896
+ "nvidia-nemotron-3-super-120b",
897
+ "nemotron-3-nano-omni",
898
+ "nemotron-nano-12b-v2-vl",
899
+ "mimo-v2.5-pro",
900
+ "glm-5.3",
901
+ "glm-5.2",
902
+ "glm-5.1",
903
+ "glm-5",
904
+ // The API reference uses this native slash id in its Chat Completions example.
905
+ "meta-llama/Meta-Llama-3.1-8B-Instruct",
906
+ ] as const;
907
+ const SCALEWAY_SERVERLESS_CHAT_MODELS = [
908
+ "glm-5.3",
909
+ "glm-5.2",
910
+ // gpt-oss-120b is intentionally omitted: Scaleway requires Responses API for tool calling,
911
+ // while this preset routes Codex agent tools through Chat Completions.
912
+ "qwen3.6-35b-a3b",
913
+ "qwen3.5-397b-a17b",
914
+ "qwen3-235b-a22b-instruct-2507",
915
+ "qwen3-coder-30b-a3b-instruct",
916
+ "gemma-4-26b-a4b-it",
917
+ "llama-3.3-70b-instruct",
918
+ "mistral-medium-3.5-128b",
919
+ "mistral-small-3.2-24b-instruct-2506",
920
+ "pixtral-12b-2409",
921
+ ] as const;
922
+ const SCALEWAY_MODEL_INPUT_MODALITIES: Record<string, string[]> = {
923
+ "pixtral-12b-2409": ["text", "image"],
924
+ };
547
925
  const UMANS_MODELS = [
548
926
  "umans-coder",
549
927
  "umans-kimi-k2.7",
550
928
  "umans-flash",
929
+ "umans-glm-5.3",
551
930
  "umans-glm-5.2",
552
931
  "umans-glm-5.1",
553
932
  "umans-qwen3.6-35b-a3b",
554
933
  ];
555
934
  const UMANS_REASONING_EFFORTS = ["low", "medium", "high", "xhigh", "max"];
556
935
  const UMANS_GLM_REASONING_EFFORTS = ["high", "xhigh", "max"];
557
- const UMANS_TEXT_ONLY_MODELS = ["umans-glm-5.2", "umans-glm-5.1"];
936
+ // 260814: Z.AI folds GLM-5.3 efforts into low/high/max, so `low` is a real tier here and
937
+ // `xhigh` is not distinct from `max` (docs.z.ai/devpack/latest-model).
938
+ const UMANS_GLM_53_REASONING_EFFORTS = ["low", "high", "max"];
939
+ const UMANS_TEXT_ONLY_MODELS = ["umans-glm-5.3", "umans-glm-5.2", "umans-glm-5.1"];
558
940
  const UMANS_MODEL_CONTEXT_WINDOWS: Record<string, number> = {
559
941
  "umans-coder": 262_144,
560
942
  "umans-kimi-k2.7": 262_144,
561
943
  "umans-flash": 262_144,
944
+ "umans-glm-5.3": 405_504,
562
945
  "umans-glm-5.2": 405_504,
563
946
  "umans-glm-5.1": 202_752,
564
947
  "umans-qwen3.6-35b-a3b": 262_144,
@@ -566,6 +949,48 @@ const UMANS_MODEL_CONTEXT_WINDOWS: Record<string, number> = {
566
949
  const UMANS_MODEL_INPUT_MODALITIES: Record<string, string[]> = Object.fromEntries(
567
950
  UMANS_MODELS.map(id => [id, UMANS_TEXT_ONLY_MODELS.includes(id) ? ["text"] : ["text", "image"]]),
568
951
  );
952
+ const CLINE_PASS_MODELS = [
953
+ "cline-pass/glm-5.3",
954
+ "cline-pass/glm-5.2",
955
+ "cline-pass/kimi-k3",
956
+ "cline-pass/kimi-k2.7-code",
957
+ "cline-pass/kimi-k2.6",
958
+ "cline-pass/deepseek-v4-pro",
959
+ "cline-pass/deepseek-v4-flash",
960
+ "cline-pass/mimo-v2.5",
961
+ "cline-pass/mimo-v2.5-pro",
962
+ "cline-pass/minimax-m3",
963
+ "cline-pass/qwen3.8-max",
964
+ "cline-pass/qwen3.7-max",
965
+ "cline-pass/qwen3.7-plus",
966
+ ];
967
+ const CLINE_PASS_MODEL_CONTEXT_WINDOWS: Record<string, number> = {
968
+ "cline-pass/glm-5.3": 1_048_576,
969
+ "cline-pass/glm-5.2": 1_048_576,
970
+ "cline-pass/kimi-k3": 1_048_576,
971
+ "cline-pass/kimi-k2.7-code": 262_144,
972
+ "cline-pass/kimi-k2.6": 262_144,
973
+ "cline-pass/deepseek-v4-pro": 1_048_576,
974
+ "cline-pass/deepseek-v4-flash": 1_048_576,
975
+ "cline-pass/mimo-v2.5": 1_050_000,
976
+ "cline-pass/mimo-v2.5-pro": 1_050_000,
977
+ "cline-pass/minimax-m3": 1_048_576,
978
+ "cline-pass/qwen3.7-max": 1_000_000,
979
+ "cline-pass/qwen3.7-plus": 1_000_000,
980
+ };
981
+ const CLINE_PASS_IMAGE_MODELS = new Set([
982
+ "cline-pass/kimi-k3",
983
+ "cline-pass/kimi-k2.7-code",
984
+ "cline-pass/kimi-k2.6",
985
+ "cline-pass/mimo-v2.5",
986
+ "cline-pass/minimax-m3",
987
+ "cline-pass/qwen3.7-plus",
988
+ ]);
989
+ const CLINE_PASS_MODALITY_KNOWN_MODELS = CLINE_PASS_MODELS.filter(id => id !== "cline-pass/qwen3.8-max");
990
+ const CLINE_PASS_TEXT_ONLY_MODELS = CLINE_PASS_MODALITY_KNOWN_MODELS.filter(id => !CLINE_PASS_IMAGE_MODELS.has(id));
991
+ const CLINE_PASS_MODEL_INPUT_MODALITIES: Record<string, string[]> = Object.fromEntries(
992
+ CLINE_PASS_MODALITY_KNOWN_MODELS.map(id => [id, CLINE_PASS_IMAGE_MODELS.has(id) ? ["text", "image"] : ["text"]]),
993
+ );
569
994
 
570
995
  export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [
571
996
  {
@@ -575,6 +1000,7 @@ export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [
575
1000
  baseUrl: "https://chatgpt.com/backend-api/codex",
576
1001
  authKind: "forward",
577
1002
  codexAccountMode: "pool",
1003
+ supportsServiceTier: true,
578
1004
  featured: true,
579
1005
  note: "Codex login account pool (default) or Direct main-account mode via codexAccountMode",
580
1006
  },
@@ -599,11 +1025,10 @@ export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [
599
1025
  // no-effort fallback to `kimi-k3-max` would never be reached. Mirrors the other K3
600
1026
  // routes (kimi, kimi-code, opencode-go).
601
1027
  modelDefaultReasoningEfforts: { "kimi-k3": "max" },
602
- // Cursor's wire protocol never forwards image parts (request-builder emits an unsupported-
603
- // content marker), so the vision sidecar covers ALL cursor models regardless of what the
604
- // upstream model could natively do. Live-discovered models outside the static list fall back
605
- // to the same marker until they appear here.
606
- noVisionModels: cursorModelIds(CURSOR_STATIC_MODELS),
1028
+ // Blind Cursor models (Auto routers, Composer, GLM-5.2, GLM-5.3) go through the vision sidecar;
1029
+ // multimodal hosts (Claude/Gemini/GPT/Kimi/Grok) take native SelectedImage. The catalog
1030
+ // still advertises image for noVision members so Codex can attach (sidecar option B).
1031
+ noVisionModels: [...CURSOR_NO_VISION_MODELS],
607
1032
  },
608
1033
  {
609
1034
  id: "xai",
@@ -612,9 +1037,21 @@ export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [
612
1037
  baseUrl: "https://api.x.ai/v1",
613
1038
  authKind: "oauth",
614
1039
  allowKeyAuthOverride: true,
1040
+ // Priority Processing is documented for xAI's public API-key Chat Completions and
1041
+ // Responses endpoints. OAuth is a separate Grok CLI subscription gateway and remains
1042
+ // unclassified; do not turn this into a provider-wide supportsServiceTier declaration.
1043
+ keyAuthServiceTier: {
1044
+ supportsServiceTier: true,
1045
+ chatServiceTier: true,
1046
+ },
1047
+ fastTierDescription: "Priority processing, 2x token price",
615
1048
  featured: true,
616
1049
  oauthId: "xai",
617
1050
  jawcodeBundle: "xai",
1051
+ supportsOpenAiWebSearchToolFields: false,
1052
+ // Live A/B on 2026-08-20: xAI rejects native custom/custom_tool_call shapes while accepting
1053
+ // the otherwise-identical request after the custom tool is lowered to a function.
1054
+ supportsResponsesCustomTools: false,
618
1055
  note: "Log in with your Grok account",
619
1056
  // Parallel tool calls: officially supported and default-on per docs.x.ai function-calling
620
1057
  // (verified 260709, devlog/_plan/260709_parallel_tool_calls). Streamed calls arrive whole
@@ -628,8 +1065,27 @@ export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [
628
1065
  // devlog/model_update/260709_model_refresh/001_xai_lineup.md.
629
1066
  // grok-4.20-multi-agent-0309 is intentionally absent: the OAuth chat-completions
630
1067
  // transport returns 400 ("Multi Agent requests are not allowed on chat completions").
631
- models: ["grok-4.5", "grok-4.3", "grok-4.20-0309-reasoning", "grok-4.20-0309-non-reasoning", "grok-build-0.1", "grok-composer-2.5-fast"],
1068
+ // 260813: grok-4.6 added per docs.x.ai/developers/grok-4-6. Context/vision still match
1069
+ // grok-4.5; the reasoning ladder does not — 4.6 adds the documented xhigh rung.
1070
+ models: ["grok-4.6", "grok-4.5", "grok-4.3", "grok-4.20-0309-reasoning", "grok-4.20-0309-non-reasoning", "grok-build-0.1", "grok-composer-2.5-fast"],
632
1071
  defaultModel: "grok-4.5",
1072
+ // Keep Codex Responses callers on the compatibility Chat wire until xAI can replay
1073
+ // opaque reasoning continuation and compaction state across later turns. The scoped
1074
+ // declaration also keeps caller-owned service tiers off the OAuth subscription route.
1075
+ modelWireDefaults: {
1076
+ "grok-4.6": {
1077
+ wire: "openai-chat",
1078
+ inbound: ["responses"],
1079
+ authModes: ["oauth"],
1080
+ forwardCallerServiceTier: false,
1081
+ },
1082
+ "grok-4.5": {
1083
+ wire: "openai-chat",
1084
+ inbound: ["responses"],
1085
+ authModes: ["oauth"],
1086
+ forwardCallerServiceTier: false,
1087
+ },
1088
+ },
633
1089
  // Vision lineup per docs.x.ai model-capabilities/images/understanding: the grok-4.x chat
634
1090
  // models accept image input (JPEG/PNG, URL or base64). Without this the catalog leaves
635
1091
  // inputModalities undefined, and deriveComboCatalogModel defaults an undefined member to
@@ -637,6 +1093,7 @@ export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [
637
1093
  // the app blocks attachments client-side. grok-build-0.1 / grok-composer-2.5-fast stay out
638
1094
  // (they are already listed in noVisionModels below).
639
1095
  modelInputModalities: {
1096
+ "grok-4.6": ["text", "image"],
640
1097
  "grok-4.5": ["text", "image"],
641
1098
  "grok-4.3": ["text", "image"],
642
1099
  "grok-4.20-0309-reasoning": ["text", "image"],
@@ -647,10 +1104,14 @@ export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [
647
1104
  // reasoning_content as the top cause of prompt-cache misses on multi-turn conversations
648
1105
  // (docs.x.ai prompt-caching/multi-turn, verified 2026-07-13 — devlog/_plan/260713_grok_caching).
649
1106
  // Models that never emit reasoning simply have no thinking parts to replay (no-op).
650
- preserveReasoningContentModels: ["grok-4.5", "grok-4.3", "grok-4.20-0309-reasoning"],
651
- // grok-4.5 reasoning is always-on with low/medium/high control (no off tier upstream).
652
- modelReasoningEfforts: { "grok-4.5": ["low", "medium", "high"] },
1107
+ preserveReasoningContentModels: ["grok-4.6", "grok-4.5", "grok-4.3", "grok-4.20-0309-reasoning"],
1108
+ // grok-4.5 reasoning is always-on with low/medium/high (no off tier, no xhigh).
1109
+ // grok-4.6 adds xhigh per docs.x.ai/developers/model-capabilities/text/reasoning;
1110
+ // xAI documents high as the upstream default.
1111
+ modelReasoningEfforts: { "grok-4.6": ["low", "medium", "high", "xhigh"], "grok-4.5": ["low", "medium", "high"] },
1112
+ modelDefaultReasoningEfforts: { "grok-4.6": "high" },
653
1113
  modelContextWindows: {
1114
+ "grok-4.6": 500_000,
654
1115
  "grok-4.5": 500_000,
655
1116
  "grok-4.3": 1_000_000,
656
1117
  "grok-4.20-0309-reasoning": 1_000_000,
@@ -659,12 +1120,50 @@ export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [
659
1120
  },
660
1121
  noVisionModels: ["grok-build-0.1", "grok-composer-2.5-fast"],
661
1122
  },
1123
+ {
1124
+ id: "command-code",
1125
+ label: "Command Code - Auth",
1126
+ adapter: "command-code",
1127
+ baseUrl: "https://api.commandcode.ai",
1128
+ authKind: "oauth",
1129
+ oauthId: "command-code",
1130
+ featured: true,
1131
+ note: "Log in with your Command Code account",
1132
+ // OAuth needs one initial selection, but the exposed catalog is always discovered from the
1133
+ // signed-in account. Do not add a static model list here.
1134
+ defaultModel: "deepseek/deepseek-v4-flash",
1135
+ liveModels: true,
1136
+ modelDiscovery: {
1137
+ url: "https://api.commandcode.ai/provider/v1/models",
1138
+ maxResponseBytes: 262_144,
1139
+ maxModels: 256,
1140
+ },
1141
+ // These are capability facts from official Command Code model profiles, not seeded models.
1142
+ // Unknown/new live models deliberately do not advertise a reasoning picker.
1143
+ reasoningEfforts: [],
1144
+ modelReasoningEfforts: COMMAND_CODE_MODEL_REASONING_EFFORTS,
1145
+ // Ox Alpha (stealth preview, changelog v1.31.0): free 1M multimodal reasoning
1146
+ // model on every plan. DeepSeek vision preview id is preemptive metadata —
1147
+ // it is expected to merge into deepseek-v4-flash later.
1148
+ modelContextWindows: {
1149
+ "stealth/ox-alpha": OX_ALPHA_CONTEXT_WINDOW,
1150
+ [`deepseek/${DEEPSEEK_VISION_PREVIEW_MODEL}`]: 1_048_576,
1151
+ },
1152
+ modelInputModalities: {
1153
+ "stealth/ox-alpha": ["text", "image"],
1154
+ [`deepseek/${DEEPSEEK_VISION_PREVIEW_MODEL}`]: ["text", "image"],
1155
+ },
1156
+ defaultMaxOutputTokens: 64_000,
1157
+ // The proprietary generate wire has no verified per-request serialization flag.
1158
+ parallelToolCalls: false,
1159
+ },
662
1160
  {
663
1161
  id: "anthropic",
664
1162
  label: "Anthropic Claude",
665
1163
  adapter: "anthropic",
666
1164
  baseUrl: "https://api.anthropic.com",
667
1165
  authKind: "oauth",
1166
+ allowBaseUrlOverride: true,
668
1167
  featured: true,
669
1168
  oauthId: "anthropic",
670
1169
  jawcodeBundle: "anthropic",
@@ -739,25 +1238,69 @@ export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [
739
1238
  modelContextWindows: KIRO_MODEL_CONTEXT_WINDOWS,
740
1239
  modelReasoningEfforts: KIRO_MODEL_REASONING_EFFORTS,
741
1240
  },
1241
+ {
1242
+ // Nous Portal — Nous Research subscription gateway (same backend Hermes Agent
1243
+ // uses). OAuth is a device grant (src/oauth/nous.ts): the access token IS the
1244
+ // per-request inference JWT (scope inference:invoke), refresh tokens are
1245
+ // single-use and rotated on every refresh. Catalog is a mix of paid models
1246
+ // (billed against the Portal subscription) and `:free` slugs (e.g.
1247
+ // tencent/hy3:free, stepfun/step-3.7-flash:free, inclusionai/ling-3.0-flash:free);
1248
+ // free-tier gating is decided live by the Portal per account, so discovery
1249
+ // from the signed-in account is authoritative; the static seed below is the
1250
+ // logged-out fallback and only lists free models verified on a real account
1251
+ // (2026-08-10): the Portal free list is authoritative and currently has
1252
+ // exactly 4 :free models: tencent/hy3:free, poolside/laguna-s-2.1:free,
1253
+ // stepfun/step-3.7-flash:free, poolside/laguna-xs-2.1:free.
1254
+ // inclusionai/ling-3.0-flash:free was removed from the Portal free list
1255
+ // (404 on the inference API since 2026-08-07) and must not be seeded.
1256
+ id: "nous",
1257
+ label: "Nous Portal",
1258
+ adapter: "openai-chat",
1259
+ baseUrl: "https://inference-api.nousresearch.com/v1",
1260
+ authKind: "oauth",
1261
+ oauthId: "nous",
1262
+ featured: true,
1263
+ // Mixed free + paid provider: the free tier is per-model (the `:free`
1264
+ // slugs), not a property of the whole provider, so freeTier stays false to
1265
+ // avoid implying every model is free.
1266
+ freeTier: false,
1267
+ dashboardUrl: "https://portal.nousresearch.com",
1268
+ defaultModel: "tencent/hy3:free",
1269
+ liveModels: true,
1270
+ models: ["tencent/hy3:free", "poolside/laguna-s-2.1:free", "stepfun/step-3.7-flash:free", "poolside/laguna-xs-2.1:free"],
1271
+ modelDiscovery: {
1272
+ // Resolves against effectiveBaseUrl (registry baseUrl .../v1) to the same
1273
+ // canonical endpoint https://inference-api.nousresearch.com/v1/models.
1274
+ path: "models",
1275
+ maxResponseBytes: 262_144,
1276
+ maxModels: 512,
1277
+ },
1278
+ note: "Nous Research subscription gateway. OAuth device login with your own Portal account; mixed paid + :free models discovered live (fallback seed 2026-08-10: tencent/hy3:free, poolside/laguna-s-2.1:free, stepfun/step-3.7-flash:free, poolside/laguna-xs-2.1:free).",
1279
+ },
742
1280
  {
743
1281
  id: "openai-apikey",
744
1282
  label: "OpenAI API",
745
1283
  adapter: "openai-responses",
746
1284
  baseUrl: "https://api.openai.com/v1",
747
1285
  authKind: "key",
1286
+ supportsServiceTier: true,
748
1287
  featured: true,
749
1288
  dashboardUrl: "https://platform.openai.com/api-keys",
750
1289
  defaultModel: "gpt-5.5",
751
- models: ["gpt-5.5", ...OPENAI_GPT56_MODELS, ...OPENAI_GPT56_PRO_MODELS],
1290
+ models: ["gpt-5.5", ...OPENAI_GPT56_MODELS, ...OPENAI_GPT56_PRO_MODELS, ...OPENAI_DAYBREAK_MODELS],
752
1291
  liveModels: true,
753
- modelContextWindows: OPENAI_API_GPT56_CONTEXT_WINDOWS,
754
- modelMaxInputTokens: OPENAI_API_GPT56_MAX_INPUT_TOKENS,
1292
+ modelContextWindows: { ...OPENAI_API_GPT56_CONTEXT_WINDOWS, ...OPENAI_DAYBREAK_CONTEXT_WINDOWS },
1293
+ modelMaxInputTokens: { ...OPENAI_API_GPT56_MAX_INPUT_TOKENS, ...OPENAI_DAYBREAK_MAX_INPUT_TOKENS },
755
1294
  modelInputModalities: Object.fromEntries(
756
- ["gpt-5.5", ...OPENAI_GPT56_MODELS, ...OPENAI_GPT56_PRO_MODELS].map(id => [id, ["text", "image"]]),
757
- ),
758
- modelReasoningEfforts: Object.fromEntries(
759
- [...OPENAI_GPT56_MODELS, ...OPENAI_GPT56_PRO_MODELS].map(id => [id, OPENAI_API_GPT56_REASONING_EFFORTS]),
1295
+ ["gpt-5.5", ...OPENAI_GPT56_MODELS, ...OPENAI_GPT56_PRO_MODELS, ...OPENAI_DAYBREAK_MODELS]
1296
+ .map(id => [id, ["text", "image"]]),
760
1297
  ),
1298
+ modelReasoningEfforts: {
1299
+ ...Object.fromEntries(
1300
+ [...OPENAI_GPT56_MODELS, ...OPENAI_GPT56_PRO_MODELS].map(id => [id, OPENAI_API_GPT56_REASONING_EFFORTS]),
1301
+ ),
1302
+ ...OPENAI_DAYBREAK_REASONING_EFFORTS,
1303
+ },
761
1304
  virtualModels: OPENAI_API_GPT56_VIRTUAL_MODELS,
762
1305
  },
763
1306
  {
@@ -777,6 +1320,7 @@ export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [
777
1320
  "umans-coder": UMANS_REASONING_EFFORTS,
778
1321
  "umans-kimi-k2.7": UMANS_REASONING_EFFORTS,
779
1322
  "umans-flash": UMANS_REASONING_EFFORTS,
1323
+ "umans-glm-5.3": UMANS_GLM_53_REASONING_EFFORTS,
780
1324
  "umans-glm-5.2": UMANS_GLM_REASONING_EFFORTS,
781
1325
  "umans-glm-5.1": UMANS_GLM_REASONING_EFFORTS,
782
1326
  "umans-qwen3.6-35b-a3b": UMANS_REASONING_EFFORTS,
@@ -788,16 +1332,41 @@ export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [
788
1332
  id: "opencode-go", label: "opencode go", adapter: "openai-chat", baseUrl: "https://opencode.ai/zen/go/v1",
789
1333
  authKind: "key", featured: true, dashboardUrl: "https://opencode.ai/auth", defaultModel: "kimi-k2.7-code",
790
1334
  jawcodeBundle: "opencode-go", note: "GLM, DeepSeek, Kimi, Qwen, MiMo…",
791
- modelContextWindows: { "kimi-k3": KIMI_K3_STANDARD_CONTEXT_WINDOW },
792
- modelInputModalities: { "kimi-k3": ["text", "image"] },
1335
+ // Zen Go can close a Chat stream after a fully assembled function call without sending
1336
+ // finish_reason or [DONE] (#2260). The adapter still rejects incomplete argument JSON.
1337
+ openaiChatEofTolerance: true,
1338
+ /* [Decision Log]
1339
+ - 목적과 의도: Route GPT 5.6 Luna to the Responses endpoint that OpenCode Go documents for that exact model.
1340
+ - 기존 구현 및 제약 조건: The provider is mixed-wire but its provider-wide `openai-chat` adapter sent Luna to `/chat/completions`; explicit user `modelAdapters` entries must remain authoritative.
1341
+ - 검토한 주요 대안: Change the whole provider to Responses; infer the wire from model-family names; add one registry-only exact-model default.
1342
+ - 선택한 방식: Declare only `gpt-5.6-luna` as `openai-responses` through the existing registry default mechanism.
1343
+ - 다른 대안 대신 이 방식을 선택한 이유: OpenCode Go documents sibling models on Chat or Anthropic endpoints, and an exact registry default preserves both those routes and explicit opt-out precedence.
1344
+ - 장점, 단점 및 영향: Luna reaches `/responses` from every inbound surface without changing siblings; a future upstream endpoint change requires an evidence-backed registry update.
1345
+ */
1346
+ modelWireDefaults: { "gpt-5.6-luna": "openai-responses" },
1347
+ modelContextWindows: {
1348
+ "kimi-k3": KIMI_K3_STANDARD_CONTEXT_WINDOW,
1349
+ // Ox Alpha (stealth 1M multimodal) and the DeepSeek vision preview are
1350
+ // metadata-only here: the Go roster is discovered live, so these apply
1351
+ // the moment the gateway starts serving the ids.
1352
+ [OPENCODE_OX_ALPHA_FREE_MODEL]: OX_ALPHA_CONTEXT_WINDOW,
1353
+ [DEEPSEEK_VISION_PREVIEW_MODEL]: 1_048_576,
1354
+ },
1355
+ modelInputModalities: {
1356
+ "kimi-k3": ["text", "image"],
1357
+ [OPENCODE_OX_ALPHA_FREE_MODEL]: ["text", "image"],
1358
+ // Experimental DeepSeek vision preview — expected to merge into deepseek-v4-flash later.
1359
+ [DEEPSEEK_VISION_PREVIEW_MODEL]: ["text", "image"],
1360
+ },
793
1361
  modelReasoningEfforts: {
1362
+ "glm-5.3": ZAI_GLM_53_REASONING_EFFORTS,
794
1363
  "glm-5.2": ZAI_GLM_52_REASONING_EFFORTS,
795
1364
  "kimi-k3": KIMI_CODING_K3_REASONING_EFFORTS,
796
1365
  "kimi-k2.7-code": [],
797
1366
  "kimi-k2.7-code-highspeed": [],
798
1367
  ...Object.fromEntries(OPENCODE_GO_THINKING_TOGGLE_MODELS.map(id => [id, THINKING_TOGGLE_EFFORTS])),
799
1368
  ...Object.fromEntries(OPENCODE_GO_THINKING_BUDGET_MODELS.map(id => [id, THINKING_BUDGET_EFFORTS])),
800
- ...Object.fromEntries(DEEPSEEK_THINKING_MODELS.map(id => [id, DEEPSEEK_THINKING_EFFORTS])),
1369
+ ...Object.fromEntries(DEEPSEEK_THINKING_MODELS.map(id => [id, deepseekThinkingEffortsFor(id)])),
801
1370
  },
802
1371
  modelDefaultReasoningEfforts: { "kimi-k3": "max" },
803
1372
  // glm-5.2 uses identity labels now that `max` is a native Codex level (no alias map);
@@ -805,7 +1374,14 @@ export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [
805
1374
  modelReasoningEffortMap: {
806
1375
  "kimi-k3": KIMI_CODING_K3_REASONING_EFFORT_MAP,
807
1376
  ...Object.fromEntries(OPENCODE_GO_THINKING_TOGGLE_MODELS.map(id => [id, THINKING_TOGGLE_MAP])),
808
- ...Object.fromEntries(DEEPSEEK_THINKING_MODELS.map(id => [id, DEEPSEEK_THINKING_REASONING_MAP])),
1377
+ ...Object.fromEntries(DEEPSEEK_THINKING_MODELS.map(id => [id, deepseekReasoningMapFor(id)])),
1378
+ },
1379
+ modelSupportsReasoningSummaries: {
1380
+ "glm-5.3": true,
1381
+ "glm-5.2": true,
1382
+ "glm-5.1": true,
1383
+ "glm-5": true,
1384
+ ...Object.fromEntries(DEEPSEEK_THINKING_MODELS.map(id => [id, true])),
809
1385
  },
810
1386
  thinkingToggleModels: OPENCODE_GO_THINKING_TOGGLE_MODELS,
811
1387
  thinkingBudgetModels: THINKING_BUDGET_MODELS,
@@ -814,7 +1390,7 @@ export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [
814
1390
  // every model listed here (and the catalog advertises image input on their behalf).
815
1391
  // Kimi K2.7 Code accepts text+image+video: do NOT list it here.
816
1392
  noVisionModels: [
817
- "glm-5.2", "glm-5", "glm-5.1",
1393
+ "glm-5.3", "glm-5.2", "glm-5", "glm-5.1",
818
1394
  "deepseek-v4-flash", "deepseek-v4-pro",
819
1395
  "mimo-v2-pro", "mimo-v2.5-pro",
820
1396
  "minimax-m2.5", "minimax-m2.7",
@@ -825,7 +1401,7 @@ export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [
825
1401
  noPenaltyModels: ["kimi-k3", "kimi-k2.7-code", "kimi-k2.7-code-highspeed"],
826
1402
  autoToolChoiceOnlyModels: ["kimi-k2.7-code", "kimi-k2.7-code-highspeed"],
827
1403
  // Issue #78: DeepSeek V4 thinking mode requires reasoning_content replay on tool-call turns.
828
- preserveReasoningContentModels: ["glm-5.2", "kimi-k3", "kimi-k2.7-code", "kimi-k2.7-code-highspeed", ...DEEPSEEK_THINKING_MODELS],
1404
+ preserveReasoningContentModels: ["glm-5.3", "glm-5.2", "kimi-k3", "kimi-k2.7-code", "kimi-k2.7-code-highspeed", ...DEEPSEEK_THINKING_MODELS],
829
1405
  },
830
1406
  {
831
1407
  id: "neuralwatt",
@@ -834,10 +1410,13 @@ export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [
834
1410
  baseUrl: "https://api.neuralwatt.com/v1",
835
1411
  authKind: "key",
836
1412
  dashboardUrl: "https://portal.neuralwatt.com",
837
- defaultModel: "glm-5.2",
1413
+ defaultModel: "glm-5.3",
838
1414
  // 2026-07-10 live /v1/models: K2.5 rows were removed and GLM-5.2 short variants added.
1415
+ // 260814: the glm-5.3 quartet is speculative; live discovery is authoritative and drops
1416
+ // any id Neuralwatt has not published yet.
839
1417
  // Evidence: devlog/_plan/260710_provider_hardening/003_research_aggregators.md and https://api.neuralwatt.com/v1/models.
840
1418
  models: [
1419
+ "glm-5.3", "glm-5.3-fast", "glm-5.3-short", "glm-5.3-short-fast",
841
1420
  "glm-5.2", "glm-5.2-fast", "glm-5.2-short", "glm-5.2-short-fast",
842
1421
  "kimi-k2.6", "kimi-k2.6-fast",
843
1422
  "kimi-k2.7-code",
@@ -845,6 +1424,10 @@ export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [
845
1424
  ],
846
1425
  // Neuralwatt's /v1/models metadata is authoritative; these static hints are the offline fallback.
847
1426
  modelReasoningEfforts: {
1427
+ "glm-5.3": ZAI_GLM_53_REASONING_EFFORTS,
1428
+ "glm-5.3-fast": [],
1429
+ "glm-5.3-short": ZAI_GLM_53_REASONING_EFFORTS,
1430
+ "glm-5.3-short-fast": [],
848
1431
  "glm-5.2": ZAI_GLM_52_REASONING_EFFORTS,
849
1432
  "glm-5.2-fast": [],
850
1433
  "glm-5.2-short": ZAI_GLM_52_REASONING_EFFORTS,
@@ -860,15 +1443,96 @@ export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [
860
1443
  "qwen3.6-35b-fast": [],
861
1444
  },
862
1445
  thinkingBudgetModels: THINKING_BUDGET_MODELS,
863
- noReasoningModels: ["glm-5.2-fast", "glm-5.2-short-fast", "kimi-k2.6-fast", "qwen3.5-397b-fast", "qwen3.6-35b-fast"],
864
- noVisionModels: ["glm-5.2", "glm-5.2-fast", "glm-5.2-short", "glm-5.2-short-fast", "qwen3.5-397b", "qwen3.5-397b-fast"],
1446
+ noReasoningModels: ["glm-5.3-fast", "glm-5.3-short-fast", "glm-5.2-fast", "glm-5.2-short-fast", "kimi-k2.6-fast", "qwen3.5-397b-fast", "qwen3.6-35b-fast"],
1447
+ noVisionModels: ["glm-5.3", "glm-5.3-fast", "glm-5.3-short", "glm-5.3-short-fast", "glm-5.2", "glm-5.2-fast", "glm-5.2-short", "glm-5.2-short-fast", "qwen3.5-397b", "qwen3.5-397b-fast"],
865
1448
  noTemperatureModels: ["kimi-k2.7-code"],
866
1449
  noTopPModels: ["kimi-k2.7-code"],
867
1450
  noPenaltyModels: ["kimi-k2.7-code"],
868
1451
  autoToolChoiceOnlyModels: ["kimi-k2.7-code"],
869
1452
  preserveReasoningContentModels: NEURALWATT_REASONING_HISTORY_MODELS,
870
1453
  },
871
- { id: "openrouter", label: "OpenRouter", adapter: "openai-chat", baseUrl: "https://openrouter.ai/api/v1", authKind: "key", featured: true, dashboardUrl: "https://openrouter.ai/keys", jawcodeBundle: "openrouter", models: ["anthropic/claude-sonnet-5", ...OPENROUTER_GPT56_MODELS], modelContextWindows: { "anthropic/claude-sonnet-5": 1_000_000, ...OPENROUTER_GPT56_CONTEXT_WINDOWS } },
1454
+ {
1455
+ id: "openrouter",
1456
+ label: "OpenRouter",
1457
+ adapter: "openai-chat",
1458
+ baseUrl: "https://openrouter.ai/api/v1",
1459
+ authKind: "key",
1460
+ featured: true,
1461
+ dashboardUrl: "https://openrouter.ai/keys",
1462
+ jawcodeBundle: "openrouter",
1463
+ // stealth/ox-alpha: free stealth-window frontier model (launched 2026-08-20).
1464
+ // /api/v1/models reports 1,048,576 context, 131,072 max output, text+image+video
1465
+ // input, $0 pricing, mandatory reasoning. Single provider slug: `stealth`.
1466
+ models: ["anthropic/claude-sonnet-5", "stealth/ox-alpha", ...OPENROUTER_GPT56_MODELS],
1467
+ modelContextWindows: {
1468
+ "anthropic/claude-sonnet-5": 1_000_000,
1469
+ "stealth/ox-alpha": OX_ALPHA_CONTEXT_WINDOW,
1470
+ ...OPENROUTER_GPT56_CONTEXT_WINDOWS,
1471
+ },
1472
+ modelInputModalities: { "stealth/ox-alpha": ["text", "image"] },
1473
+ // OpenRouter documents priority support for OpenAI endpoints, but not Anthropic. Keep the
1474
+ // provider unclassified and opt in only the exact OpenAI-backed slugs we ship. These facts
1475
+ // belong only to the canonical destination; a same-named custom gateway is unknown to us.
1476
+ modelServiceTierCapabilityBaseUrlGuard: isCanonicalOpenRouterTarget,
1477
+ modelSupportsServiceTier: {
1478
+ "openai/gpt-5.6-sol": true,
1479
+ "openai/gpt-5.6-terra": true,
1480
+ "openai/gpt-5.6-luna": true,
1481
+ },
1482
+ // Deliberately no OpenRouter route pin: it bills the endpoint actually used and reports the
1483
+ // actual service_tier. B0 confirmation therefore owns downgrade safety. Forcing `only` plus
1484
+ // `allow_fallbacks:false` would turn a graceful priority-capacity fallback into a hard failure.
1485
+ },
1486
+ {
1487
+ // Primary sources checked 2026-08-02:
1488
+ // - docs.cline.bot/getting-started/clinepass publishes this exact catalog and explicitly
1489
+ // authorizes using the full slugs through Cline's external API.
1490
+ // - docs.cline.bot/api/chat-completions and /api/errors define the endpoint, reasoning delta,
1491
+ // and choice-scoped mid-stream error contract.
1492
+ // - Cline's official catalog source resolves per-model capabilities through OpenRouter data;
1493
+ // the static context/modality snapshot below was cross-checked against that catalog.
1494
+ // - cline.bot/tos identifies Cline Bot Inc. as the operator. Maintenance owner: @lidge-jun.
1495
+ id: "cline-pass",
1496
+ label: "ClinePass",
1497
+ adapter: "openai-chat",
1498
+ baseUrl: "https://api.cline.bot/api/v1",
1499
+ authKind: "key",
1500
+ dashboardUrl: "https://app.cline.bot",
1501
+ defaultModel: "cline-pass/kimi-k3",
1502
+ models: CLINE_PASS_MODELS,
1503
+ modelContextWindows: CLINE_PASS_MODEL_CONTEXT_WINDOWS,
1504
+ modelInputModalities: CLINE_PASS_MODEL_INPUT_MODALITIES,
1505
+ noVisionModels: CLINE_PASS_TEXT_ONLY_MODELS,
1506
+ // Live-probed 2026-08-13 across every static ClinePass model: the gateway accepts and
1507
+ // validates low/medium/high/xhigh/max, and rejects an invalid sentinel. Preserve the
1508
+ // caller's requested tier and let ClinePass own any backend-specific normalization.
1509
+ reasoningEfforts: ["low", "medium", "high", "xhigh", "max"],
1510
+ reasoningWireFormat: "gateway-object",
1511
+ preserveCustomDestination: true,
1512
+ note: "ClinePass subscription API. Uses a Cline API key and the full cline-pass/<model> upstream slug; quota is shared across the account's rolling 5-hour, weekly, and monthly limits.",
1513
+ },
1514
+ // Cline API (usage-billing): OpenAI-compatible Chat Completions. Model IDs follow the
1515
+ // OpenRouter-style `provider/model` convention. Live /models discovery is key-gated (401
1516
+ // without auth), so the static seed is the cold-start fallback. Evidence: docs.cline.bot/api/*.
1517
+ {
1518
+ id: "cline",
1519
+ label: "Cline",
1520
+ adapter: "openai-chat",
1521
+ baseUrl: "https://api.cline.bot/api/v1",
1522
+ authKind: "key",
1523
+ dashboardUrl: "https://app.cline.bot",
1524
+ liveModels: true,
1525
+ defaultModel: "anthropic/claude-sonnet-4-6",
1526
+ models: [
1527
+ "anthropic/claude-sonnet-4-6",
1528
+ "openai/gpt-4o",
1529
+ "google/gemini-2.5-pro",
1530
+ "deepseek/deepseek-chat",
1531
+ "minimax/minimax-m2.5",
1532
+ ],
1533
+ preserveCustomDestination: true,
1534
+ note: "Cline usage-billing API: one key, 100+ models, OpenRouter-style ids. Promotional free models are IDE/CLI-only per Cline docs; minimax/minimax-m2.5 is the documented API free experimentation model.",
1535
+ },
872
1536
  {
873
1537
  // OrcaRouter: OpenAI-compatible adaptive router (api.orcarouter.ai). Model ids are
874
1538
  // vendor-namespaced (`<vendor>/<model>`) and pass through to the upstream as-is.
@@ -896,9 +1560,9 @@ export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [
896
1560
  // returned 200), so no noTemperatureModels entry is warranted here.
897
1561
  modelReasoningEfforts: {
898
1562
  "openai/gpt-5.5": ["low", "medium", "high", "xhigh"],
899
- "deepseek/deepseek-v4-pro": DEEPSEEK_THINKING_EFFORTS,
1563
+ "deepseek/deepseek-v4-pro": deepseekThinkingEffortsFor("deepseek/deepseek-v4-pro"),
900
1564
  },
901
- modelReasoningEffortMap: { "deepseek/deepseek-v4-pro": DEEPSEEK_THINKING_REASONING_MAP },
1565
+ modelReasoningEffortMap: { "deepseek/deepseek-v4-pro": deepseekReasoningMapFor("deepseek/deepseek-v4-pro") },
902
1566
  preserveReasoningContentModels: ["deepseek/deepseek-v4-pro"],
903
1567
  note: "OpenAI-compatible adaptive router. Default is a tool-capable model; orcarouter/auto (adaptive routing) is also selectable. Full catalog: https://www.orcarouter.ai/models",
904
1568
  },
@@ -919,12 +1583,13 @@ export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [
919
1583
  // devlog/_plan/260710_provider_hardening/001_research_frontier.md.
920
1584
  {
921
1585
  id: "google", label: "Google Gemini", adapter: "google", baseUrl: "https://generativelanguage.googleapis.com", authKind: "key", featured: true,
922
- dashboardUrl: "https://aistudio.google.com/apikey", defaultModel: "gemini-3.5-flash", models: ["gemini-3.6-flash", "gemini-3.5-flash", "gemini-3.5-flash-lite", "gemini-3.1-pro-preview"],
923
- modelContextWindows: { "gemini-3.6-flash": 1_048_576, "gemini-3.5-flash": 1_000_000, "gemini-3.5-flash-lite": 1_048_576 },
924
- modelInputModalities: { "gemini-3.6-flash": ["text", "image"], "gemini-3.5-flash-lite": ["text", "image"] },
1586
+ dashboardUrl: "https://aistudio.google.com/apikey", defaultModel: "gemini-3.5-flash", models: ["gemini-3.6-flash", "gemini-3.5-flash", "gemini-3.5-flash-lite", "gemini-3.1-pro-preview", "gemini-3.7-flash"],
1587
+ modelContextWindows: { "gemini-3.6-flash": 1_048_576, "gemini-3.5-flash": 1_000_000, "gemini-3.5-flash-lite": 1_048_576, "gemini-3.7-flash": 1_048_576 },
1588
+ modelInputModalities: { "gemini-3.6-flash": ["text", "image"], "gemini-3.5-flash-lite": ["text", "image"], "gemini-3.7-flash": ["text", "image"] },
925
1589
  modelReasoningEfforts: {
926
1590
  "gemini-3.6-flash": ["minimal", "low", "medium", "high"],
927
1591
  "gemini-3.5-flash": ["minimal", "low", "medium", "high"],
1592
+ "gemini-3.7-flash": ["minimal", "low", "medium", "high"],
928
1593
  "gemini-3.1-pro-preview": ["low", "medium", "high"],
929
1594
  },
930
1595
  jawcodeBundle: "google", extraMetadataAliases: ["gemini"],
@@ -932,7 +1597,7 @@ export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [
932
1597
  // 2026-07-10: defaultModel is frozen pending Vertex-specific Tier-2 evidence; Gemini API
933
1598
  // evidence from ai.google.dev does not establish Vertex publisher availability.
934
1599
  { id: "google-vertex", label: "Google Vertex AI", adapter: "google", baseUrl: "https://aiplatform.googleapis.com", authKind: "key", dashboardUrl: "https://console.cloud.google.com/vertex-ai", defaultModel: "gemini-3-pro", googleMode: "vertex", jawcodeBundle: "google", extraMetadataAliases: ["gemini-vertex"] },
935
- { id: "google-antigravity", label: "Google Antigravity", adapter: "google", baseUrl: "https://daily-cloudcode-pa.googleapis.com", authKind: "oauth", dashboardUrl: "https://antigravity.google", models: ANTIGRAVITY_MODELS, liveModels: false, defaultModel: "gemini-3.6-flash", modelContextWindows: ANTIGRAVITY_MODEL_CONTEXT_WINDOWS, modelReasoningEfforts: ANTIGRAVITY_MODEL_EFFORTS, googleMode: "cloud-code-assist", jawcodeBundle: "google", extraMetadataAliases: ["antigravity", "gemini-antigravity"] },
1600
+ { id: "google-antigravity", label: "Google Antigravity", adapter: "google", baseUrl: "https://daily-cloudcode-pa.googleapis.com", authKind: "oauth", allowBaseUrlOverride: true, dashboardUrl: "https://antigravity.google", models: ANTIGRAVITY_MODELS, liveModels: true, defaultModel: "gemini-3.7-flash", modelContextWindows: ANTIGRAVITY_MODEL_CONTEXT_WINDOWS, modelInputModalities: ANTIGRAVITY_MODEL_INPUT_MODALITIES, modelReasoningEfforts: ANTIGRAVITY_MODEL_EFFORTS, googleMode: "cloud-code-assist", jawcodeBundle: "google", extraMetadataAliases: ["antigravity", "gemini-antigravity"] },
936
1601
  { id: "azure-openai", label: "Azure OpenAI", adapter: "azure-openai", baseUrl: "https://{resource}.openai.azure.com/openai", authKind: "key", featured: true, dashboardUrl: "https://portal.azure.com" },
937
1602
  { id: "ollama", label: "Ollama (local)", adapter: "openai-chat", baseUrl: "http://localhost:11434/v1", authKind: "local", allowPrivateNetworkByDefault: true, allowBaseUrlOverride: true, featured: true, note: "Local — key usually blank" },
938
1603
  { id: "vllm", label: "vLLM (local)", adapter: "openai-chat", baseUrl: "http://localhost:8000/v1", authKind: "local", allowPrivateNetworkByDefault: true, allowBaseUrlOverride: true, featured: true, note: "Local — key usually blank" },
@@ -944,13 +1609,29 @@ export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [
944
1609
  adapter: "openai-chat",
945
1610
  authKind: "key",
946
1611
  dashboardUrl: "https://platform.deepseek.com/api_keys",
947
- // deepseek-chat/deepseek-reasoner are upstream-deprecated at 2026-07-24 15:59 UTC;
948
- // kept until then. Evidence: devlog/_plan/260710_provider_hardening/002_research_cn.md.
949
- models: ["deepseek-chat", "deepseek-reasoner", ...DEEPSEEK_THINKING_MODELS],
1612
+ // Route DeepSeek's own catalog bundle so routed rebuilds restore the official
1613
+ // context window from the vendored model-metadata bundle instead of falling
1614
+ // back to the 128k strict-fields default (scripts/model-metadata.source.json,
1615
+ // verified 2026-08-08).
1616
+ jawcodeBundle: "deepseek",
1617
+ // deepseek-chat/deepseek-reasoner were deprecated upstream on 2026-07-24 15:59 UTC;
1618
+ // official identifiers are now deepseek-v4-flash / deepseek-v4-pro. They stay in
1619
+ // the list only as compatibility aliases so existing saved configs and requests
1620
+ // keep validating and routing (they previously mapped to v4-flash; devlog
1621
+ // _fin/260710_provider_hardening/002_research_cn.md). The current offerings are
1622
+ // the V4 ids — defaultModel and the model-specific wiring above use them.
1623
+ // deepseek-v4-flash-vision-exp: experimental vision preview (2026-08-21) —
1624
+ // expected to merge into deepseek-v4-flash later; see DEEPSEEK_VISION_PREVIEW_MODEL.
1625
+ models: ["deepseek-chat", "deepseek-reasoner", ...DEEPSEEK_THINKING_MODELS, DEEPSEEK_VISION_PREVIEW_MODEL],
950
1626
  defaultModel: "deepseek-v4-flash",
951
- modelContextWindows: { "deepseek-v4-flash": 1_000_000, "deepseek-v4-pro": 1_000_000 },
952
- // DeepSeek documents V4-Flash as a native Responses API model adapted for Codex. The
953
- // API id is `deepseek-v4-flash`; `DeepSeek-V4-Flash-0731` is a release/version label.
1627
+ // Official DeepSeek Codex setup (codex-deepseek-setup.sh) advertises 1,048,576
1628
+ // for both V4 models; the older 1,000,000 figure was a rounded approximation.
1629
+ modelContextWindows: { "deepseek-v4-flash": 1_048_576, "deepseek-v4-pro": 1_048_576, [DEEPSEEK_VISION_PREVIEW_MODEL]: 1_048_576 },
1630
+ modelInputModalities: { [DEEPSEEK_VISION_PREVIEW_MODEL]: ["text", "image"] },
1631
+ // DeepSeek documents both V4 models as native Responses API models adapted for Codex
1632
+ // (model table marks Responses API ✓ for flash and pro; the /responses reference lists
1633
+ // both ids as accepted `model` values — verified 2026-08-13 with the V4 Pro GA,
1634
+ // version label DeepSeek-V4-Pro-0813).
954
1635
  modelWireDefaults: {
955
1636
  // Codex speaks Responses natively and DeepSeek ships a Codex-compatible
956
1637
  // apply_patch tool on that wire, so a Responses inbound goes straight out with
@@ -959,22 +1640,56 @@ export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [
959
1640
  // translating them into Responses would add a hop onto our newest upstream path
960
1641
  // for no gain.
961
1642
  "deepseek-v4-flash": { wire: "openai-responses", inbound: ["responses"] },
1643
+ "deepseek-v4-pro": { wire: "openai-responses", inbound: ["responses"] },
962
1644
  },
1645
+ // The #875-era bounded-JSON force (`modelResponsesUpstreamStreaming`) is retired
1646
+ // for this entry: the official guide documents a `response.completed` /
1647
+ // `response.incomplete` / `response.failed` terminal with NO `data: [DONE]`
1648
+ // sentinel, and live probes (2026-08-07, including the tool-result replay shape
1649
+ // that originally stalled) close on the terminal. The relay's terminal boundary
1650
+ // (src/server/relay.ts) already cuts the stream at that event and synthesizes
1651
+ // `[DONE]`, so forcing stream:false only delayed every byte until generation
1652
+ // finished (28-46 s of silence on long turns). The registry knob itself remains
1653
+ // for providers that need it — re-adding one line here restores the old policy.
1654
+ // Evidence: https://api-docs.deepseek.com/guides/responses_api/ +
1655
+ // devlog/_fin/260807_deepseek_responses_streaming/000_plan.md.
1656
+ // Current official streams normally carry a real terminal; retain a narrow grace
1657
+ // repair for the historical shape that closes after a complete graph without one.
1658
+ modelResponsesTerminalRepair: { "deepseek-v4-flash": { graceMs: 5_000 }, "deepseek-v4-pro": { graceMs: 5_000 } },
1659
+ // DeepSeek's Responses route emits bare UUID item ids, which leave Codex
1660
+ // clients stuck on an uncommitted turn (#938). Client-facing only — raw
1661
+ // continuation snapshots keep the upstream ids.
1662
+ responsesItemIdRepair: { repairInvalidIds: true, repairMissingTerminalIds: true },
963
1663
  // DeepSeek's Responses route is `POST /responses` with no `/v1` segment. Without
964
1664
  // this the passthrough adapter falls back to its legacy `/v1/responses`
965
1665
  // construction and the wire above can never route.
966
1666
  // Evidence: https://api-docs.deepseek.com/api/create-response/
967
1667
  responsesPath: "/responses",
1668
+ // DeepSeek's Responses reference does not list `service_tier`; unsupported
1669
+ // parameters are documented as silently ignored, but the fail-closed policy
1670
+ // strips the field rather than forwarding a knob the upstream never asked for.
1671
+ supportsServiceTier: false,
1672
+ // DeepSeek's Responses compatibility guide accepts plaintext reasoning items and
1673
+ // merges them into the adjacent assistant message, so replayed reasoning must
1674
+ // not be blanked the way the ChatGPT backend requires. (Whether the Responses
1675
+ // route REQUIRES replay on tool-call continuations is an inference from the
1676
+ // Chat Thinking-Mode docs, not a confirmed Responses contract.)
1677
+ preserveResponsesReasoningContent: true,
968
1678
  // "The API is stateless: responses and conversations are not stored on the
969
1679
  // server." https://api-docs.deepseek.com/api/create-response/
970
1680
  statelessResponses: true,
1681
+ // DeepSeek rejects a valid Codex continuation when hook-provided developer
1682
+ // context splits a call from its result (#1292); parallel calls remain one
1683
+ // reasoning-bearing assistant batch rather than being split per pair (#1477).
1684
+ requiresAdjacentResponsesToolResults: true,
971
1685
  /* [Decision Log]
972
1686
  - 목적: DeepSeek V4 thinking mode multi-turn/tool-call requests must replay prior assistant reasoning_content.
973
1687
  - 대안 분석: Globally preserve reasoning_content for all OpenAI-compatible models; preserve it for legacy deepseek-reasoner too; mark only V4 thinking models in registry metadata.
974
1688
  - 선택 근거: DeepSeek V4 thinking mode requires history replay, while older DeepSeek reasoner has different compatibility rules. A model-scoped registry flag fixes built-in and stale saved configs without broad provider regressions.
975
1689
  */
976
- modelReasoningEfforts: Object.fromEntries(DEEPSEEK_THINKING_MODELS.map(id => [id, DEEPSEEK_THINKING_EFFORTS])),
977
- modelReasoningEffortMap: Object.fromEntries(DEEPSEEK_THINKING_MODELS.map(id => [id, DEEPSEEK_THINKING_REASONING_MAP])),
1690
+ modelReasoningEfforts: Object.fromEntries(DEEPSEEK_THINKING_MODELS.map(id => [id, deepseekThinkingEffortsFor(id)])),
1691
+ modelReasoningEffortMap: Object.fromEntries(DEEPSEEK_THINKING_MODELS.map(id => [id, deepseekReasoningMapFor(id)])),
1692
+ modelSupportsReasoningSummaries: Object.fromEntries(DEEPSEEK_THINKING_MODELS.map(id => [id, true])),
978
1693
  preserveReasoningContentModels: DEEPSEEK_THINKING_MODELS,
979
1694
  // Issue #88: every DeepSeek API model is text-only input (no image support upstream) — the
980
1695
  // vision sidecar describes attached images for them, and the catalog advertises image input
@@ -983,6 +1698,40 @@ export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [
983
1698
  },
984
1699
  // llama-3.3-70b was deprecated by Cerebras on 2026-02-16. Evidence: devlog/_plan/260710_provider_hardening/003_research_aggregators.md.
985
1700
  { id: "cerebras", label: "Cerebras", baseUrl: "https://api.cerebras.ai/v1", adapter: "openai-chat", authKind: "key", dashboardUrl: "https://cloud.cerebras.ai/platform/apikeys", defaultModel: "gpt-oss-120b" },
1701
+ {
1702
+ // Primary sources checked 2026-08-08:
1703
+ // - https://chutes.ai/pricing documents the shared llm.chutes.ai/v1 OpenAI-compatible
1704
+ // gateway, Bearer API keys, and chat completions. Its public
1705
+ // https://llm.chutes.ai/v1/models response supplies supported_features for filtering.
1706
+ // - https://chutes.ai/terms identifies Chutes Global Corp as the platform operator, applies
1707
+ // to API consumers, and directs production/high-volume automated inference to PAYGO.
1708
+ // Maintainer: @olddonkey; no affiliation with Chutes.
1709
+ id: "chutes",
1710
+ label: "Chutes",
1711
+ baseUrl: "https://llm.chutes.ai/v1",
1712
+ adapter: "openai-chat",
1713
+ authKind: "key",
1714
+ dashboardUrl: "https://chutes.ai/auth/start",
1715
+ liveModels: true,
1716
+ preserveCustomDestination: true,
1717
+ // The public model catalog cannot prove that a supplied Bearer key is valid.
1718
+ apiKeyValidation: "unknown",
1719
+ // Chutes documents tool calling, but not a provider-wide parallel tool-call contract.
1720
+ parallelToolCalls: false,
1721
+ // The live catalog reports reasoning support, but not a stable effort ladder.
1722
+ reasoningEfforts: [],
1723
+ modelDiscovery: {
1724
+ path: "models",
1725
+ maxResponseBytes: 256 * 1024,
1726
+ maxModels: 128,
1727
+ filter: {
1728
+ // The shared LLM catalog also contains rows without native tool support. Codex needs a
1729
+ // complete agent loop, so admit only rows whose live metadata advertises tools.
1730
+ allOf: [{ path: ["supported_features"], containsAny: ["tools"] }],
1731
+ },
1732
+ },
1733
+ note: "Shared OpenAI-compatible LLM gateway only; live discovery exposes tool-capable rows. User-deployed custom Chute endpoints and non-LLM APIs require a custom provider.",
1734
+ },
986
1735
  {
987
1736
  id: "deepinfra",
988
1737
  label: "DeepInfra",
@@ -1020,6 +1769,69 @@ export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [
1020
1769
  },
1021
1770
  note: "Serverless text and vision-language chat models only; Hyperbolic's separate image, audio, and GPU endpoints are out of scope.",
1022
1771
  },
1772
+ {
1773
+ // Primary sources checked 2026-08-03:
1774
+ // - docs.nscale.com documents the production OpenAI-compatible endpoint, bearer service
1775
+ // tokens, /v1/models, and a tool-calling request using this exact Llama model id.
1776
+ // - nscale.com/policies/terms-conditions identifies Nscale AS as the service operator and
1777
+ // covers customers using its public-cloud inference offering. Maintainer: @olddonkey;
1778
+ // no affiliation with Nscale.
1779
+ id: "nscale",
1780
+ label: "Nscale Serverless Inference",
1781
+ baseUrl: "https://inference.api.nscale.com/v1",
1782
+ adapter: "openai-chat",
1783
+ authKind: "key",
1784
+ dashboardUrl: "https://console.nscale.com",
1785
+ defaultModel: "meta-llama/Llama-3.1-8B-Instruct",
1786
+ models: ["meta-llama/Llama-3.1-8B-Instruct"],
1787
+ liveModels: true,
1788
+ preserveCustomDestination: true,
1789
+ // Nscale documents tools but not parallel tool calls. Keep requests serialized.
1790
+ parallelToolCalls: false,
1791
+ // The API schema accepts reasoning_effort, but does not publish per-model tiers.
1792
+ reasoningEfforts: [],
1793
+ modelDiscovery: {
1794
+ path: "models",
1795
+ maxResponseBytes: 256 * 1024,
1796
+ maxModels: 256,
1797
+ filter: {
1798
+ // Nscale's catalog mixes chat, image, and embedding rows without a modality field.
1799
+ // Admit only the exact model used in its official tool-calling API example.
1800
+ allOf: [{ path: ["id"], equalsAny: ["meta-llama/Llama-3.1-8B-Instruct"] }],
1801
+ },
1802
+ },
1803
+ note: "Serverless OpenAI-compatible inference. Live discovery admits only the tool-capable model established by Nscale's official API example; other mixed-catalog rows remain hidden pending equivalent evidence.",
1804
+ },
1805
+ {
1806
+ // Primary sources checked 2026-08-03:
1807
+ // - docs.vultr.com documents the fixed OpenAI-compatible base URL, per-subscription bearer
1808
+ // key, /v1/models, and states that tool calling is currently limited to kimi-k2-instruct.
1809
+ // - Vultr's official properties identify VULTR as a The Constant Company, LLC trademark and
1810
+ // document customer API integrations. Maintainer: @olddonkey; no affiliation with Vultr.
1811
+ id: "vultr",
1812
+ label: "Vultr Serverless Inference",
1813
+ baseUrl: "https://api.vultrinference.com/v1",
1814
+ adapter: "openai-chat",
1815
+ authKind: "key",
1816
+ dashboardUrl: "https://my.vultr.com",
1817
+ defaultModel: "kimi-k2-instruct",
1818
+ models: ["kimi-k2-instruct"],
1819
+ liveModels: true,
1820
+ preserveCustomDestination: true,
1821
+ parallelToolCalls: false,
1822
+ reasoningEfforts: [],
1823
+ modelDiscovery: {
1824
+ path: "models",
1825
+ maxResponseBytes: 256 * 1024,
1826
+ maxModels: 256,
1827
+ filter: {
1828
+ // Vultr explicitly limits tool calling to this model. A coding agent must not select
1829
+ // another chat model that cannot complete its tool loop.
1830
+ allOf: [{ path: ["id"], equalsAny: ["kimi-k2-instruct"] }],
1831
+ },
1832
+ },
1833
+ note: "Serverless Inference subscription API. Live discovery exposes only kimi-k2-instruct because Vultr documents it as the sole tool-calling model.",
1834
+ },
1023
1835
  {
1024
1836
  id: "baseten",
1025
1837
  label: "Baseten Model APIs",
@@ -1045,6 +1857,234 @@ export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [
1045
1857
  },
1046
1858
  note: "Shared Model APIs only (personal API key, or team key with Call Model APIs access); dedicated Truss predict endpoints are outside this preset.",
1047
1859
  },
1860
+ {
1861
+ id: "commandcode",
1862
+ label: "Command Code - API",
1863
+ adapter: "openai-chat",
1864
+ baseUrl: "https://api.commandcode.ai/provider/v1",
1865
+ authKind: "key",
1866
+ dashboardUrl: "https://commandcode.ai/studio/",
1867
+ liveModels: true,
1868
+ preserveCustomDestination: true,
1869
+ defaultModel: "deepseek/deepseek-v4-flash",
1870
+ // The default is also the cold-start seed: live discovery failure must not empty the catalog
1871
+ // for a freshly configured provider with no stale cache (issue #308 pattern).
1872
+ models: ["deepseek/deepseek-v4-flash"],
1873
+ // The public model catalog is unauthenticated, so a Bearer probe cannot prove key validity.
1874
+ apiKeyValidation: "unknown",
1875
+ // The public catalog reports ids/context windows only; no trustworthy reasoning contract.
1876
+ reasoningEfforts: [],
1877
+ // Official Command Code model-profile reasoning facts (shared with the OAuth
1878
+ // `command-code` entry). Without them the API-key preset never advertises a
1879
+ // reasoning picker, and the router's known-ids decode source misses the native
1880
+ // slash ids — so a Codex-facing slug like `commandcode/deepseek-deepseek-v4-pro`
1881
+ // is sent upstream verbatim and rejected with `unsupported_model`.
1882
+ modelReasoningEfforts: COMMAND_CODE_MODEL_REASONING_EFFORTS,
1883
+ // Ox Alpha (stealth preview, Command Code changelog v1.31.0) ships with a
1884
+ // 1.05M-token multimodal context; the DeepSeek vision preview id is
1885
+ // preemptive for when the catalog serves it (merges into v4-flash later).
1886
+ modelContextWindows: {
1887
+ "stealth/ox-alpha": OX_ALPHA_CONTEXT_WINDOW,
1888
+ [`deepseek/${DEEPSEEK_VISION_PREVIEW_MODEL}`]: 1_048_576,
1889
+ },
1890
+ modelInputModalities: {
1891
+ "stealth/ox-alpha": ["text", "image"],
1892
+ [`deepseek/${DEEPSEEK_VISION_PREVIEW_MODEL}`]: ["text", "image"],
1893
+ },
1894
+ modelDiscovery: {
1895
+ path: "models",
1896
+ maxResponseBytes: 256 * 1024,
1897
+ maxModels: 256,
1898
+ },
1899
+ // Verified 2026-08-03: public /provider/v1/models returns 51 rows; /chat/completions returns
1900
+ // 401 UNAUTHORIZED without a Bearer key. Primary source: https://commandcode.ai/docs/provider.
1901
+ note: "Command Code Provider API (OpenAI-compatible); API access requires the Provider plan. Use `ocx login command-code` for OAuth account login (imports an existing local Command Code CLI credential when present). Docs: https://commandcode.ai/docs/provider.",
1902
+ },
1903
+ {
1904
+ id: "sambanova",
1905
+ label: "SambaNova Cloud",
1906
+ baseUrl: "https://api.sambanova.ai/v1",
1907
+ adapter: "openai-chat",
1908
+ authKind: "key",
1909
+ dashboardUrl: "https://cloud.sambanova.ai/apis",
1910
+ liveModels: true,
1911
+ preserveCustomDestination: true,
1912
+ apiKeyValidation: "unknown",
1913
+ // SambaNova documents this request field but does not yet support parallel function calls.
1914
+ parallelToolCalls: false,
1915
+ // The public catalog does not report a trustworthy per-model reasoning contract.
1916
+ reasoningEfforts: [],
1917
+ modelDiscovery: {
1918
+ path: "models",
1919
+ maxResponseBytes: 128 * 1024,
1920
+ maxModels: 128,
1921
+ },
1922
+ note: "SambaNova Cloud text-generation models only; private SambaStudio deployment endpoints are outside this preset.",
1923
+ },
1924
+ {
1925
+ id: "nebius",
1926
+ label: "Nebius Token Factory",
1927
+ baseUrl: "https://api.tokenfactory.nebius.com/v1",
1928
+ adapter: "openai-chat",
1929
+ authKind: "key",
1930
+ dashboardUrl: "https://tokenfactory.nebius.com",
1931
+ liveModels: true,
1932
+ preserveCustomDestination: true,
1933
+ // The public tools guide documents single function selection, not parallel tool calls.
1934
+ parallelToolCalls: false,
1935
+ // Missing reasoning metadata must not promote a model to Codex's full fallback ladder.
1936
+ reasoningEfforts: [],
1937
+ modelDiscovery: {
1938
+ path: "models",
1939
+ query: { verbose: "true" },
1940
+ maxResponseBytes: 512 * 1024,
1941
+ maxModels: 512,
1942
+ filter: {
1943
+ // Keep rows whose reported architecture output includes text (for example,
1944
+ // text->text or text+image->text); embedding and image-generation rows are excluded.
1945
+ allOf: [{ path: ["architecture", "modality"], containsAny: ["->text"] }],
1946
+ },
1947
+ },
1948
+ note: "Shared Token Factory text-output inference only; live discovery excludes embedding and image-generation rows.",
1949
+ },
1950
+ {
1951
+ id: "digitalocean",
1952
+ label: "DigitalOcean Serverless Inference",
1953
+ baseUrl: "https://inference.do-ai.run/v1",
1954
+ adapter: "openai-chat",
1955
+ authKind: "key",
1956
+ dashboardUrl: "https://cloud.digitalocean.com/model-studio/manage-keys",
1957
+ liveModels: true,
1958
+ preserveCustomDestination: true,
1959
+ // The Chat Completions contract documents function calls but not universal parallel support.
1960
+ parallelToolCalls: false,
1961
+ // Unknown catalog rows must not inherit Codex's full fallback reasoning ladder.
1962
+ reasoningEfforts: [],
1963
+ modelDiscovery: {
1964
+ path: "models",
1965
+ maxResponseBytes: 256 * 1024,
1966
+ maxModels: 256,
1967
+ filter: {
1968
+ allOf: [{ path: ["id"], equalsAny: DIGITALOCEAN_CHAT_COMPLETION_MODELS }],
1969
+ },
1970
+ },
1971
+ note: "Shared Serverless Inference Chat Completions only; agent-specific, dedicated, Responses-only, embedding, and media-generation models are outside this preset.",
1972
+ },
1973
+ {
1974
+ id: "scaleway",
1975
+ label: "Scaleway Generative APIs",
1976
+ baseUrl: "https://api.scaleway.ai/v1",
1977
+ adapter: "openai-chat",
1978
+ authKind: "key",
1979
+ dashboardUrl: "https://console.scaleway.com/generative-api",
1980
+ liveModels: true,
1981
+ freeTier: true,
1982
+ preserveCustomDestination: true,
1983
+ // Parallel support varies by model; avoid advertising it as a provider-wide capability.
1984
+ parallelToolCalls: false,
1985
+ // The generic `/models` rows carry no trustworthy reasoning metadata.
1986
+ reasoningEfforts: [],
1987
+ modelInputModalities: SCALEWAY_MODEL_INPUT_MODALITIES,
1988
+ modelDiscovery: {
1989
+ path: "models",
1990
+ maxResponseBytes: 128 * 1024,
1991
+ maxModels: 128,
1992
+ filter: {
1993
+ allOf: [{ path: ["id"], equalsAny: SCALEWAY_SERVERLESS_CHAT_MODELS }],
1994
+ },
1995
+ },
1996
+ note: "Shared Generative APIs Serverless Chat Completions only; project-qualified and dedicated deployment hosts require a custom provider.",
1997
+ },
1998
+ {
1999
+ // Primary sources checked 2026-08-08:
2000
+ // - https://featherless.ai/docs/api-overview-and-common-options documents the fixed
2001
+ // OpenAI-compatible base URL, Bearer keys, and Chat Completions.
2002
+ // - https://featherless.ai/docs/api-reference-models documents authenticated plan filtering,
2003
+ // chat capability filtering, popularity sorting, pagination, and per-row tool metadata.
2004
+ // - https://featherless.ai/legal/terms-of-service identifies Featherless as a Delaware LLC,
2005
+ // covers developers building on its APIs, and reserves arbitrary applications for Scale
2006
+ // plans. Maintainer: @olddonkey; no affiliation with Featherless.
2007
+ id: "featherless",
2008
+ label: "Featherless AI",
2009
+ baseUrl: "https://api.featherless.ai/v1",
2010
+ adapter: "openai-chat",
2011
+ authKind: "key",
2012
+ dashboardUrl: "https://featherless.ai/account/api-keys",
2013
+ liveModels: true,
2014
+ preserveCustomDestination: true,
2015
+ // /v1/models is documented as callable authenticated or unauthenticated, so a 2xx catalog
2016
+ // response cannot prove that the supplied Bearer key is valid.
2017
+ apiKeyValidation: "unknown",
2018
+ // Featherless documents tool calling, but not a provider-wide parallel tool-call contract.
2019
+ parallelToolCalls: false,
2020
+ // Reasoning controls use model-specific chat_template_kwargs, not OpenAI reasoning_effort.
2021
+ reasoningEfforts: [],
2022
+ modelDiscovery: {
2023
+ path: "models",
2024
+ query: {
2025
+ available_on_current_plan: "true",
2026
+ capabilities: "chat",
2027
+ page: "1",
2028
+ per_page: "100",
2029
+ sort: "-popularity",
2030
+ },
2031
+ maxResponseBytes: 128 * 1024,
2032
+ maxModels: 100,
2033
+ filter: {
2034
+ // Treat server-side filters as a size optimization, not an authority boundary. A row must
2035
+ // independently prove plan availability, no separate Hugging Face gate, and tool support.
2036
+ allOf: [
2037
+ { path: ["available_on_current_plan"], equalsAny: [true] },
2038
+ { path: ["is_gated"], equalsAny: [false] },
2039
+ { path: ["features", "tool_use"], equalsAny: [true] },
2040
+ ],
2041
+ },
2042
+ },
2043
+ note: "Authenticated first page of popular chat models only; live discovery admits at most 100 plan-available, ungated rows whose metadata explicitly reports tool use.",
2044
+ },
2045
+ {
2046
+ // Primary sources checked 2026-08-08:
2047
+ // - https://novita.ai/docs/api-reference/model-apis-llm-create-chat-completion and
2048
+ // https://novita.ai/docs/api-reference/model-apis-llm-list-models document the fixed
2049
+ // OpenAI-compatible Chat Completions and model-list endpoints.
2050
+ // - https://novita.ai/docs/api-reference/basic-authentication documents Bearer API keys.
2051
+ // - https://novita.ai/legal/terms-of-service (updated 2026-08-05) expressly covers AI
2052
+ // inference APIs, third-party Model Providers, and customer Input/Output processing.
2053
+ // - https://huggingface.co/docs/inference-providers/main/providers/novita lists Novita as an
2054
+ // Inference Providers partner for chat/VLM traffic, independently supporting routing use.
2055
+ // - https://tsdr.uspto.gov/statusview/sn99255805 is the official use-in-commerce record
2056
+ // connecting the NOVITA AI mark to Hivemind Labs, Inc., a Delaware corporation. The mark
2057
+ // application is now abandoned; it is cited only as the public operator-identity record.
2058
+ // Maintainer: @olddonkey; no affiliation with Novita AI or Hivemind Labs, Inc.
2059
+ id: "novita",
2060
+ label: "Novita AI",
2061
+ baseUrl: "https://api.novita.ai/openai/v1",
2062
+ adapter: "openai-chat",
2063
+ authKind: "key",
2064
+ dashboardUrl: "https://novita.ai/settings/key-management",
2065
+ liveModels: true,
2066
+ preserveCustomDestination: true,
2067
+ // The live catalog is public even though the reference shows an Authorization header, so a
2068
+ // successful model fetch cannot prove that a supplied key is valid.
2069
+ apiKeyValidation: "unknown",
2070
+ // The request reference documents tools but not a provider-wide parallel-tool contract.
2071
+ parallelToolCalls: false,
2072
+ // Novita exposes model-specific thinking flags, not an OpenAI reasoning_effort contract.
2073
+ reasoningEfforts: [],
2074
+ modelDiscovery: {
2075
+ path: "models",
2076
+ maxResponseBytes: 512 * 1024,
2077
+ maxModels: 256,
2078
+ filter: {
2079
+ // Require both Novita's chat classification and the exact configured wire endpoint.
2080
+ allOf: [
2081
+ { path: ["model_type"], equalsAny: ["chat"] },
2082
+ { path: ["endpoints"], containsAny: ["chat/completions"] },
2083
+ ],
2084
+ },
2085
+ },
2086
+ note: "Public live catalog filtered to rows that explicitly report chat type and Chat Completions support; key validity remains unknown until an authenticated inference request.",
2087
+ },
1048
2088
  // FREEZE 2026-07-10: exact serverless ids remain auth-gated/unverified. Evidence: devlog/_plan/260710_provider_hardening/003_research_aggregators.md.
1049
2089
  { id: "together", label: "Together", baseUrl: "https://api.together.xyz/v1", adapter: "openai-chat", authKind: "key", dashboardUrl: "https://api.together.xyz/settings/api-keys" },
1050
2090
  { id: "fireworks", label: "Fireworks", baseUrl: "https://api.fireworks.ai/inference/v1", adapter: "openai-chat", authKind: "key", dashboardUrl: "https://fireworks.ai/account/api-keys" },
@@ -1054,7 +2094,9 @@ export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [
1054
2094
  note: "Model data frozen pending Tier-2 entitlement proof",
1055
2095
  },
1056
2096
  {
1057
- id: "moonshot", label: "Moonshot (Kimi API)", baseUrl: "https://api.moonshot.ai/v1", adapter: "openai-chat", authKind: "key",
2097
+ id: "moonshot", label: "Moonshot (Kimi API)", baseUrl: MOONSHOT_INTL_BASE_URL, adapter: "openai-chat", authKind: "key",
2098
+ allowBaseUrlOverride: true,
2099
+ baseUrlChoices: MOONSHOT_BASE_URL_CHOICES,
1058
2100
  dashboardUrl: "https://platform.moonshot.ai/console/api-keys", defaultModel: "kimi-k2.7-code", jawcodeBundle: "moonshot",
1059
2101
  models: KIMI_API_MODELS,
1060
2102
  modelContextWindows: KIMI_API_MODEL_CONTEXT_WINDOWS,
@@ -1066,6 +2108,7 @@ export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [
1066
2108
  noPenaltyModels: KIMI_API_MODELS,
1067
2109
  autoToolChoiceOnlyModels: ["kimi-k2.7-code", "kimi-k2.7-code-highspeed"],
1068
2110
  preserveReasoningContentModels: KIMI_API_MODELS,
2111
+ note: "International default (api.moonshot.ai). China accounts: choose China (.cn) or Custom for api.moonshot.cn.",
1069
2112
  },
1070
2113
  { id: "huggingface", label: "Hugging Face", baseUrl: "https://router.huggingface.co/v1", adapter: "openai-chat", authKind: "key", dashboardUrl: "https://huggingface.co/settings/tokens" },
1071
2114
  // 260715 NIM hardening (issue #126, devlog/_plan/260715_issue126_nim_kimi):
@@ -1081,6 +2124,11 @@ export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [
1081
2124
  // Free pricing, but an API key is still required (free key from build.nvidia.com).
1082
2125
  freeTier: true,
1083
2126
  parallelToolCalls: false,
2127
+ // 260804 issue #956: NIM exposes no input modalities, so vision capability is
2128
+ // classified here. Both lists are verified per-model; unlisted ids stay unclassified
2129
+ // by design (see the comment on NVIDIA_NIM_VISION_MODELS).
2130
+ noVisionModels: NVIDIA_NIM_NO_VISION_MODELS,
2131
+ modelInputModalities: NVIDIA_NIM_VISION_INPUT_MODALITIES,
1084
2132
  noReasoningModels: NVIDIA_NIM_KIMI_MODELS,
1085
2133
  modelReasoningEfforts: Object.fromEntries(NVIDIA_NIM_KIMI_MODELS.map(id => [id, []])),
1086
2134
  preserveReasoningContentModels: NVIDIA_NIM_KIMI_THINKING_MODELS,
@@ -1089,17 +2137,26 @@ export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [
1089
2137
  { id: "venice", label: "Venice", baseUrl: "https://api.venice.ai/api/v1", adapter: "openai-chat", authKind: "key", dashboardUrl: "https://venice.ai/settings/api" },
1090
2138
  // 260710 GLM-5.2 context and path-specific ids: Tier-2 evidence in
1091
2139
  // devlog/_plan/260710_provider_hardening/002_research_cn.md.
2140
+ // 260814: glm-5.3 / glm-5.3[1m] added per docs.z.ai/devpack/latest-model, which lists them as
2141
+ // Coding Plan ids on this same endpoint.
2142
+ // 260815: docs.z.ai/guides/llm/glm-5.3 now publishes the capability table (thinking, streaming,
2143
+ // function calling, caching, structured output) and a 128K output budget, recorded here as the
2144
+ // exact 131_072 every other source in this repo uses for that model. Coding Plan pricing stays
2145
+ // unpublished, so no cost entry is asserted.
1092
2146
  {
1093
2147
  id: "zai", label: "Z.AI — GLM Coding Plan", baseUrl: "https://api.z.ai/api/coding/paas/v4", adapter: "openai-chat", authKind: "key",
1094
- dashboardUrl: "https://z.ai/manage-apikey/apikey-list", defaultModel: "glm-5.2",
1095
- note: "GLM-5.2 coding subscription",
1096
- models: ["glm-5.2", "glm-5.2[1m]", "glm-5.1", "glm-5", "glm-4.6"],
1097
- modelContextWindows: { "glm-5.2": 1_000_000, "glm-5.2[1m]": 1_000_000 },
2148
+ dashboardUrl: "https://z.ai/manage-apikey/apikey-list", defaultModel: "glm-5.3",
2149
+ note: "GLM-5.3 coding subscription",
2150
+ models: ["glm-5.3", "glm-5.3[1m]", "glm-5.2", "glm-5.2[1m]", "glm-5.1", "glm-5", "glm-4.6"],
2151
+ modelContextWindows: { "glm-5.3": 1_000_000, "glm-5.3[1m]": 1_000_000, "glm-5.2": 1_000_000, "glm-5.2[1m]": 1_000_000 },
1098
2152
  // Z.AI's OpenAI path returns 400 code 1211 for bracketed model ids.
1099
2153
  modelSuffixBracketStrip: true,
1100
- noVisionModels: ZAI_GLM_52_MODELS,
1101
- modelReasoningEfforts: Object.fromEntries(ZAI_GLM_52_MODELS.map(id => [id, ZAI_GLM_52_REASONING_EFFORTS])),
1102
- preserveReasoningContentModels: ZAI_GLM_52_MODELS,
2154
+ noVisionModels: ZAI_GLM_5X_MODELS,
2155
+ modelReasoningEfforts: ZAI_GLM_5X_REASONING_EFFORTS,
2156
+ modelDefaultReasoningEfforts: Object.fromEntries(ZAI_GLM_53_MODELS.map(id => [id, "max"])),
2157
+ modelMaxOutputTokens: Object.fromEntries(ZAI_GLM_53_MODELS.map(id => [id, 131_072])),
2158
+ modelSupportsReasoningSummaries: Object.fromEntries(ZAI_GLM_5X_MODELS.map(id => [id, true])),
2159
+ preserveReasoningContentModels: ZAI_GLM_5X_MODELS,
1103
2160
  },
1104
2161
  // Zhipu's domestic BigModel platform: OpenAI-compatible pay-as-you-go on open.bigmodel.cn — a
1105
2162
  // different host and billing product from the `zai` coding-plan subscription above.
@@ -1135,11 +2192,54 @@ export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [
1135
2192
  modelReasoningEffortMap: Object.fromEntries(
1136
2193
  ZHIPU_BIGMODEL_THINKING_TOGGLE_MODELS.map(id => [id, THINKING_TOGGLE_MAP]),
1137
2194
  ),
2195
+ modelSupportsReasoningSummaries: Object.fromEntries(
2196
+ ZHIPU_BIGMODEL_THINKING_TOGGLE_MODELS.map(id => [id, true]),
2197
+ ),
1138
2198
  preserveReasoningContentModels: ZHIPU_BIGMODEL_THINKING_TOGGLE_MODELS,
2199
+ // GLM thinking is a binary toggle (low maps to disabled), so a legitimate
2200
+ // tool round can carry no reasoning at all; never fabricate a placeholder
2201
+ // for it, only replay real recorded text (P2 on #1205).
2202
+ requiresReasoningPlaceholderModels: [],
1139
2203
  // No liveModels: GET /api/paas/v4/models has not been observed to answer on this host, and a
1140
2204
  // false live claim yields an empty picker at runtime. Flip it on once someone verifies it.
1141
2205
  note: "Domestic BigModel pay-as-you-go endpoint (open.bigmodel.cn)",
1142
2206
  },
2207
+ // BigModel's Coding Plan is a SEPARATE endpoint from the pay-as-you-go row above, and that is
2208
+ // the whole reason this one exists. #1100 was reported against
2209
+ // `https://open.bigmodel.cn/api/coding/paas/v4`; the row above covers only `/api/paas/v4`, so
2210
+ // destination enrichment matched nothing, `modelSupportsReasoningSummaries` stayed unset, and
2211
+ // Codex kept dropping the inbound reasoning object — effort displayed as `-`.
2212
+ //
2213
+ // A prefix or fuzzy endpoint match would have been the shortcut. It is also how a config
2214
+ // pointed at one vendor route silently inherits another route's metadata, so endpoints stay
2215
+ // exact and each one gets its own row.
2216
+ //
2217
+ // The id is NOT `glm-cn`, which the free-provider directory already binds to this same coding
2218
+ // path: registering it here would let routedProviderConfig() canonicalize a saved `glm-cn`
2219
+ // config onto this baseUrl. Same reasoning as `zhipu-bigmodel` above.
2220
+ //
2221
+ // Models follow Z.AI's coding-plan list rather than the pay-as-you-go one. This endpoint is
2222
+ // the subscription product, and the reporter's `glm-5.2` is only on that side.
2223
+ {
2224
+ id: "zhipu-bigmodel-coding",
2225
+ label: "Zhipu AI — BigModel Coding Plan",
2226
+ baseUrl: "https://open.bigmodel.cn/api/coding/paas/v4",
2227
+ adapter: "openai-chat",
2228
+ authKind: "key",
2229
+ dashboardUrl: "https://bigmodel.cn/console/usercenter/apikeys",
2230
+ defaultModel: "glm-5.3",
2231
+ models: ["glm-5.3", "glm-5.3[1m]", "glm-5.2", "glm-5.2[1m]", "glm-5.1", "glm-5", "glm-4.6"],
2232
+ jawcodeBundle: "zai",
2233
+ modelContextWindows: { "glm-5.3": 1_000_000, "glm-5.3[1m]": 1_000_000, "glm-5.2": 1_000_000, "glm-5.2[1m]": 1_000_000 },
2234
+ modelSuffixBracketStrip: true,
2235
+ noVisionModels: ZAI_GLM_5X_MODELS,
2236
+ modelReasoningEfforts: ZAI_GLM_5X_REASONING_EFFORTS,
2237
+ modelSupportsReasoningSummaries: Object.fromEntries(ZAI_GLM_5X_MODELS.map(id => [id, true])),
2238
+ preserveReasoningContentModels: ZAI_GLM_5X_MODELS,
2239
+ // No liveModels: the same reasoning as the pay-as-you-go row — an unverified live claim
2240
+ // yields an empty picker at runtime.
2241
+ note: "Domestic BigModel Coding Plan endpoint (open.bigmodel.cn)",
2242
+ },
1143
2243
  { id: "nanogpt", label: "NanoGPT", baseUrl: "https://nano-gpt.com/api/v1", adapter: "openai-chat", authKind: "key", dashboardUrl: "https://nano-gpt.com/api" },
1144
2244
  { id: "synthetic", label: "Synthetic", baseUrl: "https://api.synthetic.new/openai/v1", adapter: "openai-chat", authKind: "key", dashboardUrl: "https://synthetic.new" },
1145
2245
  // SiliconFlow publishes an OpenAI-compatible chat endpoint and a dynamic model catalog. Do not
@@ -1230,10 +2330,10 @@ export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [
1230
2330
  modelInputModalities: VOLCENGINE_PLAN_INPUT_MODALITIES,
1231
2331
  noVisionModels: VOLCENGINE_PLAN_TEXT_ONLY_MODELS,
1232
2332
  modelReasoningEfforts: Object.fromEntries(
1233
- DEEPSEEK_THINKING_MODELS.map(id => [id, DEEPSEEK_THINKING_EFFORTS]),
2333
+ DEEPSEEK_THINKING_MODELS.map(id => [id, deepseekThinkingEffortsFor(id)]),
1234
2334
  ),
1235
2335
  modelReasoningEffortMap: Object.fromEntries(
1236
- DEEPSEEK_THINKING_MODELS.map(id => [id, DEEPSEEK_THINKING_REASONING_MAP]),
2336
+ DEEPSEEK_THINKING_MODELS.map(id => [id, deepseekReasoningMapFor(id)]),
1237
2337
  ),
1238
2338
  preserveReasoningContentModels: DEEPSEEK_THINKING_MODELS,
1239
2339
  note: "Coding tools only. Volcengine restricts Coding Plan quota to supported AI coding tools and warns that using this key for general API calls may suspend the subscription or ban the account. Use the plan key issued by the Ark console.",
@@ -1245,6 +2345,8 @@ export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [
1245
2345
  responsesPath: "/responses",
1246
2346
  adapter: "openai-responses",
1247
2347
  authKind: "key",
2348
+ // Ark's plan route does not document `service_tier`; fail closed like DeepSeek.
2349
+ supportsServiceTier: false,
1248
2350
  preserveCustomDestination: true,
1249
2351
  dashboardUrl: "https://console.volcengine.com/ark/region:ark+cn-beijing/overview",
1250
2352
  defaultModel: "deepseek-v4-pro",
@@ -1265,24 +2367,28 @@ export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [
1265
2367
  adapter: "openai-chat",
1266
2368
  authKind: "key",
1267
2369
  dashboardUrl: "https://bailian.console.aliyun.com/cn-beijing?tab=plan",
1268
- defaultModel: "qwen3.8-max-preview",
2370
+ defaultModel: "qwen3.8-max",
1269
2371
  models: ALIBABA_TOKEN_PLAN_MODELS,
1270
2372
  liveModels: false,
1271
2373
  note: "Token Plan Personal Edition · China (Beijing)",
1272
2374
  modelInputModalities: ALIBABA_TOKEN_PLAN_INPUT_MODALITIES,
1273
2375
  modelContextWindows: {
1274
- "qwen3.8-max-preview": 983_616, "qwen3.7-max": 1_000_000, "qwen3.7-plus": 1_000_000,
1275
- "qwen3.6-flash": 1_000_000, "glm-5.2": 1_000_000, "deepseek-v4-pro": 1_000_000,
2376
+ "qwen3.8-max": 983_616, "qwen3.7-max": 1_000_000, "qwen3.7-plus": 1_000_000,
2377
+ "qwen3.6-flash": 1_000_000, "glm-5.3": 1_000_000, "glm-5.2": 1_000_000, "deepseek-v4-pro": 1_000_000,
1276
2378
  },
1277
2379
  modelReasoningEfforts: {
1278
2380
  ...Object.fromEntries(ALIBABA_TOKEN_PLAN_QWEN_MODELS.map(id => [id, THINKING_BUDGET_EFFORTS])),
2381
+ "qwen3.8-max": QWEN38_REASONING_EFFORTS,
2382
+ "glm-5.3": ZAI_GLM_53_REASONING_EFFORTS,
1279
2383
  "glm-5.2": ZAI_GLM_52_REASONING_EFFORTS,
1280
- "deepseek-v4-pro": DEEPSEEK_THINKING_EFFORTS,
2384
+ "deepseek-v4-pro": deepseekThinkingEffortsFor("deepseek-v4-pro"),
1281
2385
  },
1282
- modelReasoningEffortMap: { "deepseek-v4-pro": DEEPSEEK_THINKING_REASONING_MAP },
1283
- thinkingBudgetModels: ALIBABA_TOKEN_PLAN_QWEN_MODELS,
1284
- preserveReasoningContentModels: ["glm-5.2", "deepseek-v4-pro", "qwen3.8-max-preview", "qwen3.7-max", "qwen3.7-plus", "qwen3.6-flash"],
1285
- noVisionModels: ["glm-5.2", "deepseek-v4-pro"],
2386
+ modelDefaultReasoningEfforts: { "qwen3.8-max": "xhigh" },
2387
+ modelReasoningEffortMap: { "deepseek-v4-pro": deepseekReasoningMapFor("deepseek-v4-pro") },
2388
+ directReasoningEffortModels: ["qwen3.8-max"],
2389
+ thinkingBudgetModels: ALIBABA_TOKEN_PLAN_QWEN_MODELS.filter(id => id !== "qwen3.8-max"),
2390
+ preserveReasoningContentModels: ["glm-5.3", "glm-5.2", "deepseek-v4-pro", "qwen3.8-max", "qwen3.7-max", "qwen3.7-plus", "qwen3.6-flash"],
2391
+ noVisionModels: ["glm-5.3", "glm-5.2", "deepseek-v4-pro"],
1286
2392
  },
1287
2393
  {
1288
2394
  id: "alibaba-token-plan-intl",
@@ -1300,29 +2406,31 @@ export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [
1300
2406
  metadataModelIdNormalize: "case-insensitive",
1301
2407
  modelInputModalities: ALIBABA_INTL_TOKEN_PLAN_INPUT_MODALITIES,
1302
2408
  modelContextWindows: {
1303
- "qwen3.8-max-preview": 983_616,
2409
+ "qwen3.8-max": 983_616,
1304
2410
  "qwen3.7-max": 1_000_000, "qwen3.7-plus": 1_000_000, "qwen3.6-plus": 1_000_000, "qwen3.6-flash": 1_000_000,
1305
2411
  "deepseek-v4-pro": 1_000_000, "deepseek-v4-flash": 1_000_000, "deepseek-v3.2": 131_072,
1306
2412
  "kimi-k2.7-code": 262_144, "kimi-k2.6": 262_144, "kimi-k2.5": 262_144,
1307
- "glm-5.2": 1_000_000, "glm-5.1": 1_000_000, "glm-5": 1_000_000,
2413
+ "glm-5.3": 1_000_000, "glm-5.2": 1_000_000, "glm-5.1": 1_000_000, "glm-5": 1_000_000,
1308
2414
  "MiniMax-M2.5": 204_800,
1309
2415
  },
1310
2416
  modelReasoningEfforts: {
1311
2417
  ...Object.fromEntries(ALIBABA_INTL_TOKEN_PLAN_QWEN_MODELS.map(id => [id, THINKING_BUDGET_EFFORTS])),
1312
- "qwen3.8-max-preview": ["low", "high", "xhigh"],
2418
+ "qwen3.8-max": QWEN38_REASONING_EFFORTS,
2419
+ "glm-5.3": ZAI_GLM_53_REASONING_EFFORTS,
1313
2420
  "glm-5.2": ZAI_GLM_52_REASONING_EFFORTS,
1314
- "deepseek-v4-pro": DEEPSEEK_THINKING_EFFORTS,
1315
- "deepseek-v4-flash": DEEPSEEK_THINKING_EFFORTS,
2421
+ "deepseek-v4-pro": deepseekThinkingEffortsFor("deepseek-v4-pro"),
2422
+ "deepseek-v4-flash": deepseekThinkingEffortsFor("deepseek-v4-flash"),
1316
2423
  },
1317
2424
  modelReasoningEffortMap: {
1318
- "deepseek-v4-pro": DEEPSEEK_THINKING_REASONING_MAP,
1319
- "deepseek-v4-flash": DEEPSEEK_THINKING_REASONING_MAP,
2425
+ "deepseek-v4-pro": deepseekReasoningMapFor("deepseek-v4-pro"),
2426
+ "deepseek-v4-flash": deepseekReasoningMapFor("deepseek-v4-flash"),
1320
2427
  },
1321
- thinkingBudgetModels: ALIBABA_INTL_TOKEN_PLAN_QWEN_MODELS,
1322
- preserveReasoningContentModels: ["glm-5.2", "deepseek-v4-pro", "deepseek-v4-flash", "qwen3.8-max-preview", "qwen3.7-max", "qwen3.7-plus", "qwen3.6-plus", "qwen3.6-flash"],
1323
- noVisionModels: ["deepseek-v4-pro", "deepseek-v4-flash", "deepseek-v3.2", "glm-5.2", "glm-5.1", "glm-5", "MiniMax-M2.5"],
2428
+ directReasoningEffortModels: ["qwen3.8-max"],
2429
+ thinkingBudgetModels: ALIBABA_INTL_TOKEN_PLAN_QWEN_MODELS.filter(id => id !== "qwen3.8-max"),
2430
+ preserveReasoningContentModels: ["glm-5.3", "glm-5.2", "deepseek-v4-pro", "deepseek-v4-flash", "qwen3.8-max", "qwen3.7-max", "qwen3.7-plus", "qwen3.6-plus", "qwen3.6-flash"],
2431
+ noVisionModels: ["deepseek-v4-pro", "deepseek-v4-flash", "deepseek-v3.2", "glm-5.3", "glm-5.2", "glm-5.1", "glm-5", "MiniMax-M2.5"],
1324
2432
  noReasoningModels: ["kimi-k2.7-code", "kimi-k2.6", "kimi-k2.5", "deepseek-v3.2", "glm-5.1", "glm-5", "MiniMax-M2.5"],
1325
- modelDefaultReasoningEfforts: { "qwen3.8-max-preview": "xhigh" },
2433
+ modelDefaultReasoningEfforts: { "qwen3.8-max": "xhigh" },
1326
2434
  },
1327
2435
  // NEEDS_HUMAN 2026-07-10: kept for config compatibility, but this is a dashboard URL,
1328
2436
  // no /models endpoint is documented, and tools are silently ignored upstream per docs.parallel.ai.
@@ -1352,10 +2460,10 @@ export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [
1352
2460
  authKind: "key",
1353
2461
  dashboardUrl: "https://ollama.com/settings/keys",
1354
2462
  // Live IDs verified 2026-07-10; qwen3-coder:480b retires 2026-07-15.
1355
- models: ["glm-5.2", "deepseek-v4-pro", "qwen3-coder:480b", "gpt-oss:120b", "kimi-k2.6", "minimax-m3", "qwen3.5:397b", "gemma4:31b"],
1356
- defaultModel: "glm-5.2",
2463
+ models: ["glm-5.3", "glm-5.2", "deepseek-v4-pro", "qwen3-coder:480b", "gpt-oss:120b", "kimi-k2.6", "minimax-m3", "qwen3.5:397b", "gemma4:31b"],
2464
+ defaultModel: "glm-5.3",
1357
2465
  noVisionModels: [
1358
- "glm-5.2", "glm-5.1", "glm-5", "glm-4.7",
2466
+ "glm-5.3", "glm-5.2", "glm-5.1", "glm-5", "glm-4.7",
1359
2467
  "minimax-m2.7", "minimax-m2.5", "minimax-m2.1",
1360
2468
  "nemotron-3-ultra", "nemotron-3-super",
1361
2469
  "deepseek-v4-pro", "deepseek-v4-flash",
@@ -1372,6 +2480,10 @@ export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [
1372
2480
  modelDefaultReasoningEfforts: { "MiniMax-M3": "medium" },
1373
2481
  modelReasoningEffortMap: { "MiniMax-M3": MINIMAX_M3_REASONING_EFFORT_MAP },
1374
2482
  preserveReasoningContentModels: MINIMAX_MODELS,
2483
+ // MiniMax-M3 low effort maps to thinking disabled, so a legitimate tool
2484
+ // round can carry no reasoning at all; only replay real recorded text,
2485
+ // never a fabricated placeholder (chatgpt-codex-connector P2 on #1205).
2486
+ requiresReasoningPlaceholderModels: [],
1375
2487
  reasoningSplitModels: MINIMAX_MODELS,
1376
2488
  thinkingToggleModels: ["MiniMax-M3"],
1377
2489
  jawcodeBundle: "minimax", metadataModelIdNormalize: "case-insensitive", note: "Subscription Key or API Key",
@@ -1384,6 +2496,7 @@ export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [
1384
2496
  modelDefaultReasoningEfforts: { "MiniMax-M3": "medium" },
1385
2497
  modelReasoningEffortMap: { "MiniMax-M3": MINIMAX_M3_REASONING_EFFORT_MAP },
1386
2498
  preserveReasoningContentModels: MINIMAX_MODELS,
2499
+ requiresReasoningPlaceholderModels: [],
1387
2500
  reasoningSplitModels: MINIMAX_MODELS,
1388
2501
  thinkingToggleModels: ["MiniMax-M3"],
1389
2502
  jawcodeBundle: "minimax", metadataModelIdNormalize: "case-insensitive", note: "中国区 Subscription Key",
@@ -1407,7 +2520,33 @@ export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [
1407
2520
  autoToolChoiceOnlyModels: KIMI_AUTO_TOOL_CHOICE_ONLY_MODELS,
1408
2521
  preserveReasoningContentModels: KIMI_THINKING_MODELS,
1409
2522
  },
1410
- { id: "opencode-zen", label: "opencode zen", baseUrl: "https://opencode.ai/zen/v1", adapter: "openai-chat", authKind: "key", dashboardUrl: "https://opencode.ai/auth" },
2523
+ {
2524
+ id: "opencode-zen", label: "opencode zen", baseUrl: "https://opencode.ai/zen/v1", adapter: "openai-chat", authKind: "key", dashboardUrl: "https://opencode.ai/auth",
2525
+ // Same opencode.ai/zen/v1 gateway as `opencode-free` (keyed tier): DeepSeek thinking mode
2526
+ // requires the assistant's original reasoning_content to be replayed on tool-call
2527
+ // continuations, or the gateway answers HTTP 400 (issues #950/#994). Mirror the DeepSeek
2528
+ // reasoning + thinking metadata so `opencode-zen/deepseek-v4-flash-free` — and the other
2529
+ // Zen DeepSeek thinking models — never serialize a bare tool-call turn.
2530
+ note: "Keyed OpenCode Zen gateway. Free models on this tier are often short-window rate-limited at roughly 15-20 requests/minute (community-measured; OpenCode does not publish RPM). Zen may return generic 429s without Retry-After / X-RateLimit headers; when Retry-After is omitted, opencodex adds a synthetic backoff hint (upstream Retry-After still wins). Distinct from the keyless opencode-free desktop quota (~200 Big Pickle/free-model requests per 5 hours). Docs: https://opencode.ai/docs/zen/. Free-model prompts may be retained for training — do not send confidential material.",
2531
+ modelReasoningEfforts: Object.fromEntries(
2532
+ [...DEEPSEEK_THINKING_MODELS, ...OPENCODE_FREE_DEEPSEEK_MODELS].map(id => [id, deepseekThinkingEffortsFor(id)]),
2533
+ ),
2534
+ modelReasoningEffortMap: Object.fromEntries(
2535
+ [...DEEPSEEK_THINKING_MODELS, ...OPENCODE_FREE_DEEPSEEK_MODELS].map(id => [id, deepseekReasoningMapFor(id)]),
2536
+ ),
2537
+ preserveReasoningContentModels: [...DEEPSEEK_THINKING_MODELS, ...OPENCODE_FREE_DEEPSEEK_MODELS],
2538
+ // Same Zen gateway as opencode-free: Ox Alpha Free (1M multimodal stealth model)
2539
+ // and the DeepSeek vision preview (merges into deepseek-v4-flash later).
2540
+ modelContextWindows: {
2541
+ [OPENCODE_OX_ALPHA_FREE_MODEL]: OX_ALPHA_CONTEXT_WINDOW,
2542
+ [DEEPSEEK_VISION_PREVIEW_MODEL]: 1_048_576,
2543
+ },
2544
+ modelInputModalities: {
2545
+ [OPENCODE_OX_ALPHA_FREE_MODEL]: ["text", "image"],
2546
+ [DEEPSEEK_VISION_PREVIEW_MODEL]: ["text", "image"],
2547
+ },
2548
+ noVisionModels: [...OPENCODE_ZEN_TEXT_ONLY_MODELS, ...DEEPSEEK_THINKING_MODELS],
2549
+ },
1411
2550
  { id: "vercel-ai-gateway", label: "Vercel AI Gateway", baseUrl: "https://ai-gateway.vercel.sh/v1", adapter: "openai-chat", authKind: "key", dashboardUrl: "https://vercel.com/dashboard" },
1412
2551
  {
1413
2552
  id: "opencode-free",
@@ -1418,17 +2557,59 @@ export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [
1418
2557
  keyOptional: true,
1419
2558
  featured: true,
1420
2559
  liveModels: true,
1421
- note: "No key needed — public desktop tier. OpenCode currently advertises about 200 Big Pickle/free-model requests per 5 hours. Free models are discovered live from Zen. Data use: per OpenCode's Zen docs (https://opencode.ai/docs/zen/), prompts sent to free models may be retained and used for training/improvement — do not send confidential material through this provider.",
2560
+ note: "No key needed — public desktop tier. OpenCode currently advertises about 200 Big Pickle/free-model requests per 5 hours. The same Zen gateway can also short-window rate-limit free models at roughly 15-20 requests/minute, and may return generic 429s without Retry-After (opencodex synthesizes backoff only when that header is omitted). Free models are discovered live from Zen. Data use: per OpenCode's Zen docs (https://opencode.ai/docs/zen/), prompts sent to free models may be retained and used for training/improvement — do not send confidential material through this provider.",
1422
2561
  dashboardUrl: "https://opencode.ai",
1423
2562
  staticHeaders: {
2563
+ // Zen answers a bare runtime User-Agent (Bun/x.y.z) more aggressively than a client
2564
+ // that identifies itself, which is what the 429 in #2067 traced to. The value is
2565
+ // deliberately unversioned: a pinned "opencode-cli/<version>" is a claim about an
2566
+ // install we do not have and goes stale on the vendor's schedule, not ours.
2567
+ // Corroboration, not authority: OmniRoute — an independent open-source broker against
2568
+ // the same Zen upstream — defaults to exactly this pair (userAgent "opencode", client
2569
+ // "desktop") in open-sse/executors/opencode.ts, and got there by RETREATING from its
2570
+ // own earlier "opencode-cli/1.0.0" pin. An operator can still override either value
2571
+ // through the provider headers API; user headers win case-insensitively at route time.
2572
+ "User-Agent": "opencode",
1424
2573
  "x-opencode-client": "desktop",
1425
2574
  },
1426
- modelReasoningEfforts: Object.fromEntries(OPENCODE_FREE_DEEPSEEK_MODELS.map(id => [id, DEEPSEEK_THINKING_EFFORTS])),
1427
- modelReasoningEffortMap: Object.fromEntries(OPENCODE_FREE_DEEPSEEK_MODELS.map(id => [id, DEEPSEEK_THINKING_REASONING_MAP])),
2575
+ modelReasoningEfforts: Object.fromEntries(OPENCODE_FREE_DEEPSEEK_MODELS.map(id => [id, deepseekThinkingEffortsFor(id)])),
2576
+ modelReasoningEffortMap: Object.fromEntries(OPENCODE_FREE_DEEPSEEK_MODELS.map(id => [id, deepseekReasoningMapFor(id)])),
1428
2577
  preserveReasoningContentModels: OPENCODE_FREE_DEEPSEEK_MODELS,
1429
- noVisionModels: OPENCODE_FREE_DEEPSEEK_MODELS,
2578
+ // Ox Alpha Free (`x-preview-f-free`): the OpenRouter stealth model on Zen's
2579
+ // free tier — 1,048,576 context, text+image input. Deliberately NOT in the
2580
+ // text-only list below. The DeepSeek vision preview id is preemptive
2581
+ // metadata for when Zen starts serving it (merges into v4-flash later).
2582
+ modelContextWindows: {
2583
+ [OPENCODE_OX_ALPHA_FREE_MODEL]: OX_ALPHA_CONTEXT_WINDOW,
2584
+ [DEEPSEEK_VISION_PREVIEW_MODEL]: 1_048_576,
2585
+ },
2586
+ modelInputModalities: {
2587
+ [OPENCODE_OX_ALPHA_FREE_MODEL]: ["text", "image"],
2588
+ [DEEPSEEK_VISION_PREVIEW_MODEL]: ["text", "image"],
2589
+ },
2590
+ // Same Zen roster behind the same base URL, so it carries the same measured
2591
+ // text-only list rather than only its DeepSeek member (#1043).
2592
+ noVisionModels: OPENCODE_ZEN_TEXT_ONLY_MODELS,
1430
2593
  },
1431
2594
  { id: "xiaomi", label: "Xiaomi MiMo", baseUrl: "https://api.xiaomimimo.com/anthropic", adapter: "anthropic", authKind: "key", dashboardUrl: "https://xiaomimimo.com", defaultModel: "mimo-v2.5-pro" },
2595
+ // Xiaomi's public OpenAI-compatible endpoint is a distinct transport from both the Anthropic
2596
+ // preset above and the paid token-plan host below. Keep a separate fixed-destination contract
2597
+ // so existing custom providers are never retargeted while the official route receives the
2598
+ // strict reasoning ladder its validator enforces (#1483).
2599
+ {
2600
+ id: "xiaomi-mimo",
2601
+ label: "Xiaomi MiMo (OpenAI Chat)",
2602
+ baseUrl: "https://api.xiaomimimo.com/v1",
2603
+ adapter: "openai-chat",
2604
+ authKind: "key",
2605
+ dashboardUrl: "https://platform.xiaomimimo.com/console/balance",
2606
+ defaultModel: "mimo-v2.5",
2607
+ models: ["mimo-v2.5"],
2608
+ reasoningEfforts: ["low", "medium", "high"],
2609
+ reasoningEffortMap: { xhigh: "high", max: "high", ultra: "high" },
2610
+ preserveCustomDestination: true,
2611
+ note: "Official Xiaomi MiMo OpenAI-compatible Chat endpoint. The upstream validator accepts reasoning_effort none/low/medium/high; higher Codex tiers are clamped to high.",
2612
+ },
1432
2613
  { id: "kilo", label: "Kilo", baseUrl: "https://api.kilo.ai/api/gateway", adapter: "openai-chat", authKind: "key", dashboardUrl: "https://kilo.ai" },
1433
2614
  {
1434
2615
  id: "mimo-free",
@@ -1442,13 +2623,48 @@ export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [
1442
2623
  dashboardUrl: "https://xiaomimimo.com",
1443
2624
  defaultModel: "mimo-auto",
1444
2625
  models: ["mimo-auto"],
2626
+ reasoningEfforts: ["low", "medium", "high"],
2627
+ reasoningEffortMap: { xhigh: "high", max: "high", ultra: "high" },
1445
2628
  note: "No key needed — uses Xiaomi MiMo's free public tier (limited-time offer). A JWT is bootstrapped automatically with an anonymous random client id stored locally. The endpoint contract mirrors the official MiMoCode client and is not publicly documented — Xiaomi may change or restrict it at any time. Prompts may be processed/retained by Xiaomi; do not send confidential material.",
1446
2629
  },
2630
+ // Xiaomi MiMo paid token plan. Separate host and wire from both `xiaomi` (Anthropic) and
2631
+ // `mimo-free` (free tier, bespoke adapter), so it needs its own entry rather than a variant.
2632
+ //
2633
+ // Pinned to openai-chat deliberately (#1158). The endpoint answers the Responses wire for
2634
+ // plain turns, which is why users configuring it by hand pick `openai-responses` — MiMo
2635
+ // documents Responses support. But its gateway rejects `type: "custom"` tools with
2636
+ // `400 responses_feature_not_supported`, and `apply_patch` is a custom tool, so every agentic
2637
+ // turn fails while chat turns succeed. The Chat path lowers custom tools to `{input: string}`
2638
+ // functions and restores them as `custom_tool_call`, so the capability survives intact.
2639
+ // Stripping the tools instead would stop the 400 and disable the agent loop.
2640
+ {
2641
+ id: "mimo",
2642
+ label: "Xiaomi MiMo (token plan)",
2643
+ baseUrl: "https://token-plan-cn.xiaomimimo.com/v1",
2644
+ adapter: "openai-chat",
2645
+ authKind: "key",
2646
+ dashboardUrl: "https://xiaomimimo.com",
2647
+ defaultModel: "mimo-v2.5-pro",
2648
+ models: ["mimo-v2.5-pro", "mimo-v2.5"],
2649
+ // The gateway validates the ladder strictly and rejects anything above `high`.
2650
+ reasoningEfforts: ["low", "medium", "high"],
2651
+ reasoningEffortMap: { xhigh: "high", max: "high", ultra: "high" },
2652
+ // Live token-plan verification (#1927): the Pro route rejects image input while
2653
+ // mimo-v2.5 accepts it natively. Keep this provider-scoped so a hand-rolled
2654
+ // provider with the same id but another destination does not inherit the claim.
2655
+ noVisionModels: ["mimo-v2.5-pro"],
2656
+ // A user may already have hand-rolled a provider under this id against a different host;
2657
+ // without this, routedProviderConfig() would canonicalize their base URL onto ours and send
2658
+ // their key somewhere they did not choose.
2659
+ preserveCustomDestination: true,
2660
+ note: "Xiaomi MiMo paid token plan. Pinned to the Chat wire: the Responses endpoint rejects freeform (custom) tools such as apply_patch with 400 responses_feature_not_supported, so agentic turns fail there while plain turns succeed. Reasoning tiers above high are clamped.",
2661
+ },
1447
2662
  { id: "cloudflare-ai-gateway", label: "Cloudflare AI Gateway", baseUrl: "https://gateway.ai.cloudflare.com/v1/{account-id}/{gateway}/anthropic", adapter: "anthropic", authKind: "key", dashboardUrl: "https://dash.cloudflare.com/?to=/:account/ai/ai-gateway" },
1448
2663
  {
1449
2664
  // Cloudflare Workers AI: OpenAI-compatible endpoint. The base URL contains {account_id}
1450
2665
  // which must be resolved by the user at setup time. Model IDs use the @cf/ prefix.
1451
2666
  // Live-verified 2026-07-21 against https://developers.cloudflare.com/workers-ai/models/
2667
+ // Official search is sibling to /ai/v1 (GET .../ai/models/search?format=openrouter).
1452
2668
  id: "cloudflare-workers-ai", label: "Cloudflare Workers AI",
1453
2669
  baseUrl: "https://api.cloudflare.com/client/v4/accounts/{account_id}/ai/v1",
1454
2670
  adapter: "openai-chat", authKind: "key", freeTier: true,
@@ -1459,9 +2675,17 @@ export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [
1459
2675
  "@cf/qwen/qwq-32b",
1460
2676
  "@cf/deepseek-ai/deepseek-r1-distill-qwen-32b",
1461
2677
  "@cf/moonshotai/kimi-k2.7-code",
2678
+ "@cf/zai-org/glm-5.3",
1462
2679
  "@cf/zai-org/glm-5.2",
1463
2680
  "@cf/mistralai/mistral-small-3.1-24b-instruct",
1464
2681
  ],
2682
+ liveModels: true,
2683
+ modelDiscovery: {
2684
+ path: "../models/search",
2685
+ query: { format: "openrouter", per_page: "1000" },
2686
+ stripIdPrefix: "workers-ai/",
2687
+ maxModels: 256,
2688
+ },
1465
2689
  note: "Workers AI · Free tier included · Account ID required in base URL",
1466
2690
  },
1467
2691
  // FREEZE 2026-07-10: /models was auth-gated under key login. OAuth device-flow + copilot_internal
@@ -1476,18 +2700,83 @@ export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [
1476
2700
  featured: false,
1477
2701
  dashboardUrl: "https://github.com/settings/copilot",
1478
2702
  liveModels: true,
1479
- models: ["gpt-4o", "gpt-4.1", "gpt-4.1-mini", "claude-sonnet-4", "gemini-2.5-pro"],
2703
+ models: ["gpt-4o", "gpt-4.1", "gpt-4.1-mini", "claude-sonnet-4", "gemini-2.5-pro", "gpt-5-mini", "gpt-5.3-codex", "gpt-5.4", "gpt-5.4-mini", "gpt-5.5", "gpt-5.6-luna", "gpt-5.6-sol", "gpt-5.6-terra"],
1480
2704
  defaultModel: "gpt-4o",
2705
+ // Copilot fronts a mixed-wire catalog: these models reject /chat/completions for
2706
+ // real Codex-agent traffic (function tools + reasoning), so every inbound wire
2707
+ // rides Responses. Evidence: issue #748 field runs, pi.dev/models/github-copilot/*
2708
+ // wire declarations, BerriAI/litellm#23332 (gpt-5.4), JetBrains LLM-29711
2709
+ // (gpt-5.6-sol). gpt-5.4-nano is deliberately absent — it has no field report; a
2710
+ // user can opt it in with an explicit modelAdapters entry, which always wins.
2711
+ modelWireDefaults: {
2712
+ "gpt-5.3-codex": "openai-responses",
2713
+ "gpt-5.4": "openai-responses",
2714
+ "gpt-5.4-mini": "openai-responses",
2715
+ "gpt-5.5": "openai-responses",
2716
+ "gpt-5.6-luna": "openai-responses",
2717
+ "gpt-5.6-sol": "openai-responses",
2718
+ "gpt-5.6-terra": "openai-responses",
2719
+ },
1481
2720
  note: "Experimental unofficial Copilot bridge. Logs in via GitHub device flow using the public VS Code OAuth client id, then exchanges for a short-lived Copilot API token (copilot_internal). Requires an active Copilot subscription. GitHub may tighten or revoke this path; do not send confidential material you would not paste into Copilot Chat.",
1482
2721
  },
1483
2722
  // FREEZE 2026-07-10: no public OpenAI-compatible endpoint is documented. Evidence: devlog/_plan/260710_provider_hardening/003_research_aggregators.md.
1484
2723
  { id: "gitlab-duo", label: "GitLab Duo", baseUrl: "https://cloud.gitlab.com/ai/v1/proxy/openai/v1", adapter: "openai-chat", authKind: "key", dashboardUrl: "https://gitlab.com/-/user_settings/personal_access_tokens" },
1485
2724
  ];
1486
2725
 
2726
+ export function providerRegistryFastWireError(
2727
+ entry: Pick<ProviderRegistryEntry, "fastWire" | "supportsServiceTier" | "modelSupportsServiceTier">,
2728
+ ): string | null {
2729
+ return fastWireDeclarationError(entry);
2730
+ }
2731
+
2732
+ for (const entry of PROVIDER_REGISTRY) {
2733
+ const error = providerRegistryFastWireError(entry);
2734
+ if (error) throw new TypeError(`Invalid provider registry entry ${entry.id}: ${error}`);
2735
+ }
2736
+
1487
2737
  export function getProviderRegistryEntry(id: string): ProviderRegistryEntry | undefined {
1488
2738
  return PROVIDER_REGISTRY.find(entry => entry.id === id);
1489
2739
  }
1490
2740
 
2741
+ /**
2742
+ * Merge a registry row's `staticHeaders` beneath a provider's own headers.
2743
+ *
2744
+ * The field is documented as "merged into every upstream request for this provider", but that
2745
+ * was only ever true for a freshly seeded config: `providerConfigSeed` copies the block once
2746
+ * (`derive.ts`), `enrichProviderFromCatalog` fills it only when the whole block is absent, and
2747
+ * nothing merged it at request time. So an install that predates a header — or that saved any
2748
+ * header of its own — never received the new one, which is exactly what #2067 would have
2749
+ * shipped for every existing opencode-free user.
2750
+ *
2751
+ * The comparison is case-insensitive on purpose. HTTP header names are case-insensitive, but a
2752
+ * plain object spread is not: merging a registry `User-Agent` over a user's `user-agent`
2753
+ * produces two entries that `Headers` serializes as one comma-joined value
2754
+ * ("opencode, custom-agent"), which is a corrupted request rather than an override. The user's
2755
+ * spelling and value both win; the registry only fills names the user has not spoken for.
2756
+ */
2757
+ export function mergeRegistryStaticHeaders(
2758
+ staticHeaders: Record<string, string> | undefined,
2759
+ userHeaders: Record<string, string> | undefined,
2760
+ ): Record<string, string> | undefined {
2761
+ if (!staticHeaders) return userHeaders;
2762
+ if (!userHeaders) return { ...staticHeaders };
2763
+ const claimed = new Set(Object.keys(userHeaders).map(name => name.toLowerCase()));
2764
+ const merged: Record<string, string> = { ...userHeaders };
2765
+ for (const [name, value] of Object.entries(staticHeaders)) {
2766
+ if (!claimed.has(name.toLowerCase())) merged[name] = value;
2767
+ }
2768
+ return merged;
2769
+ }
2770
+
2771
+ /** Whether this registry row's per-model service-tier evidence applies to one configured target. */
2772
+ export function registryModelServiceTierCapabilityApplies(
2773
+ entry: Pick<ProviderRegistryEntry, "modelServiceTierCapabilityBaseUrlGuard">,
2774
+ provider: Pick<OcxProviderConfig, "baseUrl">,
2775
+ ): boolean {
2776
+ const guard = entry.modelServiceTierCapabilityBaseUrlGuard;
2777
+ return guard === undefined || guard(provider.baseUrl);
2778
+ }
2779
+
1491
2780
  function normalizedProviderEndpoint(value: string): string {
1492
2781
  const trimmed = value.trim();
1493
2782
  try {
@@ -1567,12 +2856,41 @@ export function providerModelWireDefault(
1567
2856
  if (!entry?.modelWireDefaults || !providerMatchesRegistryTransport(id, provider)) return undefined;
1568
2857
  const declared = entry.modelWireDefaults[modelId.trim().toLowerCase()];
1569
2858
  if (declared === undefined) return undefined;
1570
- // A bare string applies to every inbound; the object form only to the listed ones.
1571
- if (typeof declared !== "string" && !declared.inbound.includes(inbound)) return undefined;
2859
+ // A bare string applies to every inbound/auth mode; the object form may narrow either.
2860
+ if (typeof declared !== "string") {
2861
+ if (!declared.inbound.includes(inbound)) return undefined;
2862
+ const authMode = provider.authMode ?? entry.authKind;
2863
+ if (declared.authModes && !declared.authModes.includes(authMode)) return undefined;
2864
+ }
1572
2865
  const wire = typeof declared === "string" ? declared : declared.wire;
1573
2866
  return wire !== undefined && allowedWires.has(wire) ? wire : undefined;
1574
2867
  }
1575
2868
 
2869
+ /** Resolve a registry-only upstream-streaming compatibility hint for Responses turns. */
2870
+ export function providerModelResponsesUpstreamStreaming(
2871
+ id: string,
2872
+ provider: Pick<OcxProviderConfig, "baseUrl" | "adapter"> & Partial<Pick<OcxProviderConfig, "authMode">>,
2873
+ modelId: string,
2874
+ ): boolean | undefined {
2875
+ const entry = getProviderRegistryEntry(id);
2876
+ if (!entry?.modelResponsesUpstreamStreaming || !providerMatchesRegistryTransport(id, provider)) return undefined;
2877
+ return entry.modelResponsesUpstreamStreaming[modelId.trim().toLowerCase()];
2878
+ }
2879
+
2880
+ /** Resolve a registry-only terminal-repair policy for native Responses streams. */
2881
+ export function providerModelResponsesTerminalRepair(
2882
+ id: string,
2883
+ provider: Pick<OcxProviderConfig, "baseUrl" | "adapter"> & Partial<Pick<OcxProviderConfig, "authMode">>,
2884
+ modelId: string,
2885
+ ): ResponsesTerminalRepairPolicy | undefined {
2886
+ const entry = getProviderRegistryEntry(id);
2887
+ if (!entry?.modelResponsesTerminalRepair || !providerMatchesRegistryTransport(id, provider)) return undefined;
2888
+ const policy = entry.modelResponsesTerminalRepair[modelId.trim().toLowerCase()];
2889
+ const graceMs = Math.floor(policy?.graceMs ?? 0);
2890
+ if (!Number.isFinite(graceMs) || graceMs <= 0) return undefined;
2891
+ return { graceMs };
2892
+ }
2893
+
1576
2894
  /**
1577
2895
  * Effective Codex account mode for a provider. For canonical `openai`, a valid persisted
1578
2896
  * `codexAccountMode` on the provider config wins and a missing/invalid value defaults to