@iislee/opencodex 2.11.0 → 2.35.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 (733) hide show
  1. package/AGENTS_INSTALL.md +109 -0
  2. package/README.md +117 -22
  3. package/bin/ocx.mjs +245 -88
  4. package/bin/package-main.mjs +1 -1
  5. package/gui/dist/assets/index-Be1sDpWw.css +1 -0
  6. package/gui/dist/assets/index-D4OKgUV4.js +114 -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 +21 -10
  42. package/src/adapters/anthropic-output-schema.ts +137 -0
  43. package/src/adapters/anthropic.ts +475 -62
  44. package/src/adapters/base.ts +82 -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 +637 -0
  48. package/src/adapters/cursor/call-id.ts +44 -0
  49. package/src/adapters/cursor/catalog.ts +541 -0
  50. package/src/adapters/cursor/checkpoint-store.ts +308 -0
  51. package/src/adapters/cursor/cursor-errors.ts +144 -6
  52. package/src/adapters/cursor/discovery.ts +122 -14
  53. package/src/adapters/cursor/effort-map.ts +106 -4
  54. package/src/adapters/cursor/envelope-echo.ts +290 -0
  55. package/src/adapters/cursor/framing.ts +39 -0
  56. package/src/adapters/cursor/h2-pool.ts +123 -0
  57. package/src/adapters/cursor/http1-bidi.ts +361 -0
  58. package/src/adapters/cursor/images.ts +704 -0
  59. package/src/adapters/cursor/live-models.ts +180 -59
  60. package/src/adapters/cursor/live-transport.ts +623 -170
  61. package/src/adapters/cursor/message-mapper.ts +4 -1
  62. package/src/adapters/cursor/native-exec-common.ts +23 -2
  63. package/src/adapters/cursor/native-exec-desktop.ts +23 -0
  64. package/src/adapters/cursor/native-exec-fs.ts +10 -7
  65. package/src/adapters/cursor/native-exec-network.ts +1 -1
  66. package/src/adapters/cursor/native-exec-shell.ts +5 -3
  67. package/src/adapters/cursor/native-exec.ts +121 -14
  68. package/src/adapters/cursor/protobuf-events.ts +829 -11
  69. package/src/adapters/cursor/protobuf-request.ts +447 -75
  70. package/src/adapters/cursor/request-builder.ts +271 -35
  71. package/src/adapters/cursor/tool-definitions.ts +252 -12
  72. package/src/adapters/cursor/tool-result-normalize.ts +115 -0
  73. package/src/adapters/cursor/transport.ts +22 -0
  74. package/src/adapters/cursor/types.ts +48 -1
  75. package/src/adapters/cursor.ts +336 -39
  76. package/src/adapters/exec-tool-result-normalize.ts +99 -0
  77. package/src/adapters/google-antigravity-replay.ts +713 -41
  78. package/src/adapters/google-antigravity-wire.ts +48 -10
  79. package/src/adapters/google-errors.ts +44 -12
  80. package/src/adapters/google-http.ts +38 -10
  81. package/src/adapters/google-tool-schema.ts +80 -15
  82. package/src/adapters/google-truncation.ts +11 -0
  83. package/src/adapters/google.ts +742 -86
  84. package/src/adapters/identity.ts +39 -6
  85. package/src/adapters/image.ts +1 -1
  86. package/src/adapters/kiro-constants.ts +27 -0
  87. package/src/adapters/kiro-errors.ts +11 -0
  88. package/src/adapters/kiro-events.ts +19 -1
  89. package/src/adapters/kiro-thinking.ts +18 -2
  90. package/src/adapters/kiro-tools.ts +65 -17
  91. package/src/adapters/kiro.ts +270 -73
  92. package/src/adapters/mimo-free.ts +17 -0
  93. package/src/adapters/openai-chat-url.ts +11 -0
  94. package/src/adapters/openai-chat.ts +1377 -344
  95. package/src/adapters/openai-responses-url.ts +16 -0
  96. package/src/adapters/openai-responses.ts +1164 -64
  97. package/src/adapters/registry.ts +175 -0
  98. package/src/adapters/responses-tool-schema.ts +67 -0
  99. package/src/adapters/run-turn-queue.ts +36 -1
  100. package/src/adapters/tool-call-id.ts +119 -0
  101. package/src/adapters/tool-catalog-nudge.ts +105 -21
  102. package/src/adapters/xai-tool-schema.ts +436 -0
  103. package/src/adapters/xai-web-search.ts +186 -0
  104. package/src/bridge.ts +512 -92
  105. package/src/chat/inbound.ts +57 -20
  106. package/src/chat/outbound.ts +97 -33
  107. package/src/claude/agents-inject.ts +40 -10
  108. package/src/claude/context-windows.ts +37 -14
  109. package/src/claude/desktop-3p.ts +243 -9
  110. package/src/claude/gateway-cache.ts +41 -4
  111. package/src/claude/inbound.ts +72 -3
  112. package/src/claude/model-info.ts +38 -15
  113. package/src/claude/outbound.ts +88 -21
  114. package/src/cli/access.ts +46 -3
  115. package/src/cli/account-api.ts +150 -19
  116. package/src/cli/account-auth.ts +33 -6
  117. package/src/cli/account-catalog-refresh.ts +14 -0
  118. package/src/cli/account-extended.ts +648 -28
  119. package/src/cli/account-main.ts +317 -0
  120. package/src/cli/account.ts +97 -19
  121. package/src/cli/agent.ts +53 -2
  122. package/src/cli/alias.ts +66 -0
  123. package/src/cli/capabilities-command.ts +94 -0
  124. package/src/cli/capabilities.ts +496 -0
  125. package/src/cli/claude-agent-startup-sync.ts +73 -0
  126. package/src/cli/claude-desktop.ts +60 -15
  127. package/src/cli/claude.ts +129 -35
  128. package/src/cli/codex-log-guard-doctor.ts +103 -0
  129. package/src/cli/codex-shim-autorestore.ts +2 -0
  130. package/src/cli/codex-shim-readiness.ts +76 -0
  131. package/src/cli/combo.ts +8 -0
  132. package/src/cli/config-command.ts +74 -10
  133. package/src/cli/dispatch.ts +821 -0
  134. package/src/cli/doctor.ts +440 -56
  135. package/src/cli/ensure-desired-integrations.ts +152 -0
  136. package/src/cli/export-command.ts +46 -20
  137. package/src/cli/help.ts +34 -274
  138. package/src/cli/index.ts +417 -528
  139. package/src/cli/init.ts +5 -17
  140. package/src/cli/inspect.ts +230 -0
  141. package/src/cli/integrations.ts +120 -2
  142. package/src/cli/lab.ts +607 -0
  143. package/src/cli/launcher-context.ts +77 -0
  144. package/src/cli/minimax.ts +497 -0
  145. package/src/cli/models-runtime.ts +130 -2
  146. package/src/cli/models.ts +118 -16
  147. package/src/cli/observe.ts +123 -11
  148. package/src/cli/opencode.ts +4 -2
  149. package/src/cli/provider-runtime.ts +34 -3
  150. package/src/cli/provider.ts +25 -3
  151. package/src/cli/ready.ts +301 -0
  152. package/src/cli/registry.ts +476 -0
  153. package/src/cli/root.ts +86 -0
  154. package/src/cli/route-policy.ts +92 -0
  155. package/src/cli/runtime-api.ts +57 -10
  156. package/src/cli/star-prompt.ts +71 -15
  157. package/src/cli/status.ts +51 -4
  158. package/src/cli/storage.ts +234 -0
  159. package/src/cli/system-command.ts +16 -0
  160. package/src/cli/system-restart-client.ts +146 -0
  161. package/src/cli/tray-proxy.ts +153 -6
  162. package/src/cli/usage-report.ts +184 -0
  163. package/src/cli/v2.ts +137 -18
  164. package/src/cli/version-skew.ts +46 -0
  165. package/src/cli.ts +1 -1
  166. package/src/clients/config-export.ts +1442 -23
  167. package/src/clients/effective-status.ts +30 -2
  168. package/src/clients/probes/cline.ts +81 -0
  169. package/src/codex/account-label.ts +35 -1
  170. package/src/codex/account-lifecycle.ts +130 -13
  171. package/src/codex/account-namespaces.ts +49 -3
  172. package/src/codex/account-pause.ts +2 -1
  173. package/src/codex/account-priority.ts +84 -0
  174. package/src/codex/account-store.ts +29 -2
  175. package/src/codex/account-usability.ts +25 -2
  176. package/src/codex/admission.ts +256 -0
  177. package/src/codex/affinity-debug.ts +162 -0
  178. package/src/codex/app-server-processes.ts +570 -107
  179. package/src/codex/app-server-restart-service.ts +232 -0
  180. package/src/codex/auth-api.ts +893 -246
  181. package/src/codex/auth-collision.ts +5 -3
  182. package/src/codex/auth-context.ts +348 -32
  183. package/src/codex/autostart-health.ts +24 -1
  184. package/src/codex/catalog/account-models.ts +67 -0
  185. package/src/codex/catalog/aggregation.ts +86 -10
  186. package/src/codex/catalog/bundled.ts +331 -33
  187. package/src/codex/catalog/effort.ts +138 -32
  188. package/src/codex/catalog/filesystem-evidence.ts +302 -0
  189. package/src/codex/catalog/kinds.ts +2 -0
  190. package/src/codex/catalog/metadata.ts +555 -45
  191. package/src/codex/catalog/native-models.ts +75 -0
  192. package/src/codex/catalog/parsing.ts +269 -30
  193. package/src/codex/catalog/provider-fetch.ts +1626 -144
  194. package/src/codex/catalog/sync.ts +1576 -187
  195. package/src/codex/catalog-admission.ts +199 -0
  196. package/src/codex/catalog-refresh-status.ts +105 -0
  197. package/src/codex/catalog-write-serialization.ts +242 -0
  198. package/src/codex/catalog.ts +6 -3
  199. package/src/codex/codex-write-lock.ts +386 -0
  200. package/src/codex/convergence-types.ts +614 -0
  201. package/src/codex/convergence.ts +676 -0
  202. package/src/codex/coordinator-doctor.ts +332 -0
  203. package/src/codex/custom-model-catalog-migration.ts +176 -0
  204. package/src/codex/desired-state.ts +230 -0
  205. package/src/codex/desktop-app-restart.ts +355 -0
  206. package/src/codex/features.ts +636 -39
  207. package/src/codex/generation.ts +202 -0
  208. package/src/codex/history-job.ts +436 -0
  209. package/src/codex/history-lock.ts +242 -0
  210. package/src/codex/history-manifest.ts +112 -0
  211. package/src/codex/history-migration-guardian.ts +30 -24
  212. package/src/codex/history-provider.ts +1016 -235
  213. package/src/codex/history-transition.ts +105 -0
  214. package/src/codex/history-worker.ts +223 -0
  215. package/src/codex/inject-coordination.ts +373 -0
  216. package/src/codex/inject.ts +1114 -152
  217. package/src/codex/injected-marker.ts +37 -3
  218. package/src/codex/integration-record.ts +266 -0
  219. package/src/codex/internal/catalog-writer.ts +203 -0
  220. package/src/codex/internal/history-writer.ts +81 -0
  221. package/src/codex/journal.ts +66 -4
  222. package/src/codex/log-guard/inspect.ts +524 -0
  223. package/src/codex/log-guard/lock.ts +150 -0
  224. package/src/codex/log-guard/maintenance.ts +403 -0
  225. package/src/codex/log-guard/path-safety.ts +88 -0
  226. package/src/codex/log-guard/policy.ts +44 -0
  227. package/src/codex/log-guard/processes.ts +205 -0
  228. package/src/codex/log-guard/protection.ts +489 -0
  229. package/src/codex/log-guard/sqlite-errors.ts +9 -0
  230. package/src/codex/main-account-cache.ts +24 -0
  231. package/src/codex/main-account.ts +29 -1
  232. package/src/codex/management-convergence.ts +167 -0
  233. package/src/codex/model-cache.ts +56 -10
  234. package/src/codex/model-entitlements.ts +360 -0
  235. package/src/codex/native-main-admission.ts +47 -0
  236. package/src/codex/native-main-auth-temp.ts +187 -0
  237. package/src/codex/native-main-claim.ts +178 -0
  238. package/src/codex/native-main-lock-file.ts +162 -0
  239. package/src/codex/native-main-owner.ts +329 -0
  240. package/src/codex/native-profile-api.ts +247 -0
  241. package/src/codex/native-profile-manager.ts +1531 -0
  242. package/src/codex/native-profile-processes.ts +121 -0
  243. package/src/codex/native-profile-recovery.ts +99 -0
  244. package/src/codex/native-profile-stage-store.ts +387 -0
  245. package/src/codex/native-profile-startup.ts +622 -0
  246. package/src/codex/native-profile-store.ts +855 -0
  247. package/src/codex/native-profile-types.ts +120 -0
  248. package/src/codex/native-residue.ts +675 -0
  249. package/src/codex/paths.ts +80 -1
  250. package/src/codex/plan-from-token.ts +140 -0
  251. package/src/codex/plan.ts +40 -0
  252. package/src/codex/plugins-doctor.ts +1 -1
  253. package/src/codex/pool-rotation.ts +74 -4
  254. package/src/codex/project-config-warnings.ts +20 -6
  255. package/src/codex/prompt-journal.ts +352 -0
  256. package/src/codex/prompt-layers.ts +1313 -0
  257. package/src/codex/prompt-lock.ts +143 -0
  258. package/src/codex/prompt-text-probe.ts +238 -0
  259. package/src/codex/quota-rejection.ts +298 -0
  260. package/src/codex/quota.ts +264 -21
  261. package/src/codex/refresh.ts +11 -2
  262. package/src/codex/reset-credit-recovery.ts +1044 -0
  263. package/src/codex/routing.ts +514 -94
  264. package/src/codex/runtime.ts +159 -38
  265. package/src/codex/shim.ts +1065 -31
  266. package/src/codex/subagent-model-fallback.ts +437 -43
  267. package/src/codex/sync.ts +191 -2
  268. package/src/codex/transition-state.ts +720 -0
  269. package/src/codex/upstream-host-health.ts +368 -0
  270. package/src/codex/user-identity.ts +557 -0
  271. package/src/codex/warmup.ts +187 -81
  272. package/src/codex/write-coordination.ts +114 -0
  273. package/src/combos/failover.ts +47 -0
  274. package/src/combos/index.ts +4 -0
  275. package/src/combos/request.ts +32 -0
  276. package/src/combos/types.ts +91 -9
  277. package/src/compatibility/index.ts +26 -0
  278. package/src/compatibility/manifest.ts +253 -0
  279. package/src/compatibility/openai-responses.ts +121 -0
  280. package/src/config/atomic-write.ts +219 -0
  281. package/src/config/paths.ts +40 -0
  282. package/src/config/process-state.ts +309 -0
  283. package/src/config/provider-name.ts +24 -0
  284. package/src/config/provider-validation.ts +177 -0
  285. package/src/config/rebase-provenance.ts +68 -0
  286. package/src/config.ts +1713 -709
  287. package/src/generated/compatibility-version.json +3324 -0
  288. package/src/generated/{jawcode-model-metadata.ts → model-metadata.ts} +19 -17
  289. package/src/grok/inject.ts +16 -5
  290. package/src/grok/inspect.ts +45 -0
  291. package/src/grok/sync.ts +2 -2
  292. package/src/images/loop.ts +157 -33
  293. package/src/images/plan.ts +24 -13
  294. package/src/integrations/config-io.ts +269 -0
  295. package/src/integrations/journal.ts +315 -0
  296. package/src/integrations/merge.ts +135 -0
  297. package/src/integrations/mutation-flight.ts +71 -0
  298. package/src/integrations/native/ownership-preflight.ts +202 -0
  299. package/src/integrations/omp-yaml-source.ts +358 -0
  300. package/src/integrations/owned-refresh.ts +74 -0
  301. package/src/integrations/ownership-policy.ts +160 -0
  302. package/src/integrations/ownership.ts +155 -0
  303. package/src/integrations/registry.ts +166 -0
  304. package/src/integrations/serialize.ts +314 -0
  305. package/src/integrations/state.ts +433 -0
  306. package/src/integrations/store.ts +103 -0
  307. package/src/integrations/writer-lock.ts +98 -0
  308. package/src/integrations/writer.ts +715 -0
  309. package/src/lab/artifacts/sanitize.ts +586 -0
  310. package/src/lab/artifacts/secure-fs.ts +475 -0
  311. package/src/lab/artifacts/store.ts +310 -0
  312. package/src/lab/automation/budgets.ts +78 -0
  313. package/src/lab/automation/config-persistence.ts +256 -0
  314. package/src/lab/automation/constants.ts +39 -0
  315. package/src/lab/automation/cooldown.ts +103 -0
  316. package/src/lab/automation/dispatch.ts +211 -0
  317. package/src/lab/automation/index.ts +13 -0
  318. package/src/lab/automation/orchestrator.ts +499 -0
  319. package/src/lab/automation/persistence.ts +512 -0
  320. package/src/lab/automation/planner.ts +371 -0
  321. package/src/lab/automation/policy.ts +136 -0
  322. package/src/lab/automation/queue.ts +191 -0
  323. package/src/lab/automation/recovery.ts +24 -0
  324. package/src/lab/automation/route-context.ts +21 -0
  325. package/src/lab/automation/run-key.ts +44 -0
  326. package/src/lab/automation/runs-query.ts +34 -0
  327. package/src/lab/automation/types.ts +160 -0
  328. package/src/lab/conformance/assertion.ts +325 -0
  329. package/src/lab/conformance/digest.ts +22 -0
  330. package/src/lab/conformance/executor.ts +741 -0
  331. package/src/lab/conformance/fixture-provider.ts +27 -0
  332. package/src/lab/conformance/fixtures/live-v1-cases.json +175 -0
  333. package/src/lab/conformance/fixtures/protocol-v1-cases.json +461 -0
  334. package/src/lab/conformance/harness-budget.ts +47 -0
  335. package/src/lab/conformance/index.ts +5 -0
  336. package/src/lab/conformance/jcs.ts +64 -0
  337. package/src/lab/conformance/json-pointer.ts +39 -0
  338. package/src/lab/conformance/manifest.ts +180 -0
  339. package/src/lab/conformance/mcp-stub.ts +179 -0
  340. package/src/lab/conformance/negative-controls.ts +164 -0
  341. package/src/lab/conformance/observation.ts +355 -0
  342. package/src/lab/conformance/runner.ts +68 -0
  343. package/src/lab/conformance/sse-normalize.ts +59 -0
  344. package/src/lab/conformance/suite-manifest.ts +78 -0
  345. package/src/lab/conformance/types.ts +214 -0
  346. package/src/lab/constants.ts +126 -0
  347. package/src/lab/digest.ts +64 -0
  348. package/src/lab/events/errors.ts +9 -0
  349. package/src/lab/events/limits.ts +117 -0
  350. package/src/lab/events/types.ts +229 -0
  351. package/src/lab/events/validate.ts +781 -0
  352. package/src/lab/fabric/constants.ts +40 -0
  353. package/src/lab/fabric/executor.ts +492 -0
  354. package/src/lab/fabric/index.ts +80 -0
  355. package/src/lab/fabric/manifest.ts +222 -0
  356. package/src/lab/fabric/observe.ts +489 -0
  357. package/src/lab/fabric/patch.ts +79 -0
  358. package/src/lab/fabric/producer-child.ts +139 -0
  359. package/src/lab/fabric/producer-isolate.ts +276 -0
  360. package/src/lab/fabric/producer-protocol.ts +61 -0
  361. package/src/lab/fabric/scratch.ts +439 -0
  362. package/src/lab/fabric/subject.ts +106 -0
  363. package/src/lab/fabric/types.ts +134 -0
  364. package/src/lab/fabric/verifier.ts +98 -0
  365. package/src/lab/index.ts +54 -0
  366. package/src/lab/ledger/artifact-refs.ts +127 -0
  367. package/src/lab/ledger/invalidation.ts +136 -0
  368. package/src/lab/ledger/purge.ts +310 -0
  369. package/src/lab/ledger/store.ts +532 -0
  370. package/src/lab/live/credential-lease.ts +53 -0
  371. package/src/lab/live/destination.ts +155 -0
  372. package/src/lab/live/executor.ts +336 -0
  373. package/src/lab/live/inert-tools.ts +56 -0
  374. package/src/lab/live/manifest.ts +85 -0
  375. package/src/lab/live/mcp-loopback.ts +57 -0
  376. package/src/lab/live/runner.ts +19 -0
  377. package/src/lab/live/sandbox.ts +61 -0
  378. package/src/lab/live/suite-manifest.ts +41 -0
  379. package/src/lab/live/transport.ts +118 -0
  380. package/src/lab/live/types.ts +197 -0
  381. package/src/lab/observe/from-conformance.ts +301 -0
  382. package/src/lab/observe/from-live.ts +117 -0
  383. package/src/lab/paths.ts +153 -0
  384. package/src/lab/projection/rebuild.ts +495 -0
  385. package/src/lab/projection/schema.ts +135 -0
  386. package/src/lab/projection/verdicts.ts +474 -0
  387. package/src/lab/projection/verification.ts +412 -0
  388. package/src/lab/public/bundle.ts +217 -0
  389. package/src/lab/public/community-authority.ts +175 -0
  390. package/src/lab/public/community-files.ts +29 -0
  391. package/src/lab/public/community.ts +479 -0
  392. package/src/lab/public/file-safety.ts +155 -0
  393. package/src/lab/public/ids.ts +26 -0
  394. package/src/lab/public/index.ts +16 -0
  395. package/src/lab/public/mutation-lock.ts +424 -0
  396. package/src/lab/public/operator.ts +353 -0
  397. package/src/lab/public/origin-purge.ts +79 -0
  398. package/src/lab/public/origin.ts +203 -0
  399. package/src/lab/public/privacy.ts +143 -0
  400. package/src/lab/public/private-file.ts +261 -0
  401. package/src/lab/public/project.ts +124 -0
  402. package/src/lab/public/purge-test-fault.ts +21 -0
  403. package/src/lab/public/purge.ts +223 -0
  404. package/src/lab/public/registry.ts +44 -0
  405. package/src/lab/public/revocation.ts +252 -0
  406. package/src/lab/public/signature.ts +243 -0
  407. package/src/lab/public/storage.ts +105 -0
  408. package/src/lab/public/strict-json.ts +206 -0
  409. package/src/lab/public/time.ts +26 -0
  410. package/src/lab/public/types.ts +172 -0
  411. package/src/lab/public/validate.ts +391 -0
  412. package/src/lab/query/catalog.ts +101 -0
  413. package/src/lab/query/connection.ts +107 -0
  414. package/src/lab/query/constants.ts +4 -0
  415. package/src/lab/query/cursor.ts +132 -0
  416. package/src/lab/query/dto-map.ts +277 -0
  417. package/src/lab/query/errors.ts +22 -0
  418. package/src/lab/query/freshness.ts +53 -0
  419. package/src/lab/query/index.ts +45 -0
  420. package/src/lab/query/latest-observation.ts +59 -0
  421. package/src/lab/query/passive-production.ts +159 -0
  422. package/src/lab/query/queries.ts +444 -0
  423. package/src/lab/query/types.ts +266 -0
  424. package/src/lab/subject/behavior-fingerprint.ts +77 -0
  425. package/src/lab/subject/installation-salt.ts +112 -0
  426. package/src/lab/subject/protocol-subject.ts +80 -0
  427. package/src/lab/subject/route-subject.ts +74 -0
  428. package/src/lib/admin-secrets.ts +24 -0
  429. package/src/lib/app-owned-memory-stores.ts +22 -0
  430. package/src/lib/bounded-body.ts +167 -11
  431. package/src/lib/bun-runtime.ts +125 -12
  432. package/src/lib/bun-stream-caps.ts +13 -9
  433. package/src/lib/codex-restart-contract.ts +120 -0
  434. package/src/lib/config-ownership.ts +26 -2
  435. package/src/lib/destination-policy.ts +65 -1
  436. package/src/lib/errors.ts +80 -5
  437. package/src/lib/fabric-task-execution-authority.ts +7 -0
  438. package/src/lib/fabric-task-host.ts +29 -0
  439. package/src/lib/lab-activation.ts +223 -0
  440. package/src/lib/lab-live-execution-authority.ts +13 -0
  441. package/src/lib/lab-live-host.ts +30 -0
  442. package/src/lib/lab-live-pinned-sender.ts +56 -0
  443. package/src/lib/lab-live-route-production.ts +130 -0
  444. package/src/lib/lab-passive-linker-registration.ts +26 -0
  445. package/src/lib/local-management-attestation.ts +51 -0
  446. package/src/lib/local-management-capability.ts +100 -0
  447. package/src/lib/local-provider-reload-contract.ts +100 -0
  448. package/src/lib/optional-shutdown-hooks.ts +57 -0
  449. package/src/lib/package-tree-integrity.ts +101 -0
  450. package/src/lib/pinned-http.ts +145 -26
  451. package/src/lib/process-control.ts +6 -2
  452. package/src/lib/provider-outbound.ts +49 -9
  453. package/src/lib/redact.ts +419 -3
  454. package/src/lib/self-launch-argv.ts +15 -0
  455. package/src/lib/server-resource-ownership.ts +71 -0
  456. package/src/lib/service-secrets.ts +15 -0
  457. package/src/lib/shadow-call.ts +35 -4
  458. package/src/lib/sse-decoder.ts +41 -0
  459. package/src/lib/state-store-registrations.ts +10 -2
  460. package/src/lib/system-restart-contract.ts +73 -0
  461. package/src/lib/token-estimate.ts +19 -2
  462. package/src/lib/tool-argument-integers.ts +253 -0
  463. package/src/lib/translator-budget.ts +44 -0
  464. package/src/lib/upstream-http-version.ts +57 -0
  465. package/src/lib/upstream-reachability.ts +95 -0
  466. package/src/lib/upstream-retry.ts +156 -3
  467. package/src/lib/windows-atomic-replace.ts +155 -0
  468. package/src/lib/windows-elevation.ts +70 -2
  469. package/src/lib/windows-secret-acl.ts +409 -69
  470. package/src/lib/windows-service-wrappers.ts +72 -0
  471. package/src/lib/windows-text.ts +106 -0
  472. package/src/lib/windows-user-principal.ts +341 -0
  473. package/src/lib/winsw.ts +33 -5
  474. package/src/oauth/account-import/google-antigravity-adapter.ts +74 -0
  475. package/src/oauth/account-import/index.ts +15 -0
  476. package/src/oauth/account-import/parser.ts +83 -0
  477. package/src/oauth/account-import/registry.ts +18 -0
  478. package/src/oauth/account-import/service.ts +75 -0
  479. package/src/oauth/account-import/types.ts +91 -0
  480. package/src/oauth/anthropic.ts +12 -1
  481. package/src/oauth/callback-server.ts +30 -4
  482. package/src/oauth/chatgpt.ts +12 -1
  483. package/src/oauth/cline.ts +203 -0
  484. package/src/oauth/command-code.ts +239 -0
  485. package/src/oauth/cursor.ts +46 -5
  486. package/src/oauth/generic-account-failover.ts +231 -0
  487. package/src/oauth/google-antigravity.ts +35 -3
  488. package/src/oauth/health.ts +20 -12
  489. package/src/oauth/index.ts +462 -71
  490. package/src/oauth/key-providers.ts +25 -0
  491. package/src/oauth/kimi.ts +25 -3
  492. package/src/oauth/kiro.ts +95 -6
  493. package/src/oauth/local-token-detect.ts +13 -2
  494. package/src/oauth/log.ts +3 -1
  495. package/src/oauth/login-cli.ts +88 -28
  496. package/src/oauth/nous.ts +798 -0
  497. package/src/oauth/open-browser-choice.ts +26 -0
  498. package/src/oauth/store.ts +133 -26
  499. package/src/oauth/token-guardian.ts +9 -3
  500. package/src/oauth/types.ts +15 -0
  501. package/src/pi/models.ts +2 -2
  502. package/src/providers/alibaba-region-migration.ts +1 -1
  503. package/src/providers/antigravity-models.ts +521 -31
  504. package/src/providers/auto-compact-budget.ts +65 -0
  505. package/src/providers/base-url-choices.ts +10 -0
  506. package/src/providers/codex-capacity.ts +292 -0
  507. package/src/providers/command-code-efforts.ts +176 -0
  508. package/src/providers/context-cap.ts +26 -8
  509. package/src/providers/cursor-pool.ts +72 -0
  510. package/src/providers/default-aliases.ts +65 -0
  511. package/src/providers/derive.ts +281 -6
  512. package/src/providers/fastwire.ts +507 -0
  513. package/src/providers/free-directory.ts +10 -7
  514. package/src/providers/google-vertex-location.ts +14 -0
  515. package/src/providers/key-failover.ts +71 -3
  516. package/src/providers/label.ts +35 -2
  517. package/src/providers/model-discovery-limits.ts +16 -0
  518. package/src/providers/model-discovery.ts +115 -22
  519. package/src/providers/model-presets.ts +119 -0
  520. package/src/providers/model-rename-migration.ts +255 -0
  521. package/src/providers/model-rename-startup.ts +28 -0
  522. package/src/providers/new-model-policy.ts +146 -0
  523. package/src/providers/openai-sidecar.ts +72 -4
  524. package/src/providers/openai-tier-startup.ts +31 -2
  525. package/src/providers/openai-tiers.ts +119 -4
  526. package/src/providers/openai-virtual-models.ts +1 -0
  527. package/src/providers/opencode-zen-rate-limit.ts +102 -0
  528. package/src/providers/provider-id-rewrite.ts +30 -0
  529. package/src/providers/quota.ts +1379 -40
  530. package/src/providers/registry.ts +1545 -112
  531. package/src/providers/request-pacing.ts +310 -0
  532. package/src/providers/service-tier.ts +277 -0
  533. package/src/providers/slug-codec.ts +94 -6
  534. package/src/providers/static-model-discovery.ts +86 -0
  535. package/src/providers/xai-responses-opt-in.ts +15 -0
  536. package/src/providers/xai-transport.ts +32 -4
  537. package/src/reasoning-effort.ts +68 -3
  538. package/src/responses/apply-patch-envelope.ts +63 -0
  539. package/src/responses/code-mode-helper-compat.ts +50 -0
  540. package/src/responses/compaction.ts +26 -1
  541. package/src/responses/custom-tool-compat.ts +384 -0
  542. package/src/responses/hosted-tool-policy.ts +9 -0
  543. package/src/responses/namespace-tool-compat.ts +435 -0
  544. package/src/responses/parser.ts +225 -38
  545. package/src/responses/provider-continuation.ts +98 -0
  546. package/src/responses/provider-opaque-metadata.ts +73 -0
  547. package/src/responses/reasoning-envelope.ts +9 -1
  548. package/src/responses/reasoning-replay-cache.ts +504 -0
  549. package/src/responses/schema.ts +12 -2
  550. package/src/responses/spill-store.ts +75 -10
  551. package/src/responses/state.ts +659 -31
  552. package/src/responses/thought-signature-replay.ts +364 -0
  553. package/src/responses/tool-search-compat.ts +301 -0
  554. package/src/responses/truncated-stop-reason.ts +60 -0
  555. package/src/responses/turn-termination.ts +107 -0
  556. package/src/router.ts +405 -31
  557. package/src/routing/analytics.ts +379 -0
  558. package/src/routing/capability.ts +244 -0
  559. package/src/routing/compatibility/assemble.ts +73 -0
  560. package/src/routing/compatibility/behavior.ts +278 -0
  561. package/src/routing/compatibility/catalog.ts +99 -0
  562. package/src/routing/compatibility/endpoint.ts +52 -0
  563. package/src/routing/compatibility/lab-evidence-provider.ts +130 -0
  564. package/src/routing/compatibility/policy.ts +181 -0
  565. package/src/routing/compatibility/provider-slot.ts +56 -0
  566. package/src/routing/compatibility/reader.ts +110 -0
  567. package/src/routing/compatibility/subject.ts +191 -0
  568. package/src/routing/compatibility/types.ts +64 -0
  569. package/src/routing/compatibility/version.ts +104 -0
  570. package/src/routing/cost.ts +77 -0
  571. package/src/routing/evaluator.ts +495 -0
  572. package/src/routing/health.ts +412 -0
  573. package/src/routing/history/cursor.ts +43 -0
  574. package/src/routing/history/indexer.ts +605 -0
  575. package/src/routing/history/schema.ts +72 -0
  576. package/src/routing/profile-namespace.ts +15 -0
  577. package/src/routing/profile.ts +547 -0
  578. package/src/routing/quota.ts +155 -0
  579. package/src/routing/request-evidence.ts +45 -0
  580. package/src/routing/trace.ts +776 -0
  581. package/src/server/adapter-resolve.ts +2 -29
  582. package/src/server/auth-cors.ts +289 -48
  583. package/src/server/background-lifecycle.ts +182 -0
  584. package/src/server/chat-completions.ts +154 -72
  585. package/src/server/chat-native-sse.ts +331 -0
  586. package/src/server/chat-native.ts +445 -0
  587. package/src/server/claude-messages.ts +160 -44
  588. package/src/server/direct-local-http.ts +347 -0
  589. package/src/server/effort-policy.ts +18 -0
  590. package/src/server/github-copilot-responses-repair.ts +338 -0
  591. package/src/server/gui-static.ts +39 -10
  592. package/src/server/images.ts +94 -12
  593. package/src/server/index.ts +1021 -190
  594. package/src/server/lifecycle.ts +336 -14
  595. package/src/server/live.ts +136 -17
  596. package/src/server/local-management-read-client.ts +90 -0
  597. package/src/server/local-provider-reload-client.ts +137 -0
  598. package/src/server/management/agent-settings-routes.ts +433 -116
  599. package/src/server/management/api-key-usage.ts +31 -5
  600. package/src/server/management/body.ts +6 -0
  601. package/src/server/management/codex-prompt-routes.ts +570 -0
  602. package/src/server/management/combo-routes.ts +64 -25
  603. package/src/server/management/config-routes.ts +511 -51
  604. package/src/server/management/context.ts +89 -2
  605. package/src/server/management/integration-routes.ts +498 -0
  606. package/src/server/management/lab-automation-routes.ts +206 -0
  607. package/src/server/management/lab-routes.ts +563 -0
  608. package/src/server/management/logs-usage-routes.ts +127 -52
  609. package/src/server/management/model-routes.ts +461 -133
  610. package/src/server/management/model-rows.ts +167 -0
  611. package/src/server/management/native-integration-routes.ts +770 -0
  612. package/src/server/management/oauth-account-routes.ts +90 -8
  613. package/src/server/management/provider-capability-config.ts +48 -0
  614. package/src/server/management/provider-routes.ts +836 -159
  615. package/src/server/management/request-history-routes.ts +191 -0
  616. package/src/server/management/route-registry.ts +332 -0
  617. package/src/server/management/routing-analytics-routes.ts +74 -0
  618. package/src/server/management/routing-profile-routes.ts +380 -0
  619. package/src/server/management/shared.ts +27 -11
  620. package/src/server/management/sidebar-routes.ts +47 -31
  621. package/src/server/management/storage-log-guard-routes.ts +186 -0
  622. package/src/server/management/sync-response.ts +69 -0
  623. package/src/server/management/system-restart.ts +289 -33
  624. package/src/server/management/system-routes.ts +77 -0
  625. package/src/server/management/usage-summary-cache.ts +9 -1
  626. package/src/server/management/vision-sidecar-options.ts +167 -0
  627. package/src/server/management/web-search-sidecar-options.ts +120 -0
  628. package/src/server/management-api.ts +126 -23
  629. package/src/server/management-auth.ts +220 -5
  630. package/src/server/passive-route-linker.ts +66 -0
  631. package/src/server/port-reclaim.ts +1 -1
  632. package/src/server/ports.ts +41 -1
  633. package/src/server/proxy-liveness.ts +161 -10
  634. package/src/server/readiness.ts +99 -0
  635. package/src/server/relay-eager.ts +221 -54
  636. package/src/server/relay.ts +479 -87
  637. package/src/server/request-decompress.ts +113 -6
  638. package/src/server/request-log-conversation.ts +51 -0
  639. package/src/server/request-log.ts +298 -31
  640. package/src/server/responses/agent-task-recovery-cache.ts +143 -0
  641. package/src/server/responses/agent-task-recovery.ts +465 -0
  642. package/src/server/responses/codex-auth-error.ts +55 -0
  643. package/src/server/responses/collaboration.ts +239 -36
  644. package/src/server/responses/combo-stream-preflight.ts +178 -0
  645. package/src/server/responses/compact.ts +444 -72
  646. package/src/server/responses/core.ts +3579 -436
  647. package/src/server/responses/empty-completion-guard.ts +333 -0
  648. package/src/server/responses/encrypted-payload.ts +62 -39
  649. package/src/server/responses/fetch-helpers.ts +89 -99
  650. package/src/server/responses/input-admission.ts +187 -0
  651. package/src/server/responses/pacing-overload.ts +13 -0
  652. package/src/server/responses/passthrough-error.ts +33 -9
  653. package/src/server/responses/policy-fallback.ts +178 -0
  654. package/src/server/responses/responses-field-backfill.ts +343 -0
  655. package/src/server/responses/terminal-guard.ts +26 -5
  656. package/src/server/responses/upstream-error.ts +5 -0
  657. package/src/server/responses/ws-upstream.ts +381 -0
  658. package/src/server/responses-custom-tool-repair.ts +363 -0
  659. package/src/server/responses-item-id-repair.ts +54 -6
  660. package/src/server/responses-json-events.ts +90 -0
  661. package/src/server/responses-model-rewrite.ts +29 -0
  662. package/src/server/responses-reasoning-summary-rewrite.ts +178 -0
  663. package/src/server/responses-snapshot-repair.ts +621 -0
  664. package/src/server/responses-terminal-repair.ts +363 -0
  665. package/src/server/responses-tool-search-repair.ts +267 -0
  666. package/src/server/responses-undeclared-tool-guard.ts +376 -0
  667. package/src/server/responses.ts +18 -2
  668. package/src/server/search.ts +78 -13
  669. package/src/server/sse-frame-buffer.ts +319 -0
  670. package/src/server/sse-payload-rewrite.ts +110 -22
  671. package/src/server/startup-action-control.ts +8 -1
  672. package/src/server/startup-health-cache.ts +19 -1
  673. package/src/server/system-env.ts +80 -9
  674. package/src/server/ws-bridge.ts +53 -40
  675. package/src/service-manager-probe.ts +892 -0
  676. package/src/service.ts +1153 -94
  677. package/src/sidecar/auth.ts +92 -0
  678. package/src/sidecar/candidates.ts +83 -0
  679. package/src/storage/cleanup.ts +2 -2
  680. package/src/storage/policy-job.ts +14 -4
  681. package/src/storage/policy.ts +88 -23
  682. package/src/storage/scanner.ts +1 -1
  683. package/src/storage/worker-lifecycle.ts +14 -14
  684. package/src/tray/windows-tray.ps1 +83 -9
  685. package/src/tray/windows.ts +43 -16
  686. package/src/types/accounts.ts +37 -0
  687. package/src/types/config.ts +911 -0
  688. package/src/types/provider.ts +614 -0
  689. package/src/types/request.ts +397 -0
  690. package/src/types/tools.ts +236 -0
  691. package/src/types/wire.ts +80 -0
  692. package/src/types.ts +107 -1237
  693. package/src/update/index.ts +37 -23
  694. package/src/update/job.ts +445 -68
  695. package/src/update/notify.ts +12 -6
  696. package/src/update/npm-cache-preflight.d.mts +47 -0
  697. package/src/update/npm-cache-preflight.mjs +201 -0
  698. package/src/update/transactional-install.d.mts +22 -0
  699. package/src/update/transactional-install.mjs +266 -0
  700. package/src/usage/cost.ts +0 -0
  701. package/src/usage/expected-prices.ts +287 -16
  702. package/src/usage/log.ts +666 -50
  703. package/src/usage/summary.ts +388 -17
  704. package/src/usage/user-cost-overlay-reconciler.ts +313 -0
  705. package/src/usage/user-cost-overlays.ts +314 -0
  706. package/src/vision/anthropic-describe.ts +10 -6
  707. package/src/vision/backends.ts +97 -0
  708. package/src/vision/describe.ts +27 -16
  709. package/src/vision/eligibility.ts +250 -0
  710. package/src/vision/index.ts +238 -24
  711. package/src/vision/reasoning.ts +55 -0
  712. package/src/vision/routed-describe.ts +175 -0
  713. package/src/vision/timeout-bounds.ts +9 -0
  714. package/src/web-search/anthropic-executor.ts +13 -7
  715. package/src/web-search/backends.ts +108 -0
  716. package/src/web-search/exa-executor.ts +119 -0
  717. package/src/web-search/executor.ts +21 -6
  718. package/src/web-search/gemini-executor.ts +141 -0
  719. package/src/web-search/index.ts +150 -15
  720. package/src/web-search/loop.ts +284 -54
  721. package/src/web-search/parse.ts +125 -30
  722. package/src/web-search/sources.ts +60 -0
  723. package/src/web-search/xai-executor.ts +219 -0
  724. package/gui/dist/assets/index-DTpMHS4F.js +0 -67
  725. package/gui/dist/assets/index-ZNVDE3C7.css +0 -1
  726. package/gui/dist/provider-icons/antigravity.svg +0 -1
  727. package/gui/dist/provider-icons/claude.svg +0 -1
  728. package/gui/dist/provider-icons/copilot.svg +0 -1
  729. package/gui/dist/provider-icons/cursor.svg +0 -2
  730. package/gui/dist/provider-icons/gemini.svg +0 -1
  731. package/gui/dist/provider-icons/grok-color.svg +0 -1
  732. package/gui/dist/provider-icons/kiro.svg +0 -14
  733. 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,58 @@ 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>;
253
+ /** Registry defaults for per-model Codex Responses verbosity support. */
254
+ modelSupportsVerbosity?: Record<string, boolean>;
255
+ /**
256
+ * Registry default applied to EVERY model of this provider, including ids that arrive from
257
+ * live discovery after this table was written.
258
+ *
259
+ * `modelSupportsVerbosity` only covers the ids enumerated here, so a newly discovered model
260
+ * fell through and re-advertised a control the upstream accepts and ignores. Where the opt-out
261
+ * is a property of the provider's API rather than of one model, declare it here; a per-model
262
+ * entry still wins over it.
263
+ */
264
+ supportsVerbosity?: boolean;
164
265
  modelDiscovery?: ProviderModelDiscoverySpec;
165
266
  contextWindow?: number;
166
267
  modelContextWindows?: Record<string, number>;
@@ -172,6 +273,13 @@ export interface ProviderRegistryEntry {
172
273
  modelDefaultReasoningEfforts?: Record<string, string>;
173
274
  reasoningEffortMap?: Record<string, string>;
174
275
  modelReasoningEffortMap?: Record<string, Record<string, string>>;
276
+ /**
277
+ * Registry-authoritative models that send OpenAI's direct `reasoning_effort` field.
278
+ * Runtime enrichment uses this to repair stale preset metadata that still classifies a model
279
+ * as a thinking-budget/toggle model. This is registry-only and is never persisted as user config.
280
+ */
281
+ directReasoningEffortModels?: string[];
282
+ reasoningWireFormat?: OcxProviderConfig["reasoningWireFormat"];
175
283
  noVisionModels?: string[];
176
284
  noReasoningModels?: string[];
177
285
  noTemperatureModels?: string[];
@@ -181,8 +289,17 @@ export interface ProviderRegistryEntry {
181
289
  parallelToolCalls?: boolean;
182
290
  /** Opt this provider into forwarding prompt_cache_key (OpenAI-specific; strict backends reject it). */
183
291
  promptCacheKey?: boolean;
292
+ /**
293
+ * Opt-in: forward `service_tier` on the `/chat/completions` wire. Same hazard as
294
+ * `promptCacheKey` — an OpenAI-specific extension that strict gateways reject. Distinct from
295
+ * `supportsServiceTier`, which governs the Responses wire.
296
+ */
297
+ chatServiceTier?: boolean;
298
+ /** OpenAI Chat EOF policy for gateways that omit terminal frames after complete tool calls. */
299
+ openaiChatEofTolerance?: boolean;
184
300
  autoToolChoiceOnlyModels?: string[];
185
301
  preserveReasoningContentModels?: string[];
302
+ requiresReasoningPlaceholderModels?: string[];
186
303
  reasoningSplitModels?: string[];
187
304
  thinkingToggleModels?: string[];
188
305
  thinkingBudgetModels?: string[];
@@ -203,9 +320,9 @@ export type ProviderConfigSeed = Pick<
203
320
  "adapter" | "baseUrl" | "apiKeyTransport" | "responsesPath" | "authMode" | "keyOptional" | "freeTier" | "modelSuffixBracketStrip" | "defaultModel" | "models"
204
321
  | "liveModels" | "contextWindow" | "modelContextWindows" | "modelInputModalities"
205
322
  | "modelMaxInputTokens" | "defaultMaxOutputTokens" | "modelMaxOutputTokens"
206
- | "reasoningEfforts" | "modelReasoningEfforts" | "modelDefaultReasoningEfforts" | "reasoningEffortMap" | "modelReasoningEffortMap"
323
+ | "reasoningEfforts" | "modelReasoningEfforts" | "modelDefaultReasoningEfforts" | "reasoningEffortMap" | "modelReasoningEffortMap" | "reasoningWireFormat"
207
324
  | "noVisionModels" | "noReasoningModels" | "noTemperatureModels" | "noTopPModels" | "noPenaltyModels"
208
- | "autoToolChoiceOnlyModels" | "preserveReasoningContentModels" | "reasoningSplitModels" | "thinkingToggleModels" | "thinkingBudgetModels" | "escapeBuiltinToolNames"
325
+ | "autoToolChoiceOnlyModels" | "preserveReasoningContentModels" | "requiresReasoningPlaceholderModels" | "reasoningSplitModels" | "thinkingToggleModels" | "thinkingBudgetModels" | "escapeBuiltinToolNames" | "openaiChatEofTolerance"
209
326
  | "googleMode" | "project" | "location" | "headers"
210
327
  >;
211
328
 
@@ -214,10 +331,54 @@ export type ProviderConfigSeed = Pick<
214
331
  // 260710 context refresh: Tier-2 evidence in
215
332
  // devlog/_plan/260710_provider_hardening/001_research_frontier.md.
216
333
  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 };
334
+ 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
335
 
336
+ // 260814 GLM-5.3 is registered pre-emptively alongside 5.2 everywhere 5.2 appears. Z.AI's
337
+ // devpack "How to Switch Models" page (docs.z.ai/devpack/latest-model) lists glm-5.3 and
338
+ // glm-5.3[1m] as Coding Plan ids on the unchanged endpoints; the capability and pricing
339
+ // tables were not published yet, so every 5.3 row mirrors its 5.2 sibling until they settle.
340
+ // The non-Z.AI providers below are speculative on purpose: they carry 5.2 today and are
341
+ // expected to pick 5.3 up on their usual lag. Providers whose live /v1/models discovery is
342
+ // enabled self-correct on the next successful fetch; static ones need a follow-up refresh.
343
+ // Every 5.3 family member, so the effort ladder, the default effort and the output
344
+ // cap are derived in ONE place. `glm-5.3-flash` was seeded into the model list and
345
+ // the context map by hand and left out of this constant, which meant it advertised
346
+ // a 1M context with a null effort ladder, no default effort and no output cap while
347
+ // its siblings carried three tiers, a `max` default and 131072 tokens. A member
348
+ // added to the list but not to the family is a model whose metadata silently
349
+ // disappears.
350
+ const ZAI_GLM_53_MODELS = ["glm-5.3", "glm-5.3[1m]", "glm-5.3-flash"];
219
351
  const ZAI_GLM_52_MODELS = ["glm-5.2", "glm-5.2[1m]"];
352
+ const ZAI_GLM_5X_MODELS = [...ZAI_GLM_53_MODELS, ...ZAI_GLM_52_MODELS];
353
+ /**
354
+ * The 5.x rows whose images the PROXY has to describe, which is NOT the same set as
355
+ * the 5.x rows themselves.
356
+ *
357
+ * `glm-5.3-flash` is a native VLM (docs.z.ai/guides/vlm/glm-5.3-flash), so listing it
358
+ * in `noVisionModels` sent an image through the vision sidecar and handed the model a
359
+ * text description of a picture it could have read itself - no error, worse answer,
360
+ * extra call. The correction commit fixed the Alibaba entries and left the eight
361
+ * providers that reach this constant behind.
362
+ *
363
+ * Kept separate from ZAI_GLM_5X_MODELS rather than filtered at each use site: that
364
+ * constant also drives `modelSupportsReasoningSummaries` and
365
+ * `preserveReasoningContentModels`, where flash DOES belong.
366
+ */
367
+ const ZAI_GLM_5X_SIDECAR_VISION_MODELS = ZAI_GLM_5X_MODELS.filter(id => id !== "glm-5.3-flash");
220
368
  const ZAI_GLM_52_REASONING_EFFORTS = ["low", "medium", "high", "xhigh", "max"];
369
+ /**
370
+ * GLM-5.3 does NOT share 5.2's five-tier ladder. docs.z.ai/devpack/latest-model folds every
371
+ * incoming effort into three effective tiers — low/minimal/light -> low, medium/high -> high,
372
+ * xhigh/max/ultra -> max — with max as both the default and the unknown-value fallback.
373
+ * Advertising five levels would publish two picker rows that are indistinguishable on the wire,
374
+ * so only the effective tiers are exposed (same treatment Cursor and Baseten already give GLM).
375
+ */
376
+ const ZAI_GLM_53_REASONING_EFFORTS = ["low", "high", "max"];
377
+ /** Per-model ladders for the Coding Plan rows: 5.3 gets its three effective tiers, 5.2 keeps five. */
378
+ const ZAI_GLM_5X_REASONING_EFFORTS: Record<string, string[]> = {
379
+ ...Object.fromEntries(ZAI_GLM_53_MODELS.map(id => [id, ZAI_GLM_53_REASONING_EFFORTS])),
380
+ ...Object.fromEntries(ZAI_GLM_52_MODELS.map(id => [id, ZAI_GLM_52_REASONING_EFFORTS])),
381
+ };
221
382
  // 260710 MiniMax models and context windows: Tier-2 evidence in
222
383
  // devlog/_plan/260710_provider_hardening/002_research_cn.md.
223
384
  const MINIMAX_MODELS = [
@@ -243,12 +404,6 @@ const MINIMAX_M3_REASONING_EFFORT_MAP: Record<string, string> = {
243
404
  const OPENAI_GPT56_MODELS = ["gpt-5.6", "gpt-5.6-sol", "gpt-5.6-terra", "gpt-5.6-luna"];
244
405
  const OPENAI_GPT56_PRO_MODELS = ["gpt-5.6-sol-pro", "gpt-5.6-terra-pro", "gpt-5.6-luna-pro"];
245
406
  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
407
  const OPENAI_API_GPT56_CONTEXT_WINDOWS: Record<string, number> = {
253
408
  ...Object.fromEntries([...OPENAI_GPT56_MODELS, ...OPENAI_GPT56_PRO_MODELS].map(id => [id, OPENAI_API_GPT56_CONTEXT_WINDOW])),
254
409
  "gpt-5.5": OPENAI_API_GPT56_CONTEXT_WINDOW,
@@ -263,7 +418,47 @@ const OPENAI_API_GPT56_VIRTUAL_MODELS: Record<string, { wireModelId: string; rea
263
418
  "gpt-5.6-luna-pro": { wireModelId: "gpt-5.6-luna", reasoningMode: "pro" },
264
419
  };
265
420
  const OPENAI_API_GPT56_REASONING_EFFORTS = ["low", "medium", "high", "xhigh", "max"];
421
+ /**
422
+ * Daybreak program aliases. These `-latest` ids are the stable contract: OpenAI repoints
423
+ * them at newer snapshots over time (red -> gpt-5.6-cyber, blue -> gpt-5.6-sol as of
424
+ * 2026-08-11), so registering the ALIAS inherits future model swaps while a pinned
425
+ * snapshot id would silently go stale. Snapshot ids are deliberately absent here.
426
+ * Responses-only per both published endpoint tables (`v1/chat/completions` is marked
427
+ * Not supported) — never add these to a chat-completions provider. Access needs separate
428
+ * Daybreak approval and provisioning, so neither is ever a default.
429
+ * Verified 2026-08-11: developers.openai.com/api/docs/models/daybreak-red-latest.md
430
+ * and .../daybreak-blue-latest.md
431
+ */
432
+ const OPENAI_DAYBREAK_MODELS = ["daybreak-red-latest", "daybreak-blue-latest"];
433
+ const OPENAI_DAYBREAK_CONTEXT_WINDOWS: Record<string, number> = {
434
+ "daybreak-red-latest": 400_000,
435
+ "daybreak-blue-latest": 1_050_000,
436
+ };
437
+ const OPENAI_DAYBREAK_MAX_INPUT_TOKENS: Record<string, number> = {
438
+ "daybreak-red-latest": 272_000,
439
+ "daybreak-blue-latest": 922_000,
440
+ };
441
+ /**
442
+ * Neither Daybreak page publishes a reasoning-effort ladder. An explicit empty array means
443
+ * "expose no effort control"; OMITTING the key would instead fall back to the full routed
444
+ * ladder (`configuredReasoningEfforts` returns undefined -> `applyReasoningLevels` uses
445
+ * ROUTED_REASONING_LEVELS), which would advertise efforts the models never documented.
446
+ * `noReasoningModels` is wrong here: both pages document reasoning-token support, so these
447
+ * are reasoning models with no *selectable* ladder.
448
+ */
449
+ const OPENAI_DAYBREAK_REASONING_EFFORTS: Record<string, string[]> = Object.fromEntries(
450
+ OPENAI_DAYBREAK_MODELS.map(id => [id, [] as string[]]),
451
+ );
266
452
  const OPENROUTER_GPT56_MODELS = OPENAI_GPT56_MODELS.map(id => `openai/${id}`);
453
+ const XAI_MODELS = [
454
+ "grok-4.6",
455
+ "grok-4.5",
456
+ "grok-4.3",
457
+ "grok-4.20-0309-reasoning",
458
+ "grok-4.20-0309-non-reasoning",
459
+ "grok-build-0.1",
460
+ "grok-composer-2.5-fast",
461
+ ];
267
462
  // OpenRouter's live /endpoints routes report 1,050,000; keep this separate from the
268
463
  // unverified OpenAI API-key seed. Evidence: devlog/_plan/260710_provider_hardening/003_research_aggregators.md.
269
464
  const OPENROUTER_GPT56_CONTEXT_WINDOW = 1_050_000;
@@ -290,7 +485,7 @@ const THINKING_TOGGLE_MAP: Record<string, string> = {
290
485
  max: "enabled",
291
486
  };
292
487
  const OPENCODE_GO_THINKING_TOGGLE_MODELS = [
293
- "mimo-v2.5", "mimo-v2.5-pro", "mimo-v2-omni", "mimo-v2-pro", "glm-5", "glm-5.1",
488
+ "mimo-v2.5", "mimo-v2.5-pro", "glm-5", "glm-5.1",
294
489
  ];
295
490
  /**
296
491
  * Zhipu's domestic BigModel platform. Text families first, then the vision member: modalities are
@@ -298,47 +493,151 @@ const OPENCODE_GO_THINKING_TOGGLE_MODELS = [
298
493
  * images through the proxy's vision sidecar (src/codex/catalog/provider-fetch.ts), a claim nobody
299
494
  * has verified for BigModel-hosted GLM.
300
495
  */
301
- const ZHIPU_BIGMODEL_TEXT_MODELS = ["glm-4.6", "glm-4.7", "glm-4.7-flash", "glm-5", "glm-5.1"];
496
+ // `glm-5.3-flash` is deliberately absent: it is a native VLM
497
+ // (docs.z.ai/guides/vlm/glm-5.3-flash), unlike glm-5.3 itself.
498
+ 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
499
  const ZHIPU_BIGMODEL_MODELS = [...ZHIPU_BIGMODEL_TEXT_MODELS, "glm-4.6v"];
303
500
  const ZHIPU_BIGMODEL_INPUT_MODALITIES: Record<string, string[]> = {
304
501
  ...Object.fromEntries(ZHIPU_BIGMODEL_TEXT_MODELS.map(id => [id, ["text"]])),
305
502
  "glm-4.6v": ["text", "image"],
306
503
  };
307
- const ZHIPU_BIGMODEL_THINKING_TOGGLE_MODELS = ["glm-4.6", "glm-4.7", "glm-5", "glm-5.1"];
504
+ const ZHIPU_BIGMODEL_THINKING_TOGGLE_MODELS = ["glm-4.6", "glm-4.7", "glm-5", "glm-5.1", "glm-5.2", "glm-5.3", "glm-5.3-flash"];
308
505
  const THINKING_BUDGET_EFFORTS = ["low", "medium", "high", "xhigh", "max"];
506
+ // Qwen3.8-Max is the first Qwen3.x model with official direct `reasoning_effort` support.
507
+ // Evidence: https://qwen.ai/blog?id=qwen3.8
508
+ const QWEN38_REASONING_EFFORTS = ["low", "medium", "xhigh"];
309
509
  const THINKING_BUDGET_MODELS = [
310
510
  "qwen3.5-397b", "qwen3.6-35b",
311
511
  "qwen3.5-plus", "qwen3.6-plus", "qwen3.7-max", "qwen3.7-plus",
312
512
  ];
313
513
  const OPENCODE_GO_THINKING_BUDGET_MODELS = ["qwen3.5-plus", "qwen3.6-plus", "qwen3.7-max", "qwen3.7-plus"];
314
514
  const DEEPSEEK_THINKING_MODELS = ["deepseek-v4-pro", "deepseek-v4-flash"];
515
+ /*
516
+ * DeepSeek's experimental vision preview (released 2026-08-21, api-docs.deepseek.com):
517
+ * text+image input on the V4 Flash base. DeepSeek positions it as a preview id;
518
+ * the expectation is that vision merges into `deepseek-v4-flash` proper later,
519
+ * at which point this id retires the same way deepseek-chat/reasoner did.
520
+ */
521
+ const DEEPSEEK_VISION_PREVIEW_MODEL = "deepseek-v4-flash-vision-exp";
522
+ /**
523
+ * CommandCode routes verified to accept image input end-to-end (#2406).
524
+ *
525
+ * Verified-negative and therefore deliberately ABSENT: deepseek/deepseek-v4-flash,
526
+ * deepseek/deepseek-v4-pro, zai-org/GLM-5.2, zai-org/GLM-5.3, xai/grok-4.6. Those
527
+ * routes accept the request and drop the image, which is worse than declining it — the
528
+ * model answers about an image it never saw. Do not add an id here on family resemblance;
529
+ * capability intersection trusts this map.
530
+ */
531
+ const COMMAND_CODE_IMAGE_MODELS = [
532
+ `deepseek/${DEEPSEEK_VISION_PREVIEW_MODEL}`,
533
+ "gpt-5.6-luna",
534
+ "gpt-5.6-sol",
535
+ "MiniMaxAI/MiniMax-M3",
536
+ "moonshotai/Kimi-K3",
537
+ "meta/muse-spark-1.2",
538
+ "meta/muse-spark-1.2-contributor",
539
+ ] as const;
540
+ const COMMAND_CODE_MODEL_INPUT_MODALITIES: Record<string, ["text", "image"]> =
541
+ Object.fromEntries(COMMAND_CODE_IMAGE_MODELS.map(id => [id, ["text", "image"]]));
315
542
  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",
543
+ /*
544
+ * Zen free models that reject `image_url` upstream (#1043, and the reproducible
545
+ * half of #1024).
546
+ *
547
+ * Zen publishes NO modality metadata — its `/v1/models` returns only id, object,
548
+ * created, owned_by — so this list is measured, not derived. Each id was probed
549
+ * once against https://opencode.ai/zen/v1 on 2026-08-05 with a text control first
550
+ * and then a 1x1 PNG; the six below failed the image request, four of them with
551
+ * `[404] No endpoints found that support image input` and `big-pickle` with the
552
+ * exact deserialize error quoted in #1043.
553
+ *
554
+ * `mimo-v2.5-free` and `longcat-2.0-free` ACCEPT images and are deliberately
555
+ * absent. Adding them would silently replace a working image with a caption,
556
+ * which is worse than the loud 400 this list exists to prevent — see the negative
557
+ * assertion in tests/provider-registry-parity.test.ts.
558
+ *
559
+ * Zen's roster is discovered live while this list is static, so it is a dated
560
+ * exception list, not a capability model. Re-probe before extending it.
561
+ * Evidence: devlog/_fin/260805_bug_fix_stack/002_zen_modality_probe.md
562
+ */
563
+ const OPENCODE_ZEN_TEXT_ONLY_MODELS = [
564
+ "big-pickle",
565
+ "nemotron-3-ultra-free",
566
+ "ling-3.0-flash-free",
567
+ "north-mini-code-free",
568
+ "laguna-s-2.1-free",
569
+ "deepseek-v4-flash-free",
570
+ ];
571
+ /*
572
+ * DeepSeek's Codex ladder is low/high/max. With the V4 Pro GA release
573
+ * (DeepSeek-V4-Pro-0813) the official thinking-mode table is IDENTICAL for both
574
+ * V4 models (api-docs.deepseek.com/guides/thinking_mode, verified 2026-08-13):
575
+ *
576
+ * requested | v4-flash | v4-pro
577
+ * low | low | low
578
+ * medium | high | high
579
+ * high | high | high
580
+ * xhigh | high | high
581
+ * max | max | max
582
+ *
583
+ * Before GA, Pro silently upgraded low->high and mapped xhigh->max (#1057-era
584
+ * table); the page's footnote about an early-August Pro mapping update landed
585
+ * with this GA, so Pro now advertises the same three real tiers as Flash.
586
+ *
587
+ * Two standing notes (#1057):
588
+ *
589
+ * - `xhigh` is a COMPATIBILITY ALIAS, not a native tier. It stays in the wire maps
590
+ * so existing requests and saved configs keep working, but it is not advertised.
591
+ * - `medium` has no row in the vendor table — mapping it to `high` is OUR
592
+ * compatibility choice for clients that only speak the OpenAI ladder.
593
+ */
594
+ const DEEPSEEK_FLASH_THINKING_EFFORTS = ["low", "high", "max"];
595
+ const DEEPSEEK_PRO_THINKING_EFFORTS = ["low", "high", "max"];
596
+ const DEEPSEEK_PRO_REASONING_MAP: Record<string, string> = {
597
+ low: "low",
321
598
  medium: "high",
322
599
  high: "high",
323
- xhigh: "max",
600
+ xhigh: "high",
324
601
  max: "max",
325
602
  };
603
+ const DEEPSEEK_FLASH_REASONING_MAP: Record<string, string> = {
604
+ low: "low",
605
+ medium: "high",
606
+ high: "high",
607
+ xhigh: "high",
608
+ max: "max",
609
+ };
610
+ /**
611
+ * Flash-versus-Pro classification for DeepSeek V4 model ids, including prefixed
612
+ * (`deepseek/deepseek-v4-pro`) and suffixed (`deepseek-v4-flash-free`) forms.
613
+ * `tests/provider-registry-parity.test.ts` enumerates every id the registry
614
+ * actually passes here, so a future id this substring test would misread cannot
615
+ * land silently.
616
+ */
617
+ const isDeepseekFlashModel = (modelId: string): boolean =>
618
+ modelId.toLowerCase().includes("flash");
619
+ const deepseekThinkingEffortsFor = (modelId: string): string[] =>
620
+ isDeepseekFlashModel(modelId) ? DEEPSEEK_FLASH_THINKING_EFFORTS : DEEPSEEK_PRO_THINKING_EFFORTS;
621
+ const deepseekReasoningMapFor = (modelId: string): Record<string, string> =>
622
+ isDeepseekFlashModel(modelId) ? DEEPSEEK_FLASH_REASONING_MAP : DEEPSEEK_PRO_REASONING_MAP;
326
623
  // 260719 Alibaba Token Plan Personal Edition (China/Beijing). Keep it distinct from
327
624
  // Coding Plan: the products use different exact allowlists and different base URLs.
328
625
  // Evidence: https://help.aliyun.com/en/model-studio/token-plan-personal-overview
329
626
  // https://help.aliyun.com/en/model-studio/token-plan-quickstart
330
627
  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",
628
+ "qwen3.8-max", "qwen3.7-max", "qwen3.7-plus", "qwen3.6-flash",
629
+ "glm-5.3", "glm-5.3-flash", "glm-5.2", "deepseek-v4-pro",
333
630
  ];
334
631
  const ALIBABA_TOKEN_PLAN_QWEN_MODELS = [
335
- "qwen3.8-max-preview", "qwen3.7-max", "qwen3.7-plus", "qwen3.6-flash",
632
+ "qwen3.8-max", "qwen3.7-max", "qwen3.7-plus", "qwen3.6-flash",
336
633
  ];
337
634
  const ALIBABA_TOKEN_PLAN_INPUT_MODALITIES: Record<string, string[]> = {
338
- "qwen3.8-max-preview": ["text", "image"],
635
+ "qwen3.8-max": ["text", "image"],
339
636
  "qwen3.7-max": ["text", "image"],
340
637
  "qwen3.7-plus": ["text", "image"],
341
638
  "qwen3.6-flash": ["text", "image"],
639
+ "glm-5.3": ["text"],
640
+ "glm-5.3-flash": ["text", "image"],
342
641
  "glm-5.2": ["text"],
343
642
  "deepseek-v4-pro": ["text"],
344
643
  };
@@ -348,14 +647,14 @@ const ALIBABA_TOKEN_PLAN_INPUT_MODALITIES: Record<string, string[]> = {
348
647
  // Evidence: https://www.alibabacloud.com/help/en/model-studio/token-plan-overview
349
648
  // https://qwencloud.com/pricing/token-plan (qwen3.8 metadata)
350
649
  const ALIBABA_INTL_TOKEN_PLAN_MODELS = [
351
- "qwen3.8-max-preview", "qwen3.7-max", "qwen3.7-plus", "qwen3.6-plus", "qwen3.6-flash",
650
+ "qwen3.8-max", "qwen3.7-max", "qwen3.7-plus", "qwen3.6-plus", "qwen3.6-flash",
352
651
  "deepseek-v4-pro", "deepseek-v4-flash", "deepseek-v3.2",
353
652
  "kimi-k2.7-code", "kimi-k2.6", "kimi-k2.5",
354
- "glm-5.2", "glm-5.1", "glm-5",
653
+ "glm-5.3", "glm-5.3-flash", "glm-5.2", "glm-5.1", "glm-5",
355
654
  "MiniMax-M2.5",
356
655
  ];
357
656
  const ALIBABA_INTL_TOKEN_PLAN_QWEN_MODELS = [
358
- "qwen3.8-max-preview", "qwen3.7-max", "qwen3.7-plus", "qwen3.6-plus", "qwen3.6-flash",
657
+ "qwen3.8-max", "qwen3.7-max", "qwen3.7-plus", "qwen3.6-plus", "qwen3.6-flash",
359
658
  ];
360
659
 
361
660
  // 260722 Tencent Cloud Coding Plan. The plan's model set is explicitly dynamic; these are the
@@ -377,7 +676,7 @@ const TENCENT_CODING_PLAN_MODELS = ["tc-code-latest", "glm-5", "kimi-k2.5", "min
377
676
  // is documented as grounds for suspension — see the `note` on both Plan entries.
378
677
  // Report a break by opening an issue tagging the owner; the three things that rot first are the
379
678
  // 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
679
+ // Full evidence ledger: devlog/_fin/260801_pr611_volcengine_evidence/000_evidence_ledger.md
381
680
  const VOLCENGINE_ARK_MODELS = [
382
681
  "doubao-seed-2-1-pro-260628",
383
682
  "doubao-seed-2-1-turbo-260628",
@@ -385,6 +684,8 @@ const VOLCENGINE_ARK_MODELS = [
385
684
  "deepseek-v4-pro-260425",
386
685
  "deepseek-v4-flash-260425",
387
686
  "deepseek-v3-2-251201",
687
+ // No glm-5-3 row: Ark pins date-stamped snapshot ids (glm-5-2-260617) that cannot be
688
+ // guessed ahead of the vendor publishing them. Add it once /api/v3/models lists one.
388
689
  "glm-5-2-260617",
389
690
  "glm-4-7-251222",
390
691
  ];
@@ -398,6 +699,8 @@ const VOLCENGINE_CODING_PLAN_MODELS = [
398
699
  "doubao-seed-2.0-code",
399
700
  "deepseek-v4-pro",
400
701
  "deepseek-v4-flash",
702
+ "glm-5.3",
703
+ "glm-5.3-flash",
401
704
  "glm-5.2",
402
705
  "kimi-k2.6",
403
706
  "minimax-m3",
@@ -405,6 +708,8 @@ const VOLCENGINE_CODING_PLAN_MODELS = [
405
708
  const VOLCENGINE_AGENT_PLAN_MODELS = [
406
709
  "deepseek-v4-pro",
407
710
  "deepseek-v4-flash",
711
+ "glm-5.3",
712
+ "glm-5.3-flash",
408
713
  "glm-5.2",
409
714
  "kimi-k2.6",
410
715
  "minimax-m3",
@@ -413,6 +718,9 @@ const VOLCENGINE_AGENT_PLAN_MODELS = [
413
718
  const VOLCENGINE_PLAN_INPUT_MODALITIES: Record<string, string[]> = {
414
719
  "kimi-k2.6": ["text", "image"],
415
720
  "minimax-m3": ["text", "image"],
721
+ // Native VLM (docs.z.ai/guides/vlm/glm-5.3-flash), so it is declared here and left
722
+ // out of the text-only list below.
723
+ "glm-5.3-flash": ["text", "image"],
416
724
  };
417
725
  // Every other Plan model is text-only. Declaring this explicitly keeps the vision
418
726
  // sidecar from advertising image input for models that cannot accept it — the same
@@ -422,11 +730,12 @@ const VOLCENGINE_PLAN_TEXT_ONLY_MODELS = [
422
730
  "doubao-seed-2.0-code",
423
731
  "deepseek-v4-pro",
424
732
  "deepseek-v4-flash",
733
+ "glm-5.3",
425
734
  "glm-5.2",
426
735
  "doubao-seed-2.0-pro",
427
736
  ];
428
737
  const ALIBABA_INTL_TOKEN_PLAN_INPUT_MODALITIES: Record<string, string[]> = {
429
- "qwen3.8-max-preview": ["text", "image"],
738
+ "qwen3.8-max": ["text", "image"],
430
739
  "qwen3.7-max": ["text", "image"],
431
740
  "qwen3.7-plus": ["text", "image"],
432
741
  "qwen3.6-plus": ["text", "image"],
@@ -437,6 +746,8 @@ const ALIBABA_INTL_TOKEN_PLAN_INPUT_MODALITIES: Record<string, string[]> = {
437
746
  "kimi-k2.7-code": ["text", "image"],
438
747
  "kimi-k2.6": ["text", "image"],
439
748
  "kimi-k2.5": ["text", "image"],
749
+ "glm-5.3": ["text"],
750
+ "glm-5.3-flash": ["text", "image"],
440
751
  "glm-5.2": ["text"],
441
752
  "glm-5.1": ["text"],
442
753
  "glm-5": ["text"],
@@ -496,6 +807,77 @@ const NVIDIA_NIM_KIMI_MODELS = [
496
807
  ...NVIDIA_NIM_KIMI_THINKING_MODELS,
497
808
  "moonshotai/kimi-k2-instruct", "moonshotai/kimi-k2-instruct-0905",
498
809
  ];
810
+ /**
811
+ * 260804 issue #956: NIM publishes no input-modality metadata on `/v1/models`, so the
812
+ * registry is the only source of truth for which models can see images.
813
+ *
814
+ * Two lists, both verified per-model against NVIDIA documentation on 2026-08-04
815
+ * (build.nvidia.com model pages and docs.api.nvidia.com/nim/reference/*). Evidence and
816
+ * the per-id audit: devlog/_fin/260804_stack7_service_vision/011_nim_id_audit.md.
817
+ *
818
+ * Read `noVisionModels` carefully — it lists models that CANNOT see images, which is
819
+ * what routes them through the proxy's vision sidecar (src/vision/index.ts) and makes the
820
+ * catalog advertise image input for them. Membership is wrong in BOTH directions:
821
+ * - a text-only model missing from it keeps issue #956 (images blocked or rejected);
822
+ * - a vision model wrongly IN it gets its image silently replaced by another model's
823
+ * text description — no error, worse answers, extra cost.
824
+ *
825
+ * A new NIM id must be classified DELIBERATELY against its NVIDIA page, never assumed
826
+ * from its name: `google/gemma-4-31b-it` carries no vision marker yet accepts images,
827
+ * `-vl` also appears on embedding/reranking models, and `google/codegemma-7b` is
828
+ * text-only while `google/codegemma-1.1-7b` has no current page at all. An unclassified
829
+ * id is intentionally left alone rather than defaulted, because NIM serves non-chat
830
+ * endpoints (embeddings, rerankers, guards, OCR) that reach the same code path.
831
+ */
832
+ const NVIDIA_NIM_VISION_MODELS = [
833
+ "meta/llama-3.2-11b-vision-instruct", "meta/llama-3.2-90b-vision-instruct",
834
+ "nvidia/llama-3.1-nemotron-nano-vl-8b-v1", "nvidia/nemotron-nano-12b-v2-vl",
835
+ "nvidia/nemotron-3-nano-omni-30b-a3b-reasoning", "nvidia/cosmos3-nano-reasoner",
836
+ "nvidia/ising-calibration-1.5-31b", "nvidia/ising-calibration-1-35b-a3b",
837
+ "google/gemma-4-31b-it", "google/diffusiongemma-26b-a4b-it",
838
+ "minimaxai/minimax-m3", "moonshotai/kimi-k2.6", "moonshotai/kimi-k2.5",
839
+ "stepfun-ai/step-3.7-flash", "thinkingmachines/inkling",
840
+ "mistralai/mistral-medium-3.5-128b",
841
+ "z-ai/glm-5.3-flash",
842
+ ];
843
+ /**
844
+ * The catalog advertises image input only for `noVisionModels` members, so a natively
845
+ * vision-capable model would otherwise be published as text-only and the Codex app would
846
+ * block attachments before the native path ever runs.
847
+ */
848
+ const NVIDIA_NIM_VISION_INPUT_MODALITIES: Record<string, string[]> = Object.fromEntries(
849
+ NVIDIA_NIM_VISION_MODELS.map(id => [id, ["text", "image"]]),
850
+ );
851
+ /**
852
+ * Text-only NIM chat models — 26 ids, each carrying an explicit `Input Modalities: Text`
853
+ * (or equivalent) on its NVIDIA page. PR #964 proposed ~64; six of those are natively
854
+ * image-capable and live in NVIDIA_NIM_VISION_MODELS above, and 32 more had no current
855
+ * NVIDIA page and were dropped rather than assumed.
856
+ *
857
+ * kimi-k2-thinking and kimi-k2-instruct are text-only while k2.5/k2.6 are not — vision
858
+ * and reasoning are independent axes, so all four stay in NVIDIA_NIM_KIMI_MODELS for
859
+ * reasoning suppression regardless of which list they appear in here.
860
+ */
861
+ const NVIDIA_NIM_NO_VISION_MODELS = [
862
+ "deepseek-ai/deepseek-v4-flash", "deepseek-ai/deepseek-v4-pro",
863
+ "google/codegemma-7b",
864
+ "meta/llama-3.1-70b-instruct", "meta/llama-3.1-8b-instruct",
865
+ "meta/llama-3.2-1b-instruct", "meta/llama-3.2-3b-instruct",
866
+ "meta/llama-3.3-70b-instruct", "meta/llama2-70b",
867
+ "mistralai/mistral-7b-instruct-v0.3", "mistralai/mistral-nemotron",
868
+ "moonshotai/kimi-k2-thinking", "moonshotai/kimi-k2-instruct",
869
+ "nvidia/llama-3.1-nemotron-nano-8b-v1", "nvidia/llama-3.1-nemotron-ultra-253b-v1",
870
+ "nvidia/llama-3.3-nemotron-super-49b-v1", "nvidia/llama-3.3-nemotron-super-49b-v1.5",
871
+ "nvidia/nemotron-3-nano-30b-a3b", "nvidia/nemotron-3-super-120b-a12b",
872
+ "nvidia/nemotron-3-ultra-550b-a55b", "nvidia/nemotron-mini-4b-instruct",
873
+ "nvidia/nvidia-nemotron-nano-9b-v2",
874
+ "openai/gpt-oss-120b", "openai/gpt-oss-20b",
875
+ // z-ai/glm-5.3-flash belongs in NVIDIA_NIM_VISION_MODELS, not here: Z.AI documents
876
+ // it under docs.z.ai/guides/vlm/. The header above says an id must be classified
877
+ // deliberately rather than assumed from its name, and inheriting glm-5.3's
878
+ // text-only verdict because of the shared prefix is exactly that mistake.
879
+ "poolside/laguna-xs-2.1", "z-ai/glm-5.3", "z-ai/glm-5.2",
880
+ ];
499
881
  const KIMI_CODING_MODEL_CONTEXT_WINDOWS: Record<string, number> = Object.fromEntries(
500
882
  KIMI_CODING_MODELS.map(id => [id, id === "k3[1m]" ? KIMI_K3_1M_CONTEXT_WINDOW : KIMI_K3_STANDARD_CONTEXT_WINDOW]),
501
883
  );
@@ -503,6 +885,7 @@ const KIMI_CODING_MODEL_INPUT_MODALITIES = Object.fromEntries(
503
885
  KIMI_CODING_K3_MODELS.map(id => [id, ["text", "image"]]),
504
886
  );
505
887
  const NEURALWATT_REASONING_HISTORY_MODELS = [
888
+ "glm-5.3", "glm-5.3-short", "glm-5.3-flash",
506
889
  "glm-5.2", "glm-5.2-short",
507
890
  "kimi-k2.6", "kimi-k2.7-code",
508
891
  "qwen3.5-397b", "qwen3.6-35b",
@@ -521,6 +904,9 @@ const BASETEN_MODEL_REASONING_EFFORTS: Record<string, string[]> = {
521
904
  "thinkingmachines/inkling": BASETEN_FULL_REASONING_EFFORTS,
522
905
  "openai/gpt-oss-120b": BASETEN_FULL_REASONING_EFFORTS,
523
906
  "moonshotai/Kimi-K3": ["low", "high", "max"],
907
+ // 260814: GLM-5.3 honours low/high/max upstream, unlike 5.2's high/max on Baseten.
908
+ "zai-org/GLM-5.3": ["low", "high", "max"],
909
+ "zai-org/GLM-5.3-Fast": ["low", "high", "max"],
524
910
  "zai-org/GLM-5.2": ["high", "max"],
525
911
  "zai-org/GLM-5.2-Fast": ["high", "max"],
526
912
  };
@@ -529,6 +915,8 @@ const BASETEN_MODEL_REASONING_EFFORT_MAP: Record<string, Record<string, string>>
529
915
  "thinkingmachines/inkling": { none: "none", minimal: "minimal" },
530
916
  "openai/gpt-oss-120b": { none: "none", minimal: "minimal" },
531
917
  "moonshotai/Kimi-K3": { none: "none" },
918
+ "zai-org/GLM-5.3": { none: "none" },
919
+ "zai-org/GLM-5.3-Fast": { none: "none" },
532
920
  "zai-org/GLM-5.2": { none: "none" },
533
921
  "zai-org/GLM-5.2-Fast": { none: "none" },
534
922
  };
@@ -544,21 +932,94 @@ const BASETEN_MODEL_INPUT_MODALITIES: Record<string, string[]> = {
544
932
  "moonshotai/Kimi-K2.7-Code": ["text", "image"],
545
933
  "moonshotai/Kimi-K3": ["text", "image"],
546
934
  };
935
+
936
+ // 260801 DigitalOcean and Scaleway expose OpenAI-shaped `/v1/models` rows with only
937
+ // id/object/created/owned_by, while their shared serverless catalogs also contain
938
+ // non-chat and endpoint-specific models. Fail closed by intersecting live discovery
939
+ // with ids that the providers' current first-party model tables establish for Chat
940
+ // Completions. A newly listed id therefore needs a docs-backed registry refresh before
941
+ // it can enter the Codex catalog.
942
+ // Evidence: https://docs.digitalocean.com/products/inference/details/models/
943
+ // https://docs.digitalocean.com/reference/api/reference/serverless-inference/
944
+ // https://www.scaleway.com/en/docs/generative-apis/reference-content/supported-models/
945
+ const DIGITALOCEAN_CHAT_COMPLETION_MODELS = [
946
+ "arcee-trinity-large-thinking",
947
+ "openai-gpt-5.6-sol",
948
+ "openai-gpt-5.6-terra",
949
+ "openai-gpt-5.6-luna",
950
+ "qwen3-coder-flash",
951
+ "qwen3.5-397b-a17b",
952
+ "deepseek-v4-pro",
953
+ "deepseek-4-flash",
954
+ "deepseek-3.2",
955
+ "gemma-4-31B-it",
956
+ "minimax-m2.5",
957
+ "kimi-k3",
958
+ "kimi-k2.6",
959
+ "kimi-k2.5",
960
+ "llama3.3-70b-instruct",
961
+ "llama-4-maverick",
962
+ "mistral-3-14B",
963
+ "nemotron-3-ultra-550b",
964
+ "nvidia-nemotron-3-super-120b",
965
+ "nemotron-3-nano-omni",
966
+ "nemotron-nano-12b-v2-vl",
967
+ "mimo-v2.5-pro",
968
+ "glm-5.3",
969
+ "glm-5.3-flash",
970
+ "glm-5.2",
971
+ "glm-5.1",
972
+ "glm-5",
973
+ // The API reference uses this native slash id in its Chat Completions example.
974
+ "meta-llama/Meta-Llama-3.1-8B-Instruct",
975
+ ] as const;
976
+ const SCALEWAY_SERVERLESS_CHAT_MODELS = [
977
+ "glm-5.3",
978
+ "glm-5.3-flash",
979
+ "glm-5.2",
980
+ // gpt-oss-120b is intentionally omitted: Scaleway requires Responses API for tool calling,
981
+ // while this preset routes Codex agent tools through Chat Completions.
982
+ "qwen3.6-35b-a3b",
983
+ "qwen3.5-397b-a17b",
984
+ "qwen3-235b-a22b-instruct-2507",
985
+ "qwen3-coder-30b-a3b-instruct",
986
+ "gemma-4-26b-a4b-it",
987
+ "llama-3.3-70b-instruct",
988
+ "mistral-medium-3.5-128b",
989
+ "mistral-small-3.2-24b-instruct-2506",
990
+ "pixtral-12b-2409",
991
+ ] as const;
992
+ const SCALEWAY_MODEL_INPUT_MODALITIES: Record<string, string[]> = {
993
+ "pixtral-12b-2409": ["text", "image"],
994
+ };
547
995
  const UMANS_MODELS = [
548
996
  "umans-coder",
549
997
  "umans-kimi-k2.7",
550
998
  "umans-flash",
999
+ "umans-glm-5.3",
1000
+ "umans-glm-5.3-flash",
551
1001
  "umans-glm-5.2",
552
1002
  "umans-glm-5.1",
553
1003
  "umans-qwen3.6-35b-a3b",
554
1004
  ];
555
1005
  const UMANS_REASONING_EFFORTS = ["low", "medium", "high", "xhigh", "max"];
556
1006
  const UMANS_GLM_REASONING_EFFORTS = ["high", "xhigh", "max"];
557
- const UMANS_TEXT_ONLY_MODELS = ["umans-glm-5.2", "umans-glm-5.1"];
1007
+ // 260814: Z.AI folds GLM-5.3 efforts into low/high/max, so `low` is a real tier here and
1008
+ // `xhigh` is not distinct from `max` (docs.z.ai/devpack/latest-model).
1009
+ const UMANS_GLM_53_REASONING_EFFORTS = ["low", "high", "max"];
1010
+ // `umans-glm-5.3-flash` is NOT here: Z.AI documents glm-5.3-flash under
1011
+ // docs.z.ai/guides/vlm/, so it takes images natively and does not need the proxy's
1012
+ // vision sidecar. The seeding pass classified it from the family name and a later
1013
+ // pass corrected only some of the providers; this is one it missed.
1014
+ const UMANS_TEXT_ONLY_MODELS = ["umans-glm-5.3", "umans-glm-5.2", "umans-glm-5.1"];
558
1015
  const UMANS_MODEL_CONTEXT_WINDOWS: Record<string, number> = {
559
1016
  "umans-coder": 262_144,
560
1017
  "umans-kimi-k2.7": 262_144,
561
1018
  "umans-flash": 262_144,
1019
+ "umans-glm-5.3": 405_504,
1020
+ // Mirrors the sibling this provider already carries. Umans has not published a
1021
+ // separate window for the flash tier; asserting a different number would be a guess.
1022
+ "umans-glm-5.3-flash": 405_504,
562
1023
  "umans-glm-5.2": 405_504,
563
1024
  "umans-glm-5.1": 202_752,
564
1025
  "umans-qwen3.6-35b-a3b": 262_144,
@@ -566,6 +1027,55 @@ const UMANS_MODEL_CONTEXT_WINDOWS: Record<string, number> = {
566
1027
  const UMANS_MODEL_INPUT_MODALITIES: Record<string, string[]> = Object.fromEntries(
567
1028
  UMANS_MODELS.map(id => [id, UMANS_TEXT_ONLY_MODELS.includes(id) ? ["text"] : ["text", "image"]]),
568
1029
  );
1030
+ const CLINE_PASS_MODELS = [
1031
+ "cline-pass/glm-5.3",
1032
+ "cline-pass/glm-5.3-flash",
1033
+ "cline-pass/glm-5.2",
1034
+ "cline-pass/kimi-k3",
1035
+ "cline-pass/kimi-k2.7-code",
1036
+ "cline-pass/kimi-k2.6",
1037
+ "cline-pass/deepseek-v4-pro",
1038
+ "cline-pass/deepseek-v4-flash",
1039
+ "cline-pass/mimo-v2.5",
1040
+ "cline-pass/mimo-v2.5-pro",
1041
+ "cline-pass/minimax-m3",
1042
+ "cline-pass/qwen3.8-max",
1043
+ "cline-pass/qwen3.7-max",
1044
+ "cline-pass/qwen3.7-plus",
1045
+ ];
1046
+ const CLINE_PASS_MODEL_CONTEXT_WINDOWS: Record<string, number> = {
1047
+ "cline-pass/glm-5.3": 1_048_576,
1048
+ "cline-pass/glm-5.3-flash": 1_048_576,
1049
+ "cline-pass/glm-5.2": 1_048_576,
1050
+ "cline-pass/kimi-k3": 1_048_576,
1051
+ "cline-pass/kimi-k2.7-code": 262_144,
1052
+ "cline-pass/kimi-k2.6": 262_144,
1053
+ "cline-pass/deepseek-v4-pro": 1_048_576,
1054
+ "cline-pass/deepseek-v4-flash": 1_048_576,
1055
+ "cline-pass/mimo-v2.5": 1_050_000,
1056
+ "cline-pass/mimo-v2.5-pro": 1_050_000,
1057
+ "cline-pass/minimax-m3": 1_048_576,
1058
+ "cline-pass/qwen3.7-max": 1_000_000,
1059
+ "cline-pass/qwen3.7-plus": 1_000_000,
1060
+ };
1061
+ const CLINE_PASS_IMAGE_MODELS = new Set([
1062
+ "cline-pass/kimi-k3",
1063
+ "cline-pass/kimi-k2.7-code",
1064
+ "cline-pass/kimi-k2.6",
1065
+ "cline-pass/mimo-v2.5",
1066
+ "cline-pass/minimax-m3",
1067
+ "cline-pass/qwen3.7-plus",
1068
+ // Native VLM (docs.z.ai/guides/vlm/), so its images do not go through the proxy's
1069
+ // sidecar. Adding it here moves it out of CLINE_PASS_TEXT_ONLY_MODELS and flips its
1070
+ // declared modalities to ["text", "image"] in one edit, because both are derived
1071
+ // from this set.
1072
+ "cline-pass/glm-5.3-flash",
1073
+ ]);
1074
+ const CLINE_PASS_MODALITY_KNOWN_MODELS = CLINE_PASS_MODELS.filter(id => id !== "cline-pass/qwen3.8-max");
1075
+ const CLINE_PASS_TEXT_ONLY_MODELS = CLINE_PASS_MODALITY_KNOWN_MODELS.filter(id => !CLINE_PASS_IMAGE_MODELS.has(id));
1076
+ const CLINE_PASS_MODEL_INPUT_MODALITIES: Record<string, string[]> = Object.fromEntries(
1077
+ CLINE_PASS_MODALITY_KNOWN_MODELS.map(id => [id, CLINE_PASS_IMAGE_MODELS.has(id) ? ["text", "image"] : ["text"]]),
1078
+ );
569
1079
 
570
1080
  export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [
571
1081
  {
@@ -575,6 +1085,7 @@ export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [
575
1085
  baseUrl: "https://chatgpt.com/backend-api/codex",
576
1086
  authKind: "forward",
577
1087
  codexAccountMode: "pool",
1088
+ supportsServiceTier: true,
578
1089
  featured: true,
579
1090
  note: "Codex login account pool (default) or Direct main-account mode via codexAccountMode",
580
1091
  },
@@ -599,11 +1110,10 @@ export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [
599
1110
  // no-effort fallback to `kimi-k3-max` would never be reached. Mirrors the other K3
600
1111
  // routes (kimi, kimi-code, opencode-go).
601
1112
  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),
1113
+ // Blind Cursor models (Auto routers, Composer, GLM-5.2, GLM-5.3) go through the vision sidecar;
1114
+ // multimodal hosts (Claude/Gemini/GPT/Kimi/Grok) take native SelectedImage. The catalog
1115
+ // still advertises image for noVision members so Codex can attach (sidecar option B).
1116
+ noVisionModels: [...CURSOR_NO_VISION_MODELS],
607
1117
  },
608
1118
  {
609
1119
  id: "xai",
@@ -612,9 +1122,21 @@ export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [
612
1122
  baseUrl: "https://api.x.ai/v1",
613
1123
  authKind: "oauth",
614
1124
  allowKeyAuthOverride: true,
1125
+ // Priority Processing is documented for xAI's public API-key Chat Completions and
1126
+ // Responses endpoints. OAuth is a separate Grok CLI subscription gateway and remains
1127
+ // unclassified; do not turn this into a provider-wide supportsServiceTier declaration.
1128
+ keyAuthServiceTier: {
1129
+ supportsServiceTier: true,
1130
+ chatServiceTier: true,
1131
+ },
1132
+ fastTierDescription: "Priority processing, 2x token price",
615
1133
  featured: true,
616
1134
  oauthId: "xai",
617
1135
  jawcodeBundle: "xai",
1136
+ supportsOpenAiWebSearchToolFields: false,
1137
+ // Live A/B on 2026-08-20: xAI rejects native custom/custom_tool_call shapes while accepting
1138
+ // the otherwise-identical request after the custom tool is lowered to a function.
1139
+ supportsResponsesCustomTools: false,
618
1140
  note: "Log in with your Grok account",
619
1141
  // Parallel tool calls: officially supported and default-on per docs.x.ai function-calling
620
1142
  // (verified 260709, devlog/_plan/260709_parallel_tool_calls). Streamed calls arrive whole
@@ -628,8 +1150,38 @@ export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [
628
1150
  // devlog/model_update/260709_model_refresh/001_xai_lineup.md.
629
1151
  // grok-4.20-multi-agent-0309 is intentionally absent: the OAuth chat-completions
630
1152
  // 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"],
1153
+ // 260813: grok-4.6 added per docs.x.ai/developers/grok-4-6. Context/vision still match
1154
+ // grok-4.5; the reasoning ladder does not — 4.6 adds the documented xhigh rung.
1155
+ models: XAI_MODELS,
1156
+ // Measured only on grok-4.6 against cli-chat-proxy.grok.com: even an invalid
1157
+ // `text.verbosity` value is accepted and low/high/omitted output length is non-monotonic.
1158
+ // Apply the resulting opt-out to the whole xAI lineup because `text.verbosity` is an OpenAI
1159
+ // Responses parameter absent from xAI's documented API, not because every model was probed.
1160
+ // Keep this separate from reasoning-summary support: that bit gates Codex's
1161
+ // entire Responses reasoning object, including reasoning.effort.
1162
+ modelSupportsVerbosity: Object.fromEntries(XAI_MODELS.map(id => [id, false])),
1163
+ // Provider-wide, not merely per-model: `text.verbosity` is an OpenAI Responses parameter
1164
+ // absent from xAI's documented API, so a model discovered later has no more support for it
1165
+ // than the seeded ones do.
1166
+ supportsVerbosity: false,
632
1167
  defaultModel: "grok-4.5",
1168
+ // Keep Codex Responses callers on the compatibility Chat wire until xAI can replay
1169
+ // opaque reasoning continuation and compaction state across later turns. The scoped
1170
+ // declaration also keeps caller-owned service tiers off the OAuth subscription route.
1171
+ modelWireDefaults: {
1172
+ "grok-4.6": {
1173
+ wire: "openai-chat",
1174
+ inbound: ["responses"],
1175
+ authModes: ["oauth"],
1176
+ forwardCallerServiceTier: false,
1177
+ },
1178
+ "grok-4.5": {
1179
+ wire: "openai-chat",
1180
+ inbound: ["responses"],
1181
+ authModes: ["oauth"],
1182
+ forwardCallerServiceTier: false,
1183
+ },
1184
+ },
633
1185
  // Vision lineup per docs.x.ai model-capabilities/images/understanding: the grok-4.x chat
634
1186
  // models accept image input (JPEG/PNG, URL or base64). Without this the catalog leaves
635
1187
  // inputModalities undefined, and deriveComboCatalogModel defaults an undefined member to
@@ -637,6 +1189,7 @@ export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [
637
1189
  // the app blocks attachments client-side. grok-build-0.1 / grok-composer-2.5-fast stay out
638
1190
  // (they are already listed in noVisionModels below).
639
1191
  modelInputModalities: {
1192
+ "grok-4.6": ["text", "image"],
640
1193
  "grok-4.5": ["text", "image"],
641
1194
  "grok-4.3": ["text", "image"],
642
1195
  "grok-4.20-0309-reasoning": ["text", "image"],
@@ -647,10 +1200,14 @@ export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [
647
1200
  // reasoning_content as the top cause of prompt-cache misses on multi-turn conversations
648
1201
  // (docs.x.ai prompt-caching/multi-turn, verified 2026-07-13 — devlog/_plan/260713_grok_caching).
649
1202
  // 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"] },
1203
+ preserveReasoningContentModels: ["grok-4.6", "grok-4.5", "grok-4.3", "grok-4.20-0309-reasoning"],
1204
+ // grok-4.5 reasoning is always-on with low/medium/high (no off tier, no xhigh).
1205
+ // grok-4.6 adds xhigh per docs.x.ai/developers/model-capabilities/text/reasoning;
1206
+ // xAI documents high as the upstream default.
1207
+ modelReasoningEfforts: { "grok-4.6": ["low", "medium", "high", "xhigh"], "grok-4.5": ["low", "medium", "high"] },
1208
+ modelDefaultReasoningEfforts: { "grok-4.6": "high" },
653
1209
  modelContextWindows: {
1210
+ "grok-4.6": 500_000,
654
1211
  "grok-4.5": 500_000,
655
1212
  "grok-4.3": 1_000_000,
656
1213
  "grok-4.20-0309-reasoning": 1_000_000,
@@ -659,12 +1216,45 @@ export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [
659
1216
  },
660
1217
  noVisionModels: ["grok-build-0.1", "grok-composer-2.5-fast"],
661
1218
  },
1219
+ {
1220
+ id: "command-code",
1221
+ label: "Command Code - Auth",
1222
+ adapter: "command-code",
1223
+ baseUrl: "https://api.commandcode.ai",
1224
+ authKind: "oauth",
1225
+ oauthId: "command-code",
1226
+ featured: true,
1227
+ note: "Log in with your Command Code account",
1228
+ // OAuth needs one initial selection, but the exposed catalog is always discovered from the
1229
+ // signed-in account. Do not add a static model list here.
1230
+ defaultModel: "deepseek/deepseek-v4-flash",
1231
+ liveModels: true,
1232
+ modelDiscovery: {
1233
+ url: "https://api.commandcode.ai/provider/v1/models",
1234
+ maxResponseBytes: 262_144,
1235
+ maxModels: 256,
1236
+ },
1237
+ // These are capability facts from official Command Code model profiles, not seeded models.
1238
+ // Unknown/new live models deliberately do not advertise a reasoning picker.
1239
+ reasoningEfforts: [],
1240
+ modelReasoningEfforts: COMMAND_CODE_MODEL_REASONING_EFFORTS,
1241
+ // The DeepSeek vision preview id is preemptive metadata — it is expected to
1242
+ // merge into deepseek-v4-flash later.
1243
+ modelContextWindows: {
1244
+ [`deepseek/${DEEPSEEK_VISION_PREVIEW_MODEL}`]: 1_048_576,
1245
+ },
1246
+ modelInputModalities: COMMAND_CODE_MODEL_INPUT_MODALITIES,
1247
+ defaultMaxOutputTokens: 64_000,
1248
+ // The proprietary generate wire has no verified per-request serialization flag.
1249
+ parallelToolCalls: false,
1250
+ },
662
1251
  {
663
1252
  id: "anthropic",
664
1253
  label: "Anthropic Claude",
665
1254
  adapter: "anthropic",
666
1255
  baseUrl: "https://api.anthropic.com",
667
1256
  authKind: "oauth",
1257
+ allowBaseUrlOverride: true,
668
1258
  featured: true,
669
1259
  oauthId: "anthropic",
670
1260
  jawcodeBundle: "anthropic",
@@ -738,6 +1328,46 @@ export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [
738
1328
  // Per-model context metadata is maintained next to the Kiro model list.
739
1329
  modelContextWindows: KIRO_MODEL_CONTEXT_WINDOWS,
740
1330
  modelReasoningEfforts: KIRO_MODEL_REASONING_EFFORTS,
1331
+ modelSupportsVerbosity: Object.fromEntries(KIRO_MODELS.map(id => [id, false])),
1332
+ },
1333
+ {
1334
+ // Nous Portal — Nous Research subscription gateway (same backend Hermes Agent
1335
+ // uses). OAuth is a device grant (src/oauth/nous.ts): the access token IS the
1336
+ // per-request inference JWT (scope inference:invoke), refresh tokens are
1337
+ // single-use and rotated on every refresh. Catalog is a mix of paid models
1338
+ // (billed against the Portal subscription) and `:free` slugs (e.g.
1339
+ // tencent/hy3:free, stepfun/step-3.7-flash:free, inclusionai/ling-3.0-flash:free);
1340
+ // free-tier gating is decided live by the Portal per account, so discovery
1341
+ // from the signed-in account is authoritative; the static seed below is the
1342
+ // logged-out fallback and only lists free models verified on a real account
1343
+ // (2026-08-10): the Portal free list is authoritative and currently has
1344
+ // exactly 4 :free models: tencent/hy3:free, poolside/laguna-s-2.1:free,
1345
+ // stepfun/step-3.7-flash:free, poolside/laguna-xs-2.1:free.
1346
+ // inclusionai/ling-3.0-flash:free was removed from the Portal free list
1347
+ // (404 on the inference API since 2026-08-07) and must not be seeded.
1348
+ id: "nous",
1349
+ label: "Nous Portal",
1350
+ adapter: "openai-chat",
1351
+ baseUrl: "https://inference-api.nousresearch.com/v1",
1352
+ authKind: "oauth",
1353
+ oauthId: "nous",
1354
+ featured: true,
1355
+ // Mixed free + paid provider: the free tier is per-model (the `:free`
1356
+ // slugs), not a property of the whole provider, so freeTier stays false to
1357
+ // avoid implying every model is free.
1358
+ freeTier: false,
1359
+ dashboardUrl: "https://portal.nousresearch.com",
1360
+ defaultModel: "tencent/hy3:free",
1361
+ liveModels: true,
1362
+ models: ["tencent/hy3:free", "poolside/laguna-s-2.1:free", "stepfun/step-3.7-flash:free", "poolside/laguna-xs-2.1:free"],
1363
+ modelDiscovery: {
1364
+ // Resolves against effectiveBaseUrl (registry baseUrl .../v1) to the same
1365
+ // canonical endpoint https://inference-api.nousresearch.com/v1/models.
1366
+ path: "models",
1367
+ maxResponseBytes: 262_144,
1368
+ maxModels: 512,
1369
+ },
1370
+ 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).",
741
1371
  },
742
1372
  {
743
1373
  id: "openai-apikey",
@@ -745,19 +1375,24 @@ export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [
745
1375
  adapter: "openai-responses",
746
1376
  baseUrl: "https://api.openai.com/v1",
747
1377
  authKind: "key",
1378
+ supportsServiceTier: true,
748
1379
  featured: true,
749
1380
  dashboardUrl: "https://platform.openai.com/api-keys",
750
1381
  defaultModel: "gpt-5.5",
751
- models: ["gpt-5.5", ...OPENAI_GPT56_MODELS, ...OPENAI_GPT56_PRO_MODELS],
1382
+ models: ["gpt-5.5", ...OPENAI_GPT56_MODELS, ...OPENAI_GPT56_PRO_MODELS, ...OPENAI_DAYBREAK_MODELS],
752
1383
  liveModels: true,
753
- modelContextWindows: OPENAI_API_GPT56_CONTEXT_WINDOWS,
754
- modelMaxInputTokens: OPENAI_API_GPT56_MAX_INPUT_TOKENS,
1384
+ modelContextWindows: { ...OPENAI_API_GPT56_CONTEXT_WINDOWS, ...OPENAI_DAYBREAK_CONTEXT_WINDOWS },
1385
+ modelMaxInputTokens: { ...OPENAI_API_GPT56_MAX_INPUT_TOKENS, ...OPENAI_DAYBREAK_MAX_INPUT_TOKENS },
755
1386
  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]),
1387
+ ["gpt-5.5", ...OPENAI_GPT56_MODELS, ...OPENAI_GPT56_PRO_MODELS, ...OPENAI_DAYBREAK_MODELS]
1388
+ .map(id => [id, ["text", "image"]]),
760
1389
  ),
1390
+ modelReasoningEfforts: {
1391
+ ...Object.fromEntries(
1392
+ [...OPENAI_GPT56_MODELS, ...OPENAI_GPT56_PRO_MODELS].map(id => [id, OPENAI_API_GPT56_REASONING_EFFORTS]),
1393
+ ),
1394
+ ...OPENAI_DAYBREAK_REASONING_EFFORTS,
1395
+ },
761
1396
  virtualModels: OPENAI_API_GPT56_VIRTUAL_MODELS,
762
1397
  },
763
1398
  {
@@ -777,6 +1412,8 @@ export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [
777
1412
  "umans-coder": UMANS_REASONING_EFFORTS,
778
1413
  "umans-kimi-k2.7": UMANS_REASONING_EFFORTS,
779
1414
  "umans-flash": UMANS_REASONING_EFFORTS,
1415
+ "umans-glm-5.3": UMANS_GLM_53_REASONING_EFFORTS,
1416
+ "umans-glm-5.3-flash": UMANS_GLM_53_REASONING_EFFORTS,
780
1417
  "umans-glm-5.2": UMANS_GLM_REASONING_EFFORTS,
781
1418
  "umans-glm-5.1": UMANS_GLM_REASONING_EFFORTS,
782
1419
  "umans-qwen3.6-35b-a3b": UMANS_REASONING_EFFORTS,
@@ -788,16 +1425,46 @@ export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [
788
1425
  id: "opencode-go", label: "opencode go", adapter: "openai-chat", baseUrl: "https://opencode.ai/zen/go/v1",
789
1426
  authKind: "key", featured: true, dashboardUrl: "https://opencode.ai/auth", defaultModel: "kimi-k2.7-code",
790
1427
  jawcodeBundle: "opencode-go", note: "GLM, DeepSeek, Kimi, Qwen, MiMo…",
791
- modelContextWindows: { "kimi-k3": KIMI_K3_STANDARD_CONTEXT_WINDOW },
792
- modelInputModalities: { "kimi-k3": ["text", "image"] },
1428
+ // Zen Go can close a Chat stream after a fully assembled function call without sending
1429
+ // finish_reason or [DONE] (#2260). The adapter still rejects incomplete argument JSON.
1430
+ openaiChatEofTolerance: true,
1431
+ /* [Decision Log]
1432
+ - 목적과 의도: Route the exact models OpenCode Go documents on the Responses endpoint — GPT 5.6 Luna, and Muse Spark 1.2 Contributor (#2617).
1433
+ - 기존 구현 및 제약 조건: The provider is mixed-wire but its provider-wide `openai-chat` adapter sent Luna to `/chat/completions`; explicit user `modelAdapters` entries must remain authoritative.
1434
+ - 검토한 주요 대안: Change the whole provider to Responses; infer the wire from model-family names; add one registry-only exact-model default.
1435
+ - 선택한 방식: Declare only the named models as `openai-responses` through the existing registry default mechanism; the map stays an exact-model allowlist rather than a family or provider-wide rule.
1436
+ - 다른 대안 대신 이 방식을 선택한 이유: OpenCode Go documents sibling models on Chat or Anthropic endpoints, and an exact registry default preserves both those routes and explicit opt-out precedence.
1437
+ - 장점, 단점 및 영향: Each listed model reaches `/responses` from every inbound surface without changing siblings; a future upstream endpoint change requires an evidence-backed registry update.
1438
+ */
1439
+ modelWireDefaults: { "gpt-5.6-luna": "openai-responses", "muse-spark-1.2-contributor": "openai-responses" },
1440
+ modelContextWindows: {
1441
+ "kimi-k3": KIMI_K3_STANDARD_CONTEXT_WINDOW,
1442
+ // The DeepSeek vision preview id is metadata-only here: the Go roster is
1443
+ // discovered live, so it applies the moment the gateway serves the id.
1444
+ [DEEPSEEK_VISION_PREVIEW_MODEL]: 1_048_576,
1445
+ },
1446
+ modelInputModalities: {
1447
+ "kimi-k3": ["text", "image"],
1448
+ // Experimental DeepSeek vision preview — expected to merge into deepseek-v4-flash later.
1449
+ [DEEPSEEK_VISION_PREVIEW_MODEL]: ["text", "image"],
1450
+ // Muse Spark 1.2 Contributor is natively multimodal on Zen Go: it accepts input_image
1451
+ // parts over /responses (probed 2026-08-26). Without this declaration the catalog
1452
+ // advertises it text-only and the Codex app blocks image attachments client-side with
1453
+ // "This model does not support image inputs" before the request ever reaches the proxy.
1454
+ "muse-spark-1.2-contributor": ["text", "image"],
1455
+ },
793
1456
  modelReasoningEfforts: {
1457
+ "gpt-5.6-luna": OPENAI_API_GPT56_REASONING_EFFORTS,
1458
+ "glm-5.3": ZAI_GLM_53_REASONING_EFFORTS,
1459
+ "glm-5.3-flash": ZAI_GLM_53_REASONING_EFFORTS,
794
1460
  "glm-5.2": ZAI_GLM_52_REASONING_EFFORTS,
1461
+ "qwen3.8-max": QWEN38_REASONING_EFFORTS,
795
1462
  "kimi-k3": KIMI_CODING_K3_REASONING_EFFORTS,
796
1463
  "kimi-k2.7-code": [],
797
1464
  "kimi-k2.7-code-highspeed": [],
798
1465
  ...Object.fromEntries(OPENCODE_GO_THINKING_TOGGLE_MODELS.map(id => [id, THINKING_TOGGLE_EFFORTS])),
799
1466
  ...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])),
1467
+ ...Object.fromEntries(DEEPSEEK_THINKING_MODELS.map(id => [id, deepseekThinkingEffortsFor(id)])),
801
1468
  },
802
1469
  modelDefaultReasoningEfforts: { "kimi-k3": "max" },
803
1470
  // glm-5.2 uses identity labels now that `max` is a native Codex level (no alias map);
@@ -805,7 +1472,15 @@ export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [
805
1472
  modelReasoningEffortMap: {
806
1473
  "kimi-k3": KIMI_CODING_K3_REASONING_EFFORT_MAP,
807
1474
  ...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])),
1475
+ ...Object.fromEntries(DEEPSEEK_THINKING_MODELS.map(id => [id, deepseekReasoningMapFor(id)])),
1476
+ },
1477
+ modelSupportsReasoningSummaries: {
1478
+ "glm-5.3": true,
1479
+ "glm-5.3-flash": true,
1480
+ "glm-5.2": true,
1481
+ "glm-5.1": true,
1482
+ "glm-5": true,
1483
+ ...Object.fromEntries(DEEPSEEK_THINKING_MODELS.map(id => [id, true])),
809
1484
  },
810
1485
  thinkingToggleModels: OPENCODE_GO_THINKING_TOGGLE_MODELS,
811
1486
  thinkingBudgetModels: THINKING_BUDGET_MODELS,
@@ -814,7 +1489,7 @@ export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [
814
1489
  // every model listed here (and the catalog advertises image input on their behalf).
815
1490
  // Kimi K2.7 Code accepts text+image+video: do NOT list it here.
816
1491
  noVisionModels: [
817
- "glm-5.2", "glm-5", "glm-5.1",
1492
+ "glm-5.3", "glm-5.2", "glm-5", "glm-5.1",
818
1493
  "deepseek-v4-flash", "deepseek-v4-pro",
819
1494
  "mimo-v2-pro", "mimo-v2.5-pro",
820
1495
  "minimax-m2.5", "minimax-m2.7",
@@ -825,7 +1500,7 @@ export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [
825
1500
  noPenaltyModels: ["kimi-k3", "kimi-k2.7-code", "kimi-k2.7-code-highspeed"],
826
1501
  autoToolChoiceOnlyModels: ["kimi-k2.7-code", "kimi-k2.7-code-highspeed"],
827
1502
  // 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],
1503
+ preserveReasoningContentModels: ["glm-5.3", "glm-5.3-flash", "glm-5.2", "kimi-k3", "kimi-k2.7-code", "kimi-k2.7-code-highspeed", ...DEEPSEEK_THINKING_MODELS],
829
1504
  },
830
1505
  {
831
1506
  id: "neuralwatt",
@@ -834,10 +1509,14 @@ export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [
834
1509
  baseUrl: "https://api.neuralwatt.com/v1",
835
1510
  authKind: "key",
836
1511
  dashboardUrl: "https://portal.neuralwatt.com",
837
- defaultModel: "glm-5.2",
1512
+ defaultModel: "glm-5.3",
838
1513
  // 2026-07-10 live /v1/models: K2.5 rows were removed and GLM-5.2 short variants added.
1514
+ // 260814: the glm-5.3 quartet is speculative; live discovery is authoritative and drops
1515
+ // any id Neuralwatt has not published yet.
839
1516
  // Evidence: devlog/_plan/260710_provider_hardening/003_research_aggregators.md and https://api.neuralwatt.com/v1/models.
840
1517
  models: [
1518
+ "glm-5.3", "glm-5.3-fast", "glm-5.3-short", "glm-5.3-short-fast",
1519
+ "glm-5.3-flash",
841
1520
  "glm-5.2", "glm-5.2-fast", "glm-5.2-short", "glm-5.2-short-fast",
842
1521
  "kimi-k2.6", "kimi-k2.6-fast",
843
1522
  "kimi-k2.7-code",
@@ -845,6 +1524,13 @@ export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [
845
1524
  ],
846
1525
  // Neuralwatt's /v1/models metadata is authoritative; these static hints are the offline fallback.
847
1526
  modelReasoningEfforts: {
1527
+ "glm-5.3": ZAI_GLM_53_REASONING_EFFORTS,
1528
+ "glm-5.3-fast": [],
1529
+ "glm-5.3-short": ZAI_GLM_53_REASONING_EFFORTS,
1530
+ "glm-5.3-short-fast": [],
1531
+ // No `-fast`/`-short` variants are asserted for the flash tier: those suffixes
1532
+ // encode routing Neuralwatt documents per model, and this seed has no source for them.
1533
+ "glm-5.3-flash": ZAI_GLM_53_REASONING_EFFORTS,
848
1534
  "glm-5.2": ZAI_GLM_52_REASONING_EFFORTS,
849
1535
  "glm-5.2-fast": [],
850
1536
  "glm-5.2-short": ZAI_GLM_52_REASONING_EFFORTS,
@@ -860,15 +1546,114 @@ export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [
860
1546
  "qwen3.6-35b-fast": [],
861
1547
  },
862
1548
  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"],
1549
+ 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"],
1550
+ 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
1551
  noTemperatureModels: ["kimi-k2.7-code"],
866
1552
  noTopPModels: ["kimi-k2.7-code"],
867
1553
  noPenaltyModels: ["kimi-k2.7-code"],
868
1554
  autoToolChoiceOnlyModels: ["kimi-k2.7-code"],
869
1555
  preserveReasoningContentModels: NEURALWATT_REASONING_HISTORY_MODELS,
870
1556
  },
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 } },
1557
+ {
1558
+ id: "openrouter",
1559
+ label: "OpenRouter",
1560
+ adapter: "openai-chat",
1561
+ baseUrl: "https://openrouter.ai/api/v1",
1562
+ authKind: "key",
1563
+ featured: true,
1564
+ dashboardUrl: "https://openrouter.ai/keys",
1565
+ jawcodeBundle: "openrouter",
1566
+ models: ["anthropic/claude-sonnet-5", ...OPENROUTER_GPT56_MODELS],
1567
+ modelContextWindows: {
1568
+ "anthropic/claude-sonnet-5": 1_000_000,
1569
+ ...OPENROUTER_GPT56_CONTEXT_WINDOWS,
1570
+ },
1571
+ // OpenRouter documents priority support for OpenAI endpoints, but not Anthropic. Keep the
1572
+ // provider unclassified and opt in only the exact OpenAI-backed slugs we ship. These facts
1573
+ // belong only to the canonical destination; a same-named custom gateway is unknown to us.
1574
+ modelServiceTierCapabilityBaseUrlGuard: isCanonicalOpenRouterTarget,
1575
+ modelSupportsServiceTier: {
1576
+ "openai/gpt-5.6-sol": true,
1577
+ "openai/gpt-5.6-terra": true,
1578
+ "openai/gpt-5.6-luna": true,
1579
+ },
1580
+ // Deliberately no OpenRouter route pin: it bills the endpoint actually used and reports the
1581
+ // actual service_tier. B0 confirmation therefore owns downgrade safety. Forcing `only` plus
1582
+ // `allow_fallbacks:false` would turn a graceful priority-capacity fallback into a hard failure.
1583
+ },
1584
+ {
1585
+ // Primary sources checked 2026-08-02:
1586
+ // - docs.cline.bot/getting-started/clinepass publishes this exact catalog and explicitly
1587
+ // authorizes using the full slugs through Cline's external API.
1588
+ // - docs.cline.bot/api/chat-completions and /api/errors define the endpoint, reasoning delta,
1589
+ // and choice-scoped mid-stream error contract.
1590
+ // - Cline's official catalog source resolves per-model capabilities through OpenRouter data;
1591
+ // the static context/modality snapshot below was cross-checked against that catalog.
1592
+ // - cline.bot/tos identifies Cline Bot Inc. as the operator. Maintenance owner: @lidge-jun.
1593
+ id: "cline-pass",
1594
+ label: "ClinePass",
1595
+ adapter: "openai-chat",
1596
+ baseUrl: "https://api.cline.bot/api/v1",
1597
+ authKind: "key",
1598
+ dashboardUrl: "https://app.cline.bot",
1599
+ defaultModel: "cline-pass/kimi-k3",
1600
+ models: CLINE_PASS_MODELS,
1601
+ modelContextWindows: CLINE_PASS_MODEL_CONTEXT_WINDOWS,
1602
+ modelInputModalities: CLINE_PASS_MODEL_INPUT_MODALITIES,
1603
+ noVisionModels: CLINE_PASS_TEXT_ONLY_MODELS,
1604
+ // Live-probed 2026-08-13 across every static ClinePass model: the gateway accepts and
1605
+ // validates low/medium/high/xhigh/max, and rejects an invalid sentinel. Preserve the
1606
+ // caller's requested tier and let ClinePass own any backend-specific normalization.
1607
+ reasoningEfforts: ["low", "medium", "high", "xhigh", "max"],
1608
+ reasoningWireFormat: "gateway-object",
1609
+ preserveCustomDestination: true,
1610
+ 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.",
1611
+ },
1612
+ // Cline API (usage-billing): OpenAI-compatible Chat Completions. Model IDs follow the
1613
+ // OpenRouter-style `provider/model` convention. Live /models discovery is key-gated (401
1614
+ // without auth), so the static seed is the cold-start fallback. Evidence: docs.cline.bot/api/*.
1615
+ {
1616
+ id: "cline",
1617
+ label: "Cline",
1618
+ adapter: "openai-chat",
1619
+ baseUrl: "https://api.cline.bot/api/v1",
1620
+ authKind: "oauth",
1621
+ oauthId: "cline",
1622
+ featured: true,
1623
+ dashboardUrl: "https://app.cline.bot",
1624
+ liveModels: true,
1625
+ preserveCustomDestination: true,
1626
+ defaultModel: "anthropic/claude-sonnet-4-6",
1627
+ models: [
1628
+ "anthropic/claude-sonnet-4-6",
1629
+ "openai/gpt-4o",
1630
+ "openai/gpt-4o-mini",
1631
+ "google/gemini-2.5-pro",
1632
+ "deepseek/deepseek-chat",
1633
+ "minimax/minimax-m2.5",
1634
+ "stealth/ox-alpha",
1635
+ ],
1636
+ note: "Log in with your Cline account (imports an existing local Cline CLI / VS Code credential when present).",
1637
+ },
1638
+ {
1639
+ id: "cline-apikey",
1640
+ label: "Cline - API Key",
1641
+ adapter: "openai-chat",
1642
+ baseUrl: "https://api.cline.bot/api/v1",
1643
+ authKind: "key",
1644
+ dashboardUrl: "https://app.cline.bot",
1645
+ liveModels: true,
1646
+ defaultModel: "anthropic/claude-sonnet-4-6",
1647
+ models: [
1648
+ "anthropic/claude-sonnet-4-6",
1649
+ "openai/gpt-4o",
1650
+ "google/gemini-2.5-pro",
1651
+ "deepseek/deepseek-chat",
1652
+ "minimax/minimax-m2.5",
1653
+ ],
1654
+ preserveCustomDestination: true,
1655
+ 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.",
1656
+ },
872
1657
  {
873
1658
  // OrcaRouter: OpenAI-compatible adaptive router (api.orcarouter.ai). Model ids are
874
1659
  // vendor-namespaced (`<vendor>/<model>`) and pass through to the upstream as-is.
@@ -896,9 +1681,9 @@ export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [
896
1681
  // returned 200), so no noTemperatureModels entry is warranted here.
897
1682
  modelReasoningEfforts: {
898
1683
  "openai/gpt-5.5": ["low", "medium", "high", "xhigh"],
899
- "deepseek/deepseek-v4-pro": DEEPSEEK_THINKING_EFFORTS,
1684
+ "deepseek/deepseek-v4-pro": deepseekThinkingEffortsFor("deepseek/deepseek-v4-pro"),
900
1685
  },
901
- modelReasoningEffortMap: { "deepseek/deepseek-v4-pro": DEEPSEEK_THINKING_REASONING_MAP },
1686
+ modelReasoningEffortMap: { "deepseek/deepseek-v4-pro": deepseekReasoningMapFor("deepseek/deepseek-v4-pro") },
902
1687
  preserveReasoningContentModels: ["deepseek/deepseek-v4-pro"],
903
1688
  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
1689
  },
@@ -919,12 +1704,13 @@ export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [
919
1704
  // devlog/_plan/260710_provider_hardening/001_research_frontier.md.
920
1705
  {
921
1706
  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"] },
1707
+ 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"],
1708
+ 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 },
1709
+ modelInputModalities: { "gemini-3.6-flash": ["text", "image"], "gemini-3.5-flash-lite": ["text", "image"], "gemini-3.7-flash": ["text", "image"] },
925
1710
  modelReasoningEfforts: {
926
1711
  "gemini-3.6-flash": ["minimal", "low", "medium", "high"],
927
1712
  "gemini-3.5-flash": ["minimal", "low", "medium", "high"],
1713
+ "gemini-3.7-flash": ["minimal", "low", "medium", "high"],
928
1714
  "gemini-3.1-pro-preview": ["low", "medium", "high"],
929
1715
  },
930
1716
  jawcodeBundle: "google", extraMetadataAliases: ["gemini"],
@@ -932,7 +1718,7 @@ export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [
932
1718
  // 2026-07-10: defaultModel is frozen pending Vertex-specific Tier-2 evidence; Gemini API
933
1719
  // evidence from ai.google.dev does not establish Vertex publisher availability.
934
1720
  { 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"] },
1721
+ { 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
1722
  { 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
1723
  { 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
1724
  { 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 +1730,29 @@ export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [
944
1730
  adapter: "openai-chat",
945
1731
  authKind: "key",
946
1732
  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],
1733
+ // Route DeepSeek's own catalog bundle so routed rebuilds restore the official
1734
+ // context window from the vendored model-metadata bundle instead of falling
1735
+ // back to the 128k strict-fields default (scripts/model-metadata.source.json,
1736
+ // verified 2026-08-08).
1737
+ jawcodeBundle: "deepseek",
1738
+ // deepseek-chat/deepseek-reasoner were deprecated upstream on 2026-07-24 15:59 UTC;
1739
+ // official identifiers are now deepseek-v4-flash / deepseek-v4-pro. They stay in
1740
+ // the list only as compatibility aliases so existing saved configs and requests
1741
+ // keep validating and routing (they previously mapped to v4-flash; devlog
1742
+ // _fin/260710_provider_hardening/002_research_cn.md). The current offerings are
1743
+ // the V4 ids — defaultModel and the model-specific wiring above use them.
1744
+ // deepseek-v4-flash-vision-exp: experimental vision preview (2026-08-21) —
1745
+ // expected to merge into deepseek-v4-flash later; see DEEPSEEK_VISION_PREVIEW_MODEL.
1746
+ models: ["deepseek-chat", "deepseek-reasoner", ...DEEPSEEK_THINKING_MODELS, DEEPSEEK_VISION_PREVIEW_MODEL],
950
1747
  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.
1748
+ // Official DeepSeek Codex setup (codex-deepseek-setup.sh) advertises 1,048,576
1749
+ // for both V4 models; the older 1,000,000 figure was a rounded approximation.
1750
+ modelContextWindows: { "deepseek-v4-flash": 1_048_576, "deepseek-v4-pro": 1_048_576, [DEEPSEEK_VISION_PREVIEW_MODEL]: 1_048_576 },
1751
+ modelInputModalities: { [DEEPSEEK_VISION_PREVIEW_MODEL]: ["text", "image"] },
1752
+ // DeepSeek documents both V4 models as native Responses API models adapted for Codex
1753
+ // (model table marks Responses API ✓ for flash and pro; the /responses reference lists
1754
+ // both ids as accepted `model` values — verified 2026-08-13 with the V4 Pro GA,
1755
+ // version label DeepSeek-V4-Pro-0813).
954
1756
  modelWireDefaults: {
955
1757
  // Codex speaks Responses natively and DeepSeek ships a Codex-compatible
956
1758
  // apply_patch tool on that wire, so a Responses inbound goes straight out with
@@ -959,22 +1761,56 @@ export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [
959
1761
  // translating them into Responses would add a hop onto our newest upstream path
960
1762
  // for no gain.
961
1763
  "deepseek-v4-flash": { wire: "openai-responses", inbound: ["responses"] },
1764
+ "deepseek-v4-pro": { wire: "openai-responses", inbound: ["responses"] },
962
1765
  },
1766
+ // The #875-era bounded-JSON force (`modelResponsesUpstreamStreaming`) is retired
1767
+ // for this entry: the official guide documents a `response.completed` /
1768
+ // `response.incomplete` / `response.failed` terminal with NO `data: [DONE]`
1769
+ // sentinel, and live probes (2026-08-07, including the tool-result replay shape
1770
+ // that originally stalled) close on the terminal. The relay's terminal boundary
1771
+ // (src/server/relay.ts) already cuts the stream at that event and synthesizes
1772
+ // `[DONE]`, so forcing stream:false only delayed every byte until generation
1773
+ // finished (28-46 s of silence on long turns). The registry knob itself remains
1774
+ // for providers that need it — re-adding one line here restores the old policy.
1775
+ // Evidence: https://api-docs.deepseek.com/guides/responses_api/ +
1776
+ // devlog/_fin/260807_deepseek_responses_streaming/000_plan.md.
1777
+ // Current official streams normally carry a real terminal; retain a narrow grace
1778
+ // repair for the historical shape that closes after a complete graph without one.
1779
+ modelResponsesTerminalRepair: { "deepseek-v4-flash": { graceMs: 5_000 }, "deepseek-v4-pro": { graceMs: 5_000 } },
1780
+ // DeepSeek's Responses route emits bare UUID item ids, which leave Codex
1781
+ // clients stuck on an uncommitted turn (#938). Client-facing only — raw
1782
+ // continuation snapshots keep the upstream ids.
1783
+ responsesItemIdRepair: { repairInvalidIds: true, repairMissingTerminalIds: true },
963
1784
  // DeepSeek's Responses route is `POST /responses` with no `/v1` segment. Without
964
1785
  // this the passthrough adapter falls back to its legacy `/v1/responses`
965
1786
  // construction and the wire above can never route.
966
1787
  // Evidence: https://api-docs.deepseek.com/api/create-response/
967
1788
  responsesPath: "/responses",
1789
+ // DeepSeek's Responses reference does not list `service_tier`; unsupported
1790
+ // parameters are documented as silently ignored, but the fail-closed policy
1791
+ // strips the field rather than forwarding a knob the upstream never asked for.
1792
+ supportsServiceTier: false,
1793
+ // DeepSeek's Responses compatibility guide accepts plaintext reasoning items and
1794
+ // merges them into the adjacent assistant message, so replayed reasoning must
1795
+ // not be blanked the way the ChatGPT backend requires. (Whether the Responses
1796
+ // route REQUIRES replay on tool-call continuations is an inference from the
1797
+ // Chat Thinking-Mode docs, not a confirmed Responses contract.)
1798
+ preserveResponsesReasoningContent: true,
968
1799
  // "The API is stateless: responses and conversations are not stored on the
969
1800
  // server." https://api-docs.deepseek.com/api/create-response/
970
1801
  statelessResponses: true,
1802
+ // DeepSeek rejects a valid Codex continuation when hook-provided developer
1803
+ // context splits a call from its result (#1292); parallel calls remain one
1804
+ // reasoning-bearing assistant batch rather than being split per pair (#1477).
1805
+ requiresAdjacentResponsesToolResults: true,
971
1806
  /* [Decision Log]
972
1807
  - 목적: DeepSeek V4 thinking mode multi-turn/tool-call requests must replay prior assistant reasoning_content.
973
1808
  - 대안 분석: 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
1809
  - 선택 근거: 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
1810
  */
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])),
1811
+ modelReasoningEfforts: Object.fromEntries(DEEPSEEK_THINKING_MODELS.map(id => [id, deepseekThinkingEffortsFor(id)])),
1812
+ modelReasoningEffortMap: Object.fromEntries(DEEPSEEK_THINKING_MODELS.map(id => [id, deepseekReasoningMapFor(id)])),
1813
+ modelSupportsReasoningSummaries: Object.fromEntries(DEEPSEEK_THINKING_MODELS.map(id => [id, true])),
978
1814
  preserveReasoningContentModels: DEEPSEEK_THINKING_MODELS,
979
1815
  // Issue #88: every DeepSeek API model is text-only input (no image support upstream) — the
980
1816
  // vision sidecar describes attached images for them, and the catalog advertises image input
@@ -983,6 +1819,40 @@ export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [
983
1819
  },
984
1820
  // llama-3.3-70b was deprecated by Cerebras on 2026-02-16. Evidence: devlog/_plan/260710_provider_hardening/003_research_aggregators.md.
985
1821
  { 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" },
1822
+ {
1823
+ // Primary sources checked 2026-08-08:
1824
+ // - https://chutes.ai/pricing documents the shared llm.chutes.ai/v1 OpenAI-compatible
1825
+ // gateway, Bearer API keys, and chat completions. Its public
1826
+ // https://llm.chutes.ai/v1/models response supplies supported_features for filtering.
1827
+ // - https://chutes.ai/terms identifies Chutes Global Corp as the platform operator, applies
1828
+ // to API consumers, and directs production/high-volume automated inference to PAYGO.
1829
+ // Maintainer: @olddonkey; no affiliation with Chutes.
1830
+ id: "chutes",
1831
+ label: "Chutes",
1832
+ baseUrl: "https://llm.chutes.ai/v1",
1833
+ adapter: "openai-chat",
1834
+ authKind: "key",
1835
+ dashboardUrl: "https://chutes.ai/auth/start",
1836
+ liveModels: true,
1837
+ preserveCustomDestination: true,
1838
+ // The public model catalog cannot prove that a supplied Bearer key is valid.
1839
+ apiKeyValidation: "unknown",
1840
+ // Chutes documents tool calling, but not a provider-wide parallel tool-call contract.
1841
+ parallelToolCalls: false,
1842
+ // The live catalog reports reasoning support, but not a stable effort ladder.
1843
+ reasoningEfforts: [],
1844
+ modelDiscovery: {
1845
+ path: "models",
1846
+ maxResponseBytes: 256 * 1024,
1847
+ maxModels: 128,
1848
+ filter: {
1849
+ // The shared LLM catalog also contains rows without native tool support. Codex needs a
1850
+ // complete agent loop, so admit only rows whose live metadata advertises tools.
1851
+ allOf: [{ path: ["supported_features"], containsAny: ["tools"] }],
1852
+ },
1853
+ },
1854
+ 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.",
1855
+ },
986
1856
  {
987
1857
  id: "deepinfra",
988
1858
  label: "DeepInfra",
@@ -1020,6 +1890,69 @@ export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [
1020
1890
  },
1021
1891
  note: "Serverless text and vision-language chat models only; Hyperbolic's separate image, audio, and GPU endpoints are out of scope.",
1022
1892
  },
1893
+ {
1894
+ // Primary sources checked 2026-08-03:
1895
+ // - docs.nscale.com documents the production OpenAI-compatible endpoint, bearer service
1896
+ // tokens, /v1/models, and a tool-calling request using this exact Llama model id.
1897
+ // - nscale.com/policies/terms-conditions identifies Nscale AS as the service operator and
1898
+ // covers customers using its public-cloud inference offering. Maintainer: @olddonkey;
1899
+ // no affiliation with Nscale.
1900
+ id: "nscale",
1901
+ label: "Nscale Serverless Inference",
1902
+ baseUrl: "https://inference.api.nscale.com/v1",
1903
+ adapter: "openai-chat",
1904
+ authKind: "key",
1905
+ dashboardUrl: "https://console.nscale.com",
1906
+ defaultModel: "meta-llama/Llama-3.1-8B-Instruct",
1907
+ models: ["meta-llama/Llama-3.1-8B-Instruct"],
1908
+ liveModels: true,
1909
+ preserveCustomDestination: true,
1910
+ // Nscale documents tools but not parallel tool calls. Keep requests serialized.
1911
+ parallelToolCalls: false,
1912
+ // The API schema accepts reasoning_effort, but does not publish per-model tiers.
1913
+ reasoningEfforts: [],
1914
+ modelDiscovery: {
1915
+ path: "models",
1916
+ maxResponseBytes: 256 * 1024,
1917
+ maxModels: 256,
1918
+ filter: {
1919
+ // Nscale's catalog mixes chat, image, and embedding rows without a modality field.
1920
+ // Admit only the exact model used in its official tool-calling API example.
1921
+ allOf: [{ path: ["id"], equalsAny: ["meta-llama/Llama-3.1-8B-Instruct"] }],
1922
+ },
1923
+ },
1924
+ 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.",
1925
+ },
1926
+ {
1927
+ // Primary sources checked 2026-08-03:
1928
+ // - docs.vultr.com documents the fixed OpenAI-compatible base URL, per-subscription bearer
1929
+ // key, /v1/models, and states that tool calling is currently limited to kimi-k2-instruct.
1930
+ // - Vultr's official properties identify VULTR as a The Constant Company, LLC trademark and
1931
+ // document customer API integrations. Maintainer: @olddonkey; no affiliation with Vultr.
1932
+ id: "vultr",
1933
+ label: "Vultr Serverless Inference",
1934
+ baseUrl: "https://api.vultrinference.com/v1",
1935
+ adapter: "openai-chat",
1936
+ authKind: "key",
1937
+ dashboardUrl: "https://my.vultr.com",
1938
+ defaultModel: "kimi-k2-instruct",
1939
+ models: ["kimi-k2-instruct"],
1940
+ liveModels: true,
1941
+ preserveCustomDestination: true,
1942
+ parallelToolCalls: false,
1943
+ reasoningEfforts: [],
1944
+ modelDiscovery: {
1945
+ path: "models",
1946
+ maxResponseBytes: 256 * 1024,
1947
+ maxModels: 256,
1948
+ filter: {
1949
+ // Vultr explicitly limits tool calling to this model. A coding agent must not select
1950
+ // another chat model that cannot complete its tool loop.
1951
+ allOf: [{ path: ["id"], equalsAny: ["kimi-k2-instruct"] }],
1952
+ },
1953
+ },
1954
+ note: "Serverless Inference subscription API. Live discovery exposes only kimi-k2-instruct because Vultr documents it as the sole tool-calling model.",
1955
+ },
1023
1956
  {
1024
1957
  id: "baseten",
1025
1958
  label: "Baseten Model APIs",
@@ -1045,6 +1978,229 @@ export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [
1045
1978
  },
1046
1979
  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
1980
  },
1981
+ {
1982
+ id: "commandcode",
1983
+ label: "Command Code - API",
1984
+ adapter: "openai-chat",
1985
+ baseUrl: "https://api.commandcode.ai/provider/v1",
1986
+ authKind: "key",
1987
+ dashboardUrl: "https://commandcode.ai/studio/",
1988
+ liveModels: true,
1989
+ preserveCustomDestination: true,
1990
+ defaultModel: "deepseek/deepseek-v4-flash",
1991
+ // The default is also the cold-start seed: live discovery failure must not empty the catalog
1992
+ // for a freshly configured provider with no stale cache (issue #308 pattern).
1993
+ models: ["deepseek/deepseek-v4-flash"],
1994
+ // The public model catalog is unauthenticated, so a Bearer probe cannot prove key validity.
1995
+ apiKeyValidation: "unknown",
1996
+ // The public catalog reports ids/context windows only; no trustworthy reasoning contract.
1997
+ reasoningEfforts: [],
1998
+ // Official Command Code model-profile reasoning facts (shared with the OAuth
1999
+ // `command-code` entry). Without them the API-key preset never advertises a
2000
+ // reasoning picker, and the router's known-ids decode source misses the native
2001
+ // slash ids — so a Codex-facing slug like `commandcode/deepseek-deepseek-v4-pro`
2002
+ // is sent upstream verbatim and rejected with `unsupported_model`.
2003
+ modelReasoningEfforts: COMMAND_CODE_MODEL_REASONING_EFFORTS,
2004
+ // The DeepSeek vision preview id is preemptive for when the catalog serves it
2005
+ // (merges into v4-flash later).
2006
+ modelContextWindows: {
2007
+ [`deepseek/${DEEPSEEK_VISION_PREVIEW_MODEL}`]: 1_048_576,
2008
+ },
2009
+ modelInputModalities: COMMAND_CODE_MODEL_INPUT_MODALITIES,
2010
+ modelDiscovery: {
2011
+ path: "models",
2012
+ maxResponseBytes: 256 * 1024,
2013
+ maxModels: 256,
2014
+ },
2015
+ // Verified 2026-08-03: public /provider/v1/models returns 51 rows; /chat/completions returns
2016
+ // 401 UNAUTHORIZED without a Bearer key. Primary source: https://commandcode.ai/docs/provider.
2017
+ 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.",
2018
+ },
2019
+ {
2020
+ id: "sambanova",
2021
+ label: "SambaNova Cloud",
2022
+ baseUrl: "https://api.sambanova.ai/v1",
2023
+ adapter: "openai-chat",
2024
+ authKind: "key",
2025
+ dashboardUrl: "https://cloud.sambanova.ai/apis",
2026
+ liveModels: true,
2027
+ preserveCustomDestination: true,
2028
+ apiKeyValidation: "unknown",
2029
+ // SambaNova documents this request field but does not yet support parallel function calls.
2030
+ parallelToolCalls: false,
2031
+ // The public catalog does not report a trustworthy per-model reasoning contract.
2032
+ reasoningEfforts: [],
2033
+ modelDiscovery: {
2034
+ path: "models",
2035
+ maxResponseBytes: 128 * 1024,
2036
+ maxModels: 128,
2037
+ },
2038
+ note: "SambaNova Cloud text-generation models only; private SambaStudio deployment endpoints are outside this preset.",
2039
+ },
2040
+ {
2041
+ id: "nebius",
2042
+ label: "Nebius Token Factory",
2043
+ baseUrl: "https://api.tokenfactory.nebius.com/v1",
2044
+ adapter: "openai-chat",
2045
+ authKind: "key",
2046
+ dashboardUrl: "https://tokenfactory.nebius.com",
2047
+ liveModels: true,
2048
+ preserveCustomDestination: true,
2049
+ // The public tools guide documents single function selection, not parallel tool calls.
2050
+ parallelToolCalls: false,
2051
+ // Missing reasoning metadata must not promote a model to Codex's full fallback ladder.
2052
+ reasoningEfforts: [],
2053
+ modelDiscovery: {
2054
+ path: "models",
2055
+ query: { verbose: "true" },
2056
+ maxResponseBytes: 512 * 1024,
2057
+ maxModels: 512,
2058
+ filter: {
2059
+ // Keep rows whose reported architecture output includes text (for example,
2060
+ // text->text or text+image->text); embedding and image-generation rows are excluded.
2061
+ allOf: [{ path: ["architecture", "modality"], containsAny: ["->text"] }],
2062
+ },
2063
+ },
2064
+ note: "Shared Token Factory text-output inference only; live discovery excludes embedding and image-generation rows.",
2065
+ },
2066
+ {
2067
+ id: "digitalocean",
2068
+ label: "DigitalOcean Serverless Inference",
2069
+ baseUrl: "https://inference.do-ai.run/v1",
2070
+ adapter: "openai-chat",
2071
+ authKind: "key",
2072
+ dashboardUrl: "https://cloud.digitalocean.com/model-studio/manage-keys",
2073
+ liveModels: true,
2074
+ preserveCustomDestination: true,
2075
+ // The Chat Completions contract documents function calls but not universal parallel support.
2076
+ parallelToolCalls: false,
2077
+ // Unknown catalog rows must not inherit Codex's full fallback reasoning ladder.
2078
+ reasoningEfforts: [],
2079
+ modelDiscovery: {
2080
+ path: "models",
2081
+ maxResponseBytes: 256 * 1024,
2082
+ maxModels: 256,
2083
+ filter: {
2084
+ allOf: [{ path: ["id"], equalsAny: DIGITALOCEAN_CHAT_COMPLETION_MODELS }],
2085
+ },
2086
+ },
2087
+ note: "Shared Serverless Inference Chat Completions only; agent-specific, dedicated, Responses-only, embedding, and media-generation models are outside this preset.",
2088
+ },
2089
+ {
2090
+ id: "scaleway",
2091
+ label: "Scaleway Generative APIs",
2092
+ baseUrl: "https://api.scaleway.ai/v1",
2093
+ adapter: "openai-chat",
2094
+ authKind: "key",
2095
+ dashboardUrl: "https://console.scaleway.com/generative-api",
2096
+ liveModels: true,
2097
+ freeTier: true,
2098
+ preserveCustomDestination: true,
2099
+ // Parallel support varies by model; avoid advertising it as a provider-wide capability.
2100
+ parallelToolCalls: false,
2101
+ // The generic `/models` rows carry no trustworthy reasoning metadata.
2102
+ reasoningEfforts: [],
2103
+ modelInputModalities: SCALEWAY_MODEL_INPUT_MODALITIES,
2104
+ modelDiscovery: {
2105
+ path: "models",
2106
+ maxResponseBytes: 128 * 1024,
2107
+ maxModels: 128,
2108
+ filter: {
2109
+ allOf: [{ path: ["id"], equalsAny: SCALEWAY_SERVERLESS_CHAT_MODELS }],
2110
+ },
2111
+ },
2112
+ note: "Shared Generative APIs Serverless Chat Completions only; project-qualified and dedicated deployment hosts require a custom provider.",
2113
+ },
2114
+ {
2115
+ // Primary sources checked 2026-08-08:
2116
+ // - https://featherless.ai/docs/api-overview-and-common-options documents the fixed
2117
+ // OpenAI-compatible base URL, Bearer keys, and Chat Completions.
2118
+ // - https://featherless.ai/docs/api-reference-models documents authenticated plan filtering,
2119
+ // chat capability filtering, popularity sorting, pagination, and per-row tool metadata.
2120
+ // - https://featherless.ai/legal/terms-of-service identifies Featherless as a Delaware LLC,
2121
+ // covers developers building on its APIs, and reserves arbitrary applications for Scale
2122
+ // plans. Maintainer: @olddonkey; no affiliation with Featherless.
2123
+ id: "featherless",
2124
+ label: "Featherless AI",
2125
+ baseUrl: "https://api.featherless.ai/v1",
2126
+ adapter: "openai-chat",
2127
+ authKind: "key",
2128
+ dashboardUrl: "https://featherless.ai/account/api-keys",
2129
+ liveModels: true,
2130
+ preserveCustomDestination: true,
2131
+ // /v1/models is documented as callable authenticated or unauthenticated, so a 2xx catalog
2132
+ // response cannot prove that the supplied Bearer key is valid.
2133
+ apiKeyValidation: "unknown",
2134
+ // Featherless documents tool calling, but not a provider-wide parallel tool-call contract.
2135
+ parallelToolCalls: false,
2136
+ // Reasoning controls use model-specific chat_template_kwargs, not OpenAI reasoning_effort.
2137
+ reasoningEfforts: [],
2138
+ modelDiscovery: {
2139
+ path: "models",
2140
+ query: {
2141
+ available_on_current_plan: "true",
2142
+ capabilities: "chat",
2143
+ page: "1",
2144
+ per_page: "100",
2145
+ sort: "-popularity",
2146
+ },
2147
+ maxResponseBytes: 128 * 1024,
2148
+ maxModels: 100,
2149
+ filter: {
2150
+ // Treat server-side filters as a size optimization, not an authority boundary. A row must
2151
+ // independently prove plan availability, no separate Hugging Face gate, and tool support.
2152
+ allOf: [
2153
+ { path: ["available_on_current_plan"], equalsAny: [true] },
2154
+ { path: ["is_gated"], equalsAny: [false] },
2155
+ { path: ["features", "tool_use"], equalsAny: [true] },
2156
+ ],
2157
+ },
2158
+ },
2159
+ 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.",
2160
+ },
2161
+ {
2162
+ // Primary sources checked 2026-08-08:
2163
+ // - https://novita.ai/docs/api-reference/model-apis-llm-create-chat-completion and
2164
+ // https://novita.ai/docs/api-reference/model-apis-llm-list-models document the fixed
2165
+ // OpenAI-compatible Chat Completions and model-list endpoints.
2166
+ // - https://novita.ai/docs/api-reference/basic-authentication documents Bearer API keys.
2167
+ // - https://novita.ai/legal/terms-of-service (updated 2026-08-05) expressly covers AI
2168
+ // inference APIs, third-party Model Providers, and customer Input/Output processing.
2169
+ // - https://huggingface.co/docs/inference-providers/main/providers/novita lists Novita as an
2170
+ // Inference Providers partner for chat/VLM traffic, independently supporting routing use.
2171
+ // - https://tsdr.uspto.gov/statusview/sn99255805 is the official use-in-commerce record
2172
+ // connecting the NOVITA AI mark to Hivemind Labs, Inc., a Delaware corporation. The mark
2173
+ // application is now abandoned; it is cited only as the public operator-identity record.
2174
+ // Maintainer: @olddonkey; no affiliation with Novita AI or Hivemind Labs, Inc.
2175
+ id: "novita",
2176
+ label: "Novita AI",
2177
+ baseUrl: "https://api.novita.ai/openai/v1",
2178
+ adapter: "openai-chat",
2179
+ authKind: "key",
2180
+ dashboardUrl: "https://novita.ai/settings/key-management",
2181
+ liveModels: true,
2182
+ preserveCustomDestination: true,
2183
+ // The live catalog is public even though the reference shows an Authorization header, so a
2184
+ // successful model fetch cannot prove that a supplied key is valid.
2185
+ apiKeyValidation: "unknown",
2186
+ // The request reference documents tools but not a provider-wide parallel-tool contract.
2187
+ parallelToolCalls: false,
2188
+ // Novita exposes model-specific thinking flags, not an OpenAI reasoning_effort contract.
2189
+ reasoningEfforts: [],
2190
+ modelDiscovery: {
2191
+ path: "models",
2192
+ maxResponseBytes: 512 * 1024,
2193
+ maxModels: 256,
2194
+ filter: {
2195
+ // Require both Novita's chat classification and the exact configured wire endpoint.
2196
+ allOf: [
2197
+ { path: ["model_type"], equalsAny: ["chat"] },
2198
+ { path: ["endpoints"], containsAny: ["chat/completions"] },
2199
+ ],
2200
+ },
2201
+ },
2202
+ 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.",
2203
+ },
1048
2204
  // FREEZE 2026-07-10: exact serverless ids remain auth-gated/unverified. Evidence: devlog/_plan/260710_provider_hardening/003_research_aggregators.md.
1049
2205
  { id: "together", label: "Together", baseUrl: "https://api.together.xyz/v1", adapter: "openai-chat", authKind: "key", dashboardUrl: "https://api.together.xyz/settings/api-keys" },
1050
2206
  { 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 +2210,9 @@ export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [
1054
2210
  note: "Model data frozen pending Tier-2 entitlement proof",
1055
2211
  },
1056
2212
  {
1057
- id: "moonshot", label: "Moonshot (Kimi API)", baseUrl: "https://api.moonshot.ai/v1", adapter: "openai-chat", authKind: "key",
2213
+ id: "moonshot", label: "Moonshot (Kimi API)", baseUrl: MOONSHOT_INTL_BASE_URL, adapter: "openai-chat", authKind: "key",
2214
+ allowBaseUrlOverride: true,
2215
+ baseUrlChoices: MOONSHOT_BASE_URL_CHOICES,
1058
2216
  dashboardUrl: "https://platform.moonshot.ai/console/api-keys", defaultModel: "kimi-k2.7-code", jawcodeBundle: "moonshot",
1059
2217
  models: KIMI_API_MODELS,
1060
2218
  modelContextWindows: KIMI_API_MODEL_CONTEXT_WINDOWS,
@@ -1066,6 +2224,7 @@ export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [
1066
2224
  noPenaltyModels: KIMI_API_MODELS,
1067
2225
  autoToolChoiceOnlyModels: ["kimi-k2.7-code", "kimi-k2.7-code-highspeed"],
1068
2226
  preserveReasoningContentModels: KIMI_API_MODELS,
2227
+ note: "International default (api.moonshot.ai). China accounts: choose China (.cn) or Custom for api.moonshot.cn.",
1069
2228
  },
1070
2229
  { id: "huggingface", label: "Hugging Face", baseUrl: "https://router.huggingface.co/v1", adapter: "openai-chat", authKind: "key", dashboardUrl: "https://huggingface.co/settings/tokens" },
1071
2230
  // 260715 NIM hardening (issue #126, devlog/_plan/260715_issue126_nim_kimi):
@@ -1081,6 +2240,11 @@ export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [
1081
2240
  // Free pricing, but an API key is still required (free key from build.nvidia.com).
1082
2241
  freeTier: true,
1083
2242
  parallelToolCalls: false,
2243
+ // 260804 issue #956: NIM exposes no input modalities, so vision capability is
2244
+ // classified here. Both lists are verified per-model; unlisted ids stay unclassified
2245
+ // by design (see the comment on NVIDIA_NIM_VISION_MODELS).
2246
+ noVisionModels: NVIDIA_NIM_NO_VISION_MODELS,
2247
+ modelInputModalities: NVIDIA_NIM_VISION_INPUT_MODALITIES,
1084
2248
  noReasoningModels: NVIDIA_NIM_KIMI_MODELS,
1085
2249
  modelReasoningEfforts: Object.fromEntries(NVIDIA_NIM_KIMI_MODELS.map(id => [id, []])),
1086
2250
  preserveReasoningContentModels: NVIDIA_NIM_KIMI_THINKING_MODELS,
@@ -1089,17 +2253,26 @@ export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [
1089
2253
  { id: "venice", label: "Venice", baseUrl: "https://api.venice.ai/api/v1", adapter: "openai-chat", authKind: "key", dashboardUrl: "https://venice.ai/settings/api" },
1090
2254
  // 260710 GLM-5.2 context and path-specific ids: Tier-2 evidence in
1091
2255
  // devlog/_plan/260710_provider_hardening/002_research_cn.md.
2256
+ // 260814: glm-5.3 / glm-5.3[1m] added per docs.z.ai/devpack/latest-model, which lists them as
2257
+ // Coding Plan ids on this same endpoint.
2258
+ // 260815: docs.z.ai/guides/llm/glm-5.3 now publishes the capability table (thinking, streaming,
2259
+ // function calling, caching, structured output) and a 128K output budget, recorded here as the
2260
+ // exact 131_072 every other source in this repo uses for that model. Coding Plan pricing stays
2261
+ // unpublished, so no cost entry is asserted.
1092
2262
  {
1093
2263
  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 },
2264
+ dashboardUrl: "https://z.ai/manage-apikey/apikey-list", defaultModel: "glm-5.3",
2265
+ note: "GLM-5.3 coding subscription",
2266
+ models: ["glm-5.3", "glm-5.3[1m]", "glm-5.3-flash", "glm-5.2", "glm-5.2[1m]", "glm-5.1", "glm-5", "glm-4.6"],
2267
+ modelContextWindows: { "glm-5.3": 1_000_000, "glm-5.3[1m]": 1_000_000, "glm-5.3-flash": 1_000_000, "glm-5.2": 1_000_000, "glm-5.2[1m]": 1_000_000 },
1098
2268
  // Z.AI's OpenAI path returns 400 code 1211 for bracketed model ids.
1099
2269
  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,
2270
+ noVisionModels: ZAI_GLM_5X_SIDECAR_VISION_MODELS,
2271
+ modelReasoningEfforts: ZAI_GLM_5X_REASONING_EFFORTS,
2272
+ modelDefaultReasoningEfforts: Object.fromEntries(ZAI_GLM_53_MODELS.map(id => [id, "max"])),
2273
+ modelMaxOutputTokens: Object.fromEntries(ZAI_GLM_53_MODELS.map(id => [id, 131_072])),
2274
+ modelSupportsReasoningSummaries: Object.fromEntries(ZAI_GLM_5X_MODELS.map(id => [id, true])),
2275
+ preserveReasoningContentModels: ZAI_GLM_5X_MODELS,
1103
2276
  },
1104
2277
  // Zhipu's domestic BigModel platform: OpenAI-compatible pay-as-you-go on open.bigmodel.cn — a
1105
2278
  // different host and billing product from the `zai` coding-plan subscription above.
@@ -1135,11 +2308,54 @@ export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [
1135
2308
  modelReasoningEffortMap: Object.fromEntries(
1136
2309
  ZHIPU_BIGMODEL_THINKING_TOGGLE_MODELS.map(id => [id, THINKING_TOGGLE_MAP]),
1137
2310
  ),
2311
+ modelSupportsReasoningSummaries: Object.fromEntries(
2312
+ ZHIPU_BIGMODEL_THINKING_TOGGLE_MODELS.map(id => [id, true]),
2313
+ ),
1138
2314
  preserveReasoningContentModels: ZHIPU_BIGMODEL_THINKING_TOGGLE_MODELS,
2315
+ // GLM thinking is a binary toggle (low maps to disabled), so a legitimate
2316
+ // tool round can carry no reasoning at all; never fabricate a placeholder
2317
+ // for it, only replay real recorded text (P2 on #1205).
2318
+ requiresReasoningPlaceholderModels: [],
1139
2319
  // No liveModels: GET /api/paas/v4/models has not been observed to answer on this host, and a
1140
2320
  // false live claim yields an empty picker at runtime. Flip it on once someone verifies it.
1141
2321
  note: "Domestic BigModel pay-as-you-go endpoint (open.bigmodel.cn)",
1142
2322
  },
2323
+ // BigModel's Coding Plan is a SEPARATE endpoint from the pay-as-you-go row above, and that is
2324
+ // the whole reason this one exists. #1100 was reported against
2325
+ // `https://open.bigmodel.cn/api/coding/paas/v4`; the row above covers only `/api/paas/v4`, so
2326
+ // destination enrichment matched nothing, `modelSupportsReasoningSummaries` stayed unset, and
2327
+ // Codex kept dropping the inbound reasoning object — effort displayed as `-`.
2328
+ //
2329
+ // A prefix or fuzzy endpoint match would have been the shortcut. It is also how a config
2330
+ // pointed at one vendor route silently inherits another route's metadata, so endpoints stay
2331
+ // exact and each one gets its own row.
2332
+ //
2333
+ // The id is NOT `glm-cn`, which the free-provider directory already binds to this same coding
2334
+ // path: registering it here would let routedProviderConfig() canonicalize a saved `glm-cn`
2335
+ // config onto this baseUrl. Same reasoning as `zhipu-bigmodel` above.
2336
+ //
2337
+ // Models follow Z.AI's coding-plan list rather than the pay-as-you-go one. This endpoint is
2338
+ // the subscription product, and the reporter's `glm-5.2` is only on that side.
2339
+ {
2340
+ id: "zhipu-bigmodel-coding",
2341
+ label: "Zhipu AI — BigModel Coding Plan",
2342
+ baseUrl: "https://open.bigmodel.cn/api/coding/paas/v4",
2343
+ adapter: "openai-chat",
2344
+ authKind: "key",
2345
+ dashboardUrl: "https://bigmodel.cn/console/usercenter/apikeys",
2346
+ defaultModel: "glm-5.3",
2347
+ models: ["glm-5.3", "glm-5.3[1m]", "glm-5.3-flash", "glm-5.2", "glm-5.2[1m]", "glm-5.1", "glm-5", "glm-4.6"],
2348
+ jawcodeBundle: "zai",
2349
+ modelContextWindows: { "glm-5.3": 1_000_000, "glm-5.3[1m]": 1_000_000, "glm-5.3-flash": 1_000_000, "glm-5.2": 1_000_000, "glm-5.2[1m]": 1_000_000 },
2350
+ modelSuffixBracketStrip: true,
2351
+ noVisionModels: ZAI_GLM_5X_SIDECAR_VISION_MODELS,
2352
+ modelReasoningEfforts: ZAI_GLM_5X_REASONING_EFFORTS,
2353
+ modelSupportsReasoningSummaries: Object.fromEntries(ZAI_GLM_5X_MODELS.map(id => [id, true])),
2354
+ preserveReasoningContentModels: ZAI_GLM_5X_MODELS,
2355
+ // No liveModels: the same reasoning as the pay-as-you-go row — an unverified live claim
2356
+ // yields an empty picker at runtime.
2357
+ note: "Domestic BigModel Coding Plan endpoint (open.bigmodel.cn)",
2358
+ },
1143
2359
  { id: "nanogpt", label: "NanoGPT", baseUrl: "https://nano-gpt.com/api/v1", adapter: "openai-chat", authKind: "key", dashboardUrl: "https://nano-gpt.com/api" },
1144
2360
  { id: "synthetic", label: "Synthetic", baseUrl: "https://api.synthetic.new/openai/v1", adapter: "openai-chat", authKind: "key", dashboardUrl: "https://synthetic.new" },
1145
2361
  // SiliconFlow publishes an OpenAI-compatible chat endpoint and a dynamic model catalog. Do not
@@ -1230,10 +2446,10 @@ export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [
1230
2446
  modelInputModalities: VOLCENGINE_PLAN_INPUT_MODALITIES,
1231
2447
  noVisionModels: VOLCENGINE_PLAN_TEXT_ONLY_MODELS,
1232
2448
  modelReasoningEfforts: Object.fromEntries(
1233
- DEEPSEEK_THINKING_MODELS.map(id => [id, DEEPSEEK_THINKING_EFFORTS]),
2449
+ DEEPSEEK_THINKING_MODELS.map(id => [id, deepseekThinkingEffortsFor(id)]),
1234
2450
  ),
1235
2451
  modelReasoningEffortMap: Object.fromEntries(
1236
- DEEPSEEK_THINKING_MODELS.map(id => [id, DEEPSEEK_THINKING_REASONING_MAP]),
2452
+ DEEPSEEK_THINKING_MODELS.map(id => [id, deepseekReasoningMapFor(id)]),
1237
2453
  ),
1238
2454
  preserveReasoningContentModels: DEEPSEEK_THINKING_MODELS,
1239
2455
  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 +2461,8 @@ export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [
1245
2461
  responsesPath: "/responses",
1246
2462
  adapter: "openai-responses",
1247
2463
  authKind: "key",
2464
+ // Ark's plan route does not document `service_tier`; fail closed like DeepSeek.
2465
+ supportsServiceTier: false,
1248
2466
  preserveCustomDestination: true,
1249
2467
  dashboardUrl: "https://console.volcengine.com/ark/region:ark+cn-beijing/overview",
1250
2468
  defaultModel: "deepseek-v4-pro",
@@ -1265,24 +2483,29 @@ export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [
1265
2483
  adapter: "openai-chat",
1266
2484
  authKind: "key",
1267
2485
  dashboardUrl: "https://bailian.console.aliyun.com/cn-beijing?tab=plan",
1268
- defaultModel: "qwen3.8-max-preview",
2486
+ defaultModel: "qwen3.8-max",
1269
2487
  models: ALIBABA_TOKEN_PLAN_MODELS,
1270
2488
  liveModels: false,
1271
2489
  note: "Token Plan Personal Edition · China (Beijing)",
1272
2490
  modelInputModalities: ALIBABA_TOKEN_PLAN_INPUT_MODALITIES,
1273
2491
  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,
2492
+ "qwen3.8-max": 983_616, "qwen3.7-max": 1_000_000, "qwen3.7-plus": 1_000_000,
2493
+ "qwen3.6-flash": 1_000_000, "glm-5.3": 1_000_000, "glm-5.3-flash": 1_000_000, "glm-5.2": 1_000_000, "deepseek-v4-pro": 1_000_000,
1276
2494
  },
1277
2495
  modelReasoningEfforts: {
1278
2496
  ...Object.fromEntries(ALIBABA_TOKEN_PLAN_QWEN_MODELS.map(id => [id, THINKING_BUDGET_EFFORTS])),
2497
+ "qwen3.8-max": QWEN38_REASONING_EFFORTS,
2498
+ "glm-5.3": ZAI_GLM_53_REASONING_EFFORTS,
2499
+ "glm-5.3-flash": ZAI_GLM_53_REASONING_EFFORTS,
1279
2500
  "glm-5.2": ZAI_GLM_52_REASONING_EFFORTS,
1280
- "deepseek-v4-pro": DEEPSEEK_THINKING_EFFORTS,
2501
+ "deepseek-v4-pro": deepseekThinkingEffortsFor("deepseek-v4-pro"),
1281
2502
  },
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"],
2503
+ modelDefaultReasoningEfforts: { "qwen3.8-max": "xhigh" },
2504
+ modelReasoningEffortMap: { "deepseek-v4-pro": deepseekReasoningMapFor("deepseek-v4-pro") },
2505
+ directReasoningEffortModels: ["qwen3.8-max"],
2506
+ thinkingBudgetModels: ALIBABA_TOKEN_PLAN_QWEN_MODELS.filter(id => id !== "qwen3.8-max"),
2507
+ preserveReasoningContentModels: ["glm-5.3", "glm-5.3-flash", "glm-5.2", "deepseek-v4-pro", "qwen3.8-max", "qwen3.7-max", "qwen3.7-plus", "qwen3.6-flash"],
2508
+ noVisionModels: ["glm-5.3", "glm-5.2", "deepseek-v4-pro"],
1286
2509
  },
1287
2510
  {
1288
2511
  id: "alibaba-token-plan-intl",
@@ -1300,29 +2523,32 @@ export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [
1300
2523
  metadataModelIdNormalize: "case-insensitive",
1301
2524
  modelInputModalities: ALIBABA_INTL_TOKEN_PLAN_INPUT_MODALITIES,
1302
2525
  modelContextWindows: {
1303
- "qwen3.8-max-preview": 983_616,
2526
+ "qwen3.8-max": 983_616,
1304
2527
  "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
2528
  "deepseek-v4-pro": 1_000_000, "deepseek-v4-flash": 1_000_000, "deepseek-v3.2": 131_072,
1306
2529
  "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,
2530
+ "glm-5.3": 1_000_000, "glm-5.3-flash": 1_000_000, "glm-5.2": 1_000_000, "glm-5.1": 1_000_000, "glm-5": 1_000_000,
1308
2531
  "MiniMax-M2.5": 204_800,
1309
2532
  },
1310
2533
  modelReasoningEfforts: {
1311
2534
  ...Object.fromEntries(ALIBABA_INTL_TOKEN_PLAN_QWEN_MODELS.map(id => [id, THINKING_BUDGET_EFFORTS])),
1312
- "qwen3.8-max-preview": ["low", "high", "xhigh"],
2535
+ "qwen3.8-max": QWEN38_REASONING_EFFORTS,
2536
+ "glm-5.3": ZAI_GLM_53_REASONING_EFFORTS,
2537
+ "glm-5.3-flash": ZAI_GLM_53_REASONING_EFFORTS,
1313
2538
  "glm-5.2": ZAI_GLM_52_REASONING_EFFORTS,
1314
- "deepseek-v4-pro": DEEPSEEK_THINKING_EFFORTS,
1315
- "deepseek-v4-flash": DEEPSEEK_THINKING_EFFORTS,
2539
+ "deepseek-v4-pro": deepseekThinkingEffortsFor("deepseek-v4-pro"),
2540
+ "deepseek-v4-flash": deepseekThinkingEffortsFor("deepseek-v4-flash"),
1316
2541
  },
1317
2542
  modelReasoningEffortMap: {
1318
- "deepseek-v4-pro": DEEPSEEK_THINKING_REASONING_MAP,
1319
- "deepseek-v4-flash": DEEPSEEK_THINKING_REASONING_MAP,
2543
+ "deepseek-v4-pro": deepseekReasoningMapFor("deepseek-v4-pro"),
2544
+ "deepseek-v4-flash": deepseekReasoningMapFor("deepseek-v4-flash"),
1320
2545
  },
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"],
2546
+ directReasoningEffortModels: ["qwen3.8-max"],
2547
+ thinkingBudgetModels: ALIBABA_INTL_TOKEN_PLAN_QWEN_MODELS.filter(id => id !== "qwen3.8-max"),
2548
+ preserveReasoningContentModels: ["glm-5.3", "glm-5.3-flash", "glm-5.2", "deepseek-v4-pro", "deepseek-v4-flash", "qwen3.8-max", "qwen3.7-max", "qwen3.7-plus", "qwen3.6-plus", "qwen3.6-flash"],
2549
+ 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
2550
  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" },
2551
+ modelDefaultReasoningEfforts: { "qwen3.8-max": "xhigh" },
1326
2552
  },
1327
2553
  // NEEDS_HUMAN 2026-07-10: kept for config compatibility, but this is a dashboard URL,
1328
2554
  // no /models endpoint is documented, and tools are silently ignored upstream per docs.parallel.ai.
@@ -1352,10 +2578,12 @@ export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [
1352
2578
  authKind: "key",
1353
2579
  dashboardUrl: "https://ollama.com/settings/keys",
1354
2580
  // 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",
2581
+ models: ["glm-5.3", "glm-5.3-flash", "glm-5.2", "deepseek-v4-pro", "qwen3-coder:480b", "gpt-oss:120b", "kimi-k2.6", "minimax-m3", "qwen3.5:397b", "gemma4:31b"],
2582
+ defaultModel: "glm-5.3",
1357
2583
  noVisionModels: [
1358
- "glm-5.2", "glm-5.1", "glm-5", "glm-4.7",
2584
+ // glm-5.3-flash is absent on purpose: native VLM
2585
+ // (docs.z.ai/guides/vlm/glm-5.3-flash), so its images skip the sidecar.
2586
+ "glm-5.3", "glm-5.2", "glm-5.1", "glm-5", "glm-4.7",
1359
2587
  "minimax-m2.7", "minimax-m2.5", "minimax-m2.1",
1360
2588
  "nemotron-3-ultra", "nemotron-3-super",
1361
2589
  "deepseek-v4-pro", "deepseek-v4-flash",
@@ -1372,6 +2600,10 @@ export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [
1372
2600
  modelDefaultReasoningEfforts: { "MiniMax-M3": "medium" },
1373
2601
  modelReasoningEffortMap: { "MiniMax-M3": MINIMAX_M3_REASONING_EFFORT_MAP },
1374
2602
  preserveReasoningContentModels: MINIMAX_MODELS,
2603
+ // MiniMax-M3 low effort maps to thinking disabled, so a legitimate tool
2604
+ // round can carry no reasoning at all; only replay real recorded text,
2605
+ // never a fabricated placeholder (chatgpt-codex-connector P2 on #1205).
2606
+ requiresReasoningPlaceholderModels: [],
1375
2607
  reasoningSplitModels: MINIMAX_MODELS,
1376
2608
  thinkingToggleModels: ["MiniMax-M3"],
1377
2609
  jawcodeBundle: "minimax", metadataModelIdNormalize: "case-insensitive", note: "Subscription Key or API Key",
@@ -1384,6 +2616,7 @@ export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [
1384
2616
  modelDefaultReasoningEfforts: { "MiniMax-M3": "medium" },
1385
2617
  modelReasoningEffortMap: { "MiniMax-M3": MINIMAX_M3_REASONING_EFFORT_MAP },
1386
2618
  preserveReasoningContentModels: MINIMAX_MODELS,
2619
+ requiresReasoningPlaceholderModels: [],
1387
2620
  reasoningSplitModels: MINIMAX_MODELS,
1388
2621
  thinkingToggleModels: ["MiniMax-M3"],
1389
2622
  jawcodeBundle: "minimax", metadataModelIdNormalize: "case-insensitive", note: "中国区 Subscription Key",
@@ -1407,7 +2640,31 @@ export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [
1407
2640
  autoToolChoiceOnlyModels: KIMI_AUTO_TOOL_CHOICE_ONLY_MODELS,
1408
2641
  preserveReasoningContentModels: KIMI_THINKING_MODELS,
1409
2642
  },
1410
- { id: "opencode-zen", label: "opencode zen", baseUrl: "https://opencode.ai/zen/v1", adapter: "openai-chat", authKind: "key", dashboardUrl: "https://opencode.ai/auth" },
2643
+ {
2644
+ id: "opencode-zen", label: "opencode zen", baseUrl: "https://opencode.ai/zen/v1", adapter: "openai-chat", authKind: "key", dashboardUrl: "https://opencode.ai/auth",
2645
+ // Same opencode.ai/zen/v1 gateway as `opencode-free` (keyed tier): DeepSeek thinking mode
2646
+ // requires the assistant's original reasoning_content to be replayed on tool-call
2647
+ // continuations, or the gateway answers HTTP 400 (issues #950/#994). Mirror the DeepSeek
2648
+ // reasoning + thinking metadata so `opencode-zen/deepseek-v4-flash-free` — and the other
2649
+ // Zen DeepSeek thinking models — never serialize a bare tool-call turn.
2650
+ 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.",
2651
+ modelReasoningEfforts: Object.fromEntries(
2652
+ [...DEEPSEEK_THINKING_MODELS, ...OPENCODE_FREE_DEEPSEEK_MODELS].map(id => [id, deepseekThinkingEffortsFor(id)]),
2653
+ ),
2654
+ modelReasoningEffortMap: Object.fromEntries(
2655
+ [...DEEPSEEK_THINKING_MODELS, ...OPENCODE_FREE_DEEPSEEK_MODELS].map(id => [id, deepseekReasoningMapFor(id)]),
2656
+ ),
2657
+ preserveReasoningContentModels: [...DEEPSEEK_THINKING_MODELS, ...OPENCODE_FREE_DEEPSEEK_MODELS],
2658
+ // Same Zen gateway as opencode-free: the DeepSeek vision preview id
2659
+ // (merges into deepseek-v4-flash later).
2660
+ modelContextWindows: {
2661
+ [DEEPSEEK_VISION_PREVIEW_MODEL]: 1_048_576,
2662
+ },
2663
+ modelInputModalities: {
2664
+ [DEEPSEEK_VISION_PREVIEW_MODEL]: ["text", "image"],
2665
+ },
2666
+ noVisionModels: [...OPENCODE_ZEN_TEXT_ONLY_MODELS, ...DEEPSEEK_THINKING_MODELS],
2667
+ },
1411
2668
  { 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
2669
  {
1413
2670
  id: "opencode-free",
@@ -1418,17 +2675,55 @@ export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [
1418
2675
  keyOptional: true,
1419
2676
  featured: true,
1420
2677
  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.",
2678
+ 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
2679
  dashboardUrl: "https://opencode.ai",
1423
2680
  staticHeaders: {
2681
+ // Zen answers a bare runtime User-Agent (Bun/x.y.z) more aggressively than a client
2682
+ // that identifies itself, which is what the 429 in #2067 traced to. The value is
2683
+ // deliberately unversioned: a pinned "opencode-cli/<version>" is a claim about an
2684
+ // install we do not have and goes stale on the vendor's schedule, not ours.
2685
+ // Corroboration, not authority: OmniRoute — an independent open-source broker against
2686
+ // the same Zen upstream — defaults to exactly this pair (userAgent "opencode", client
2687
+ // "desktop") in open-sse/executors/opencode.ts, and got there by RETREATING from its
2688
+ // own earlier "opencode-cli/1.0.0" pin. An operator can still override either value
2689
+ // through the provider headers API; user headers win case-insensitively at route time.
2690
+ "User-Agent": "opencode",
1424
2691
  "x-opencode-client": "desktop",
1425
2692
  },
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])),
2693
+ modelReasoningEfforts: Object.fromEntries(OPENCODE_FREE_DEEPSEEK_MODELS.map(id => [id, deepseekThinkingEffortsFor(id)])),
2694
+ modelReasoningEffortMap: Object.fromEntries(OPENCODE_FREE_DEEPSEEK_MODELS.map(id => [id, deepseekReasoningMapFor(id)])),
1428
2695
  preserveReasoningContentModels: OPENCODE_FREE_DEEPSEEK_MODELS,
1429
- noVisionModels: OPENCODE_FREE_DEEPSEEK_MODELS,
2696
+ // The DeepSeek vision preview id is preemptive metadata for when Zen starts
2697
+ // serving it (merges into v4-flash later).
2698
+ modelContextWindows: {
2699
+ [DEEPSEEK_VISION_PREVIEW_MODEL]: 1_048_576,
2700
+ },
2701
+ modelInputModalities: {
2702
+ [DEEPSEEK_VISION_PREVIEW_MODEL]: ["text", "image"],
2703
+ },
2704
+ // Same Zen roster behind the same base URL, so it carries the same measured
2705
+ // text-only list rather than only its DeepSeek member (#1043).
2706
+ noVisionModels: OPENCODE_ZEN_TEXT_ONLY_MODELS,
1430
2707
  },
1431
2708
  { id: "xiaomi", label: "Xiaomi MiMo", baseUrl: "https://api.xiaomimimo.com/anthropic", adapter: "anthropic", authKind: "key", dashboardUrl: "https://xiaomimimo.com", defaultModel: "mimo-v2.5-pro" },
2709
+ // Xiaomi's public OpenAI-compatible endpoint is a distinct transport from both the Anthropic
2710
+ // preset above and the paid token-plan host below. Keep a separate fixed-destination contract
2711
+ // so existing custom providers are never retargeted while the official route receives the
2712
+ // strict reasoning ladder its validator enforces (#1483).
2713
+ {
2714
+ id: "xiaomi-mimo",
2715
+ label: "Xiaomi MiMo (OpenAI Chat)",
2716
+ baseUrl: "https://api.xiaomimimo.com/v1",
2717
+ adapter: "openai-chat",
2718
+ authKind: "key",
2719
+ dashboardUrl: "https://platform.xiaomimimo.com/console/balance",
2720
+ defaultModel: "mimo-v2.5",
2721
+ models: ["mimo-v2.5"],
2722
+ reasoningEfforts: ["low", "medium", "high"],
2723
+ reasoningEffortMap: { xhigh: "high", max: "high", ultra: "high" },
2724
+ preserveCustomDestination: true,
2725
+ 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.",
2726
+ },
1432
2727
  { id: "kilo", label: "Kilo", baseUrl: "https://api.kilo.ai/api/gateway", adapter: "openai-chat", authKind: "key", dashboardUrl: "https://kilo.ai" },
1433
2728
  {
1434
2729
  id: "mimo-free",
@@ -1442,13 +2737,48 @@ export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [
1442
2737
  dashboardUrl: "https://xiaomimimo.com",
1443
2738
  defaultModel: "mimo-auto",
1444
2739
  models: ["mimo-auto"],
2740
+ reasoningEfforts: ["low", "medium", "high"],
2741
+ reasoningEffortMap: { xhigh: "high", max: "high", ultra: "high" },
1445
2742
  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
2743
  },
2744
+ // Xiaomi MiMo paid token plan. Separate host and wire from both `xiaomi` (Anthropic) and
2745
+ // `mimo-free` (free tier, bespoke adapter), so it needs its own entry rather than a variant.
2746
+ //
2747
+ // Pinned to openai-chat deliberately (#1158). The endpoint answers the Responses wire for
2748
+ // plain turns, which is why users configuring it by hand pick `openai-responses` — MiMo
2749
+ // documents Responses support. But its gateway rejects `type: "custom"` tools with
2750
+ // `400 responses_feature_not_supported`, and `apply_patch` is a custom tool, so every agentic
2751
+ // turn fails while chat turns succeed. The Chat path lowers custom tools to `{input: string}`
2752
+ // functions and restores them as `custom_tool_call`, so the capability survives intact.
2753
+ // Stripping the tools instead would stop the 400 and disable the agent loop.
2754
+ {
2755
+ id: "mimo",
2756
+ label: "Xiaomi MiMo (token plan)",
2757
+ baseUrl: "https://token-plan-cn.xiaomimimo.com/v1",
2758
+ adapter: "openai-chat",
2759
+ authKind: "key",
2760
+ dashboardUrl: "https://xiaomimimo.com",
2761
+ defaultModel: "mimo-v2.5-pro",
2762
+ models: ["mimo-v2.5-pro", "mimo-v2.5"],
2763
+ // The gateway validates the ladder strictly and rejects anything above `high`.
2764
+ reasoningEfforts: ["low", "medium", "high"],
2765
+ reasoningEffortMap: { xhigh: "high", max: "high", ultra: "high" },
2766
+ // Live token-plan verification (#1927): the Pro route rejects image input while
2767
+ // mimo-v2.5 accepts it natively. Keep this provider-scoped so a hand-rolled
2768
+ // provider with the same id but another destination does not inherit the claim.
2769
+ noVisionModels: ["mimo-v2.5-pro"],
2770
+ // A user may already have hand-rolled a provider under this id against a different host;
2771
+ // without this, routedProviderConfig() would canonicalize their base URL onto ours and send
2772
+ // their key somewhere they did not choose.
2773
+ preserveCustomDestination: true,
2774
+ 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.",
2775
+ },
1447
2776
  { 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
2777
  {
1449
2778
  // Cloudflare Workers AI: OpenAI-compatible endpoint. The base URL contains {account_id}
1450
2779
  // which must be resolved by the user at setup time. Model IDs use the @cf/ prefix.
1451
2780
  // Live-verified 2026-07-21 against https://developers.cloudflare.com/workers-ai/models/
2781
+ // Official search is sibling to /ai/v1 (GET .../ai/models/search?format=openrouter).
1452
2782
  id: "cloudflare-workers-ai", label: "Cloudflare Workers AI",
1453
2783
  baseUrl: "https://api.cloudflare.com/client/v4/accounts/{account_id}/ai/v1",
1454
2784
  adapter: "openai-chat", authKind: "key", freeTier: true,
@@ -1459,9 +2789,18 @@ export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [
1459
2789
  "@cf/qwen/qwq-32b",
1460
2790
  "@cf/deepseek-ai/deepseek-r1-distill-qwen-32b",
1461
2791
  "@cf/moonshotai/kimi-k2.7-code",
2792
+ "@cf/zai-org/glm-5.3",
2793
+ "@cf/zai-org/glm-5.3-flash",
1462
2794
  "@cf/zai-org/glm-5.2",
1463
2795
  "@cf/mistralai/mistral-small-3.1-24b-instruct",
1464
2796
  ],
2797
+ liveModels: true,
2798
+ modelDiscovery: {
2799
+ path: "../models/search",
2800
+ query: { format: "openrouter", per_page: "1000" },
2801
+ stripIdPrefix: "workers-ai/",
2802
+ maxModels: 256,
2803
+ },
1465
2804
  note: "Workers AI · Free tier included · Account ID required in base URL",
1466
2805
  },
1467
2806
  // FREEZE 2026-07-10: /models was auth-gated under key login. OAuth device-flow + copilot_internal
@@ -1476,18 +2815,83 @@ export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [
1476
2815
  featured: false,
1477
2816
  dashboardUrl: "https://github.com/settings/copilot",
1478
2817
  liveModels: true,
1479
- models: ["gpt-4o", "gpt-4.1", "gpt-4.1-mini", "claude-sonnet-4", "gemini-2.5-pro"],
2818
+ 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
2819
  defaultModel: "gpt-4o",
2820
+ // Copilot fronts a mixed-wire catalog: these models reject /chat/completions for
2821
+ // real Codex-agent traffic (function tools + reasoning), so every inbound wire
2822
+ // rides Responses. Evidence: issue #748 field runs, pi.dev/models/github-copilot/*
2823
+ // wire declarations, BerriAI/litellm#23332 (gpt-5.4), JetBrains LLM-29711
2824
+ // (gpt-5.6-sol). gpt-5.4-nano is deliberately absent — it has no field report; a
2825
+ // user can opt it in with an explicit modelAdapters entry, which always wins.
2826
+ modelWireDefaults: {
2827
+ "gpt-5.3-codex": "openai-responses",
2828
+ "gpt-5.4": "openai-responses",
2829
+ "gpt-5.4-mini": "openai-responses",
2830
+ "gpt-5.5": "openai-responses",
2831
+ "gpt-5.6-luna": "openai-responses",
2832
+ "gpt-5.6-sol": "openai-responses",
2833
+ "gpt-5.6-terra": "openai-responses",
2834
+ },
1481
2835
  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
2836
  },
1483
2837
  // FREEZE 2026-07-10: no public OpenAI-compatible endpoint is documented. Evidence: devlog/_plan/260710_provider_hardening/003_research_aggregators.md.
1484
2838
  { 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
2839
  ];
1486
2840
 
2841
+ export function providerRegistryFastWireError(
2842
+ entry: Pick<ProviderRegistryEntry, "fastWire" | "supportsServiceTier" | "modelSupportsServiceTier">,
2843
+ ): string | null {
2844
+ return fastWireDeclarationError(entry);
2845
+ }
2846
+
2847
+ for (const entry of PROVIDER_REGISTRY) {
2848
+ const error = providerRegistryFastWireError(entry);
2849
+ if (error) throw new TypeError(`Invalid provider registry entry ${entry.id}: ${error}`);
2850
+ }
2851
+
1487
2852
  export function getProviderRegistryEntry(id: string): ProviderRegistryEntry | undefined {
1488
2853
  return PROVIDER_REGISTRY.find(entry => entry.id === id);
1489
2854
  }
1490
2855
 
2856
+ /**
2857
+ * Merge a registry row's `staticHeaders` beneath a provider's own headers.
2858
+ *
2859
+ * The field is documented as "merged into every upstream request for this provider", but that
2860
+ * was only ever true for a freshly seeded config: `providerConfigSeed` copies the block once
2861
+ * (`derive.ts`), `enrichProviderFromCatalog` fills it only when the whole block is absent, and
2862
+ * nothing merged it at request time. So an install that predates a header — or that saved any
2863
+ * header of its own — never received the new one, which is exactly what #2067 would have
2864
+ * shipped for every existing opencode-free user.
2865
+ *
2866
+ * The comparison is case-insensitive on purpose. HTTP header names are case-insensitive, but a
2867
+ * plain object spread is not: merging a registry `User-Agent` over a user's `user-agent`
2868
+ * produces two entries that `Headers` serializes as one comma-joined value
2869
+ * ("opencode, custom-agent"), which is a corrupted request rather than an override. The user's
2870
+ * spelling and value both win; the registry only fills names the user has not spoken for.
2871
+ */
2872
+ export function mergeRegistryStaticHeaders(
2873
+ staticHeaders: Record<string, string> | undefined,
2874
+ userHeaders: Record<string, string> | undefined,
2875
+ ): Record<string, string> | undefined {
2876
+ if (!staticHeaders) return userHeaders;
2877
+ if (!userHeaders) return { ...staticHeaders };
2878
+ const claimed = new Set(Object.keys(userHeaders).map(name => name.toLowerCase()));
2879
+ const merged: Record<string, string> = { ...userHeaders };
2880
+ for (const [name, value] of Object.entries(staticHeaders)) {
2881
+ if (!claimed.has(name.toLowerCase())) merged[name] = value;
2882
+ }
2883
+ return merged;
2884
+ }
2885
+
2886
+ /** Whether this registry row's per-model service-tier evidence applies to one configured target. */
2887
+ export function registryModelServiceTierCapabilityApplies(
2888
+ entry: Pick<ProviderRegistryEntry, "modelServiceTierCapabilityBaseUrlGuard">,
2889
+ provider: Pick<OcxProviderConfig, "baseUrl">,
2890
+ ): boolean {
2891
+ const guard = entry.modelServiceTierCapabilityBaseUrlGuard;
2892
+ return guard === undefined || guard(provider.baseUrl);
2893
+ }
2894
+
1491
2895
  function normalizedProviderEndpoint(value: string): string {
1492
2896
  const trimmed = value.trim();
1493
2897
  try {
@@ -1567,12 +2971,41 @@ export function providerModelWireDefault(
1567
2971
  if (!entry?.modelWireDefaults || !providerMatchesRegistryTransport(id, provider)) return undefined;
1568
2972
  const declared = entry.modelWireDefaults[modelId.trim().toLowerCase()];
1569
2973
  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;
2974
+ // A bare string applies to every inbound/auth mode; the object form may narrow either.
2975
+ if (typeof declared !== "string") {
2976
+ if (!declared.inbound.includes(inbound)) return undefined;
2977
+ const authMode = provider.authMode ?? entry.authKind;
2978
+ if (declared.authModes && !declared.authModes.includes(authMode)) return undefined;
2979
+ }
1572
2980
  const wire = typeof declared === "string" ? declared : declared.wire;
1573
2981
  return wire !== undefined && allowedWires.has(wire) ? wire : undefined;
1574
2982
  }
1575
2983
 
2984
+ /** Resolve a registry-only upstream-streaming compatibility hint for Responses turns. */
2985
+ export function providerModelResponsesUpstreamStreaming(
2986
+ id: string,
2987
+ provider: Pick<OcxProviderConfig, "baseUrl" | "adapter"> & Partial<Pick<OcxProviderConfig, "authMode">>,
2988
+ modelId: string,
2989
+ ): boolean | undefined {
2990
+ const entry = getProviderRegistryEntry(id);
2991
+ if (!entry?.modelResponsesUpstreamStreaming || !providerMatchesRegistryTransport(id, provider)) return undefined;
2992
+ return entry.modelResponsesUpstreamStreaming[modelId.trim().toLowerCase()];
2993
+ }
2994
+
2995
+ /** Resolve a registry-only terminal-repair policy for native Responses streams. */
2996
+ export function providerModelResponsesTerminalRepair(
2997
+ id: string,
2998
+ provider: Pick<OcxProviderConfig, "baseUrl" | "adapter"> & Partial<Pick<OcxProviderConfig, "authMode">>,
2999
+ modelId: string,
3000
+ ): ResponsesTerminalRepairPolicy | undefined {
3001
+ const entry = getProviderRegistryEntry(id);
3002
+ if (!entry?.modelResponsesTerminalRepair || !providerMatchesRegistryTransport(id, provider)) return undefined;
3003
+ const policy = entry.modelResponsesTerminalRepair[modelId.trim().toLowerCase()];
3004
+ const graceMs = Math.floor(policy?.graceMs ?? 0);
3005
+ if (!Number.isFinite(graceMs) || graceMs <= 0) return undefined;
3006
+ return { graceMs };
3007
+ }
3008
+
1576
3009
  /**
1577
3010
  * Effective Codex account mode for a provider. For canonical `openai`, a valid persisted
1578
3011
  * `codexAccountMode` on the provider config wins and a missing/invalid value defaults to