@bitkyc08/opencodex 2.59.0 → 2.61.0-preview.20260922

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 (374) hide show
  1. package/AGENTS_INSTALL.md +64 -0
  2. package/README.md +28 -1
  3. package/bin/ocx.mjs +382 -209
  4. package/gui/dist/assets/App-E64Rzjap.js +50 -0
  5. package/gui/dist/assets/Tray-_nfzD8k4.js +1 -0
  6. package/gui/dist/assets/index-DpdfZWMK.js +86 -0
  7. package/gui/dist/assets/index-_bpvxJu0.css +1 -0
  8. package/gui/dist/assets/usage-companion-chart-DtoK7T6h.js +1 -0
  9. package/gui/dist/favicon.png +0 -0
  10. package/gui/dist/index.html +2 -2
  11. package/gui/dist/provider-icons/stepfun-color.svg +1 -0
  12. package/package.json +8 -1
  13. package/src/adapters/anthropic-image-codec.ts +16 -2
  14. package/src/adapters/anthropic-image-normalize.ts +49 -2
  15. package/src/adapters/anthropic.ts +20 -1
  16. package/src/adapters/base.ts +23 -0
  17. package/src/adapters/coding-agent/protocol.ts +36 -6
  18. package/src/adapters/coding-agent/turn.ts +32 -4
  19. package/src/adapters/command-code.ts +52 -4
  20. package/src/adapters/cursor/catalog.ts +51 -7
  21. package/src/adapters/cursor/checkpoint-store.ts +3 -0
  22. package/src/adapters/cursor/discovery.ts +11 -8
  23. package/src/adapters/cursor/live-transport.ts +26 -9
  24. package/src/adapters/cursor/protobuf-request.ts +6 -3
  25. package/src/adapters/cursor/request-builder.ts +20 -4
  26. package/src/adapters/cursor/transport.ts +19 -0
  27. package/src/adapters/cursor.ts +14 -3
  28. package/src/adapters/declaration-carrier.ts +45 -0
  29. package/src/adapters/devin/cloud-direct/chat.ts +3 -1
  30. package/src/adapters/devin/cloud-direct/stated-reset-retry.ts +42 -5
  31. package/src/adapters/devin.ts +125 -36
  32. package/src/adapters/google-antigravity-replay.ts +1 -1
  33. package/src/adapters/google-antigravity-wire.ts +34 -7
  34. package/src/adapters/google-errors.ts +7 -1
  35. package/src/adapters/google-http.ts +49 -10
  36. package/src/adapters/google-tool-schema.ts +595 -31
  37. package/src/adapters/google-wire-compiler.ts +93 -10
  38. package/src/adapters/google-wire-shape.ts +461 -0
  39. package/src/adapters/google.ts +66 -11
  40. package/src/adapters/image.ts +4 -1
  41. package/src/adapters/input-media-guard.ts +21 -9
  42. package/src/adapters/kiro/usage.ts +3 -2
  43. package/src/adapters/kiro-tool-fallback.ts +1 -1
  44. package/src/adapters/ollama-native.ts +6 -0
  45. package/src/adapters/openai-chat/developer-role.ts +61 -0
  46. package/src/adapters/openai-chat/messages.ts +46 -27
  47. package/src/adapters/openai-chat/parallel-tool-calls.ts +32 -0
  48. package/src/adapters/openai-chat/passthrough.ts +33 -9
  49. package/src/adapters/openai-chat/reasoning-wire.ts +89 -0
  50. package/src/adapters/openai-chat-images.ts +3 -1
  51. package/src/adapters/openai-chat.ts +23 -58
  52. package/src/adapters/openai-responses/image-gen.ts +8 -6
  53. package/src/adapters/openai-responses/passthrough.ts +17 -3
  54. package/src/adapters/openai-responses/reasoning.ts +7 -0
  55. package/src/adapters/opencode-go-additional-tools.ts +12 -2
  56. package/src/adapters/registry.ts +3 -2
  57. package/src/adapters/run-turn-queue.ts +178 -29
  58. package/src/adapters/xai-web-search.ts +16 -1
  59. package/src/bridge/errors.ts +8 -2
  60. package/src/bridge/response-json.ts +9 -1
  61. package/src/bridge/sse.ts +13 -151
  62. package/src/chat/inbound.ts +141 -5
  63. package/src/claude/desktop-3p.ts +7 -1
  64. package/src/claude/desktop-first-party.ts +183 -0
  65. package/src/claude/desktop-gateway-state.ts +41 -0
  66. package/src/claude/inbound-content-options.ts +6 -0
  67. package/src/claude/inbound.ts +32 -6
  68. package/src/claude/intercept/connect-proxy.ts +179 -0
  69. package/src/claude/intercept/listener.ts +122 -0
  70. package/src/claude/intercept/local-ca.ts +298 -0
  71. package/src/claude/intercept/runtime.ts +98 -0
  72. package/src/claude/intercept/settings.ts +189 -0
  73. package/src/cli/access.ts +87 -0
  74. package/src/cli/account-auth.ts +19 -0
  75. package/src/cli/account-extended.ts +4 -4
  76. package/src/cli/capabilities.ts +31 -0
  77. package/src/cli/claude-desktop.ts +206 -16
  78. package/src/cli/codex-shim-autorestore.ts +3 -0
  79. package/src/cli/companion.ts +56 -0
  80. package/src/cli/dispatch.ts +46 -7
  81. package/src/cli/doctor.ts +28 -9
  82. package/src/cli/ensure-desired-integrations.ts +43 -5
  83. package/src/cli/help.ts +7 -9
  84. package/src/cli/hub.ts +3 -2
  85. package/src/cli/index.ts +203 -59
  86. package/src/cli/init.ts +8 -0
  87. package/src/cli/integrations.ts +7 -1
  88. package/src/cli/opencode.ts +2 -2
  89. package/src/cli/provider.ts +13 -1
  90. package/src/cli/registry.ts +41 -2
  91. package/src/cli/resolve.ts +230 -0
  92. package/src/cli/root.ts +24 -1
  93. package/src/cli/start-ownership-publication.ts +56 -0
  94. package/src/cli/status-probes.ts +2 -18
  95. package/src/cli/status.ts +62 -0
  96. package/src/cli/stop-report.ts +143 -0
  97. package/src/cli/uninstall-plan.ts +9 -0
  98. package/src/client/machine-api.ts +2 -2
  99. package/src/client/machine-listener.ts +4 -7
  100. package/src/client/runtime.ts +26 -2
  101. package/src/clients/aside-profiles.ts +4 -0
  102. package/src/clients/config-export/zcode-store.ts +157 -0
  103. package/src/clients/config-export.ts +36 -0
  104. package/src/codex/account-store.ts +65 -0
  105. package/src/codex/app-server-processes.ts +72 -40
  106. package/src/codex/auth-api/account-list.ts +19 -11
  107. package/src/codex/auth-api/login-flow.ts +6 -1
  108. package/src/codex/auth-api/pool-quota-probe.ts +30 -7
  109. package/src/codex/autostart-health.ts +28 -0
  110. package/src/codex/catalog/build-entries.ts +2 -2
  111. package/src/codex/catalog/effort.ts +3 -3
  112. package/src/codex/catalog/gather-capture.ts +21 -2
  113. package/src/codex/catalog/model-hints.ts +29 -28
  114. package/src/codex/catalog/parsing.ts +7 -0
  115. package/src/codex/catalog/provider-models.ts +43 -17
  116. package/src/codex/catalog/retained-sync.ts +24 -28
  117. package/src/codex/catalog/routed-gather.ts +19 -0
  118. package/src/codex/context-compat.ts +5 -2
  119. package/src/codex/convergence.ts +2 -2
  120. package/src/codex/desired-state.ts +4 -1
  121. package/src/codex/history-job.ts +6 -6
  122. package/src/codex/history-provider.ts +31 -166
  123. package/src/codex/history-rollout-read.ts +174 -0
  124. package/src/codex/inject/config-toml.ts +41 -6
  125. package/src/codex/inject/paginated-openai-compat.ts +90 -0
  126. package/src/codex/inject.ts +18 -15
  127. package/src/codex/injected-marker.ts +18 -0
  128. package/src/codex/internal/catalog-writer.ts +33 -1
  129. package/src/codex/main-account.ts +6 -0
  130. package/src/codex/model-cache.ts +99 -6
  131. package/src/codex/model-entitlement-admission.ts +59 -0
  132. package/src/codex/model-entitlements.ts +116 -54
  133. package/src/codex/native-main-admission.ts +83 -0
  134. package/src/codex/observed-model-denials.ts +101 -8
  135. package/src/codex/prompt-text-probe.ts +9 -6
  136. package/src/codex/routing/health-store.ts +39 -0
  137. package/src/codex/routing/selection.ts +37 -1
  138. package/src/codex/routing.ts +12 -42
  139. package/src/codex/shim-templates.ts +29 -3
  140. package/src/codex/shim.ts +1 -1
  141. package/src/codex/subagent-model-fallback.ts +22 -4
  142. package/src/combos/failover.ts +3 -0
  143. package/src/companion/settings.ts +132 -0
  144. package/src/config/admitted-identity.ts +222 -0
  145. package/src/config/atomic-write.ts +117 -5
  146. package/src/config/diagnostics.ts +22 -1
  147. package/src/config/feature-flags.ts +5 -0
  148. package/src/config/load-degrade.ts +52 -7
  149. package/src/config/process-state.ts +1 -1
  150. package/src/config/proxy-env.ts +8 -2
  151. package/src/config/schema/compaction-triggers.ts +11 -0
  152. package/src/config/schema/config-schema.ts +31 -1
  153. package/src/config/schema/leaf-validators.ts +59 -0
  154. package/src/config.ts +3 -3
  155. package/src/generated/compatibility-version.json +604 -280
  156. package/src/grok/reset-coupons.ts +38 -19
  157. package/src/images/loop.ts +6 -1
  158. package/src/integrations/aside-profile-context.ts +37 -3
  159. package/src/integrations/aside-profile-journal.ts +68 -3
  160. package/src/integrations/aside-profiles.ts +128 -3
  161. package/src/integrations/config-io.ts +44 -10
  162. package/src/integrations/merge.ts +120 -13
  163. package/src/integrations/mutation-plan.ts +921 -0
  164. package/src/integrations/registry.ts +38 -0
  165. package/src/integrations/state.ts +78 -45
  166. package/src/integrations/target.ts +208 -0
  167. package/src/integrations/writer.ts +134 -110
  168. package/src/lab/conformance/fixture-provider.ts +5 -0
  169. package/src/lab/live/transport.ts +4 -0
  170. package/src/lab/live/types.ts +5 -0
  171. package/src/lab/subject/behavior-fingerprint.ts +1 -1
  172. package/src/lib/admin-secrets.ts +9 -1
  173. package/src/lib/browser-launch-notice.ts +59 -0
  174. package/src/lib/bun-runtime.ts +6 -2
  175. package/src/lib/debug-log-buffer.ts +6 -1
  176. package/src/lib/debug.ts +63 -0
  177. package/src/lib/errors.ts +79 -0
  178. package/src/lib/http-response-semantics.ts +57 -0
  179. package/src/lib/lab-live-pinned-sender.ts +26 -12
  180. package/src/lib/open-url.ts +51 -7
  181. package/src/lib/package-tree-integrity.ts +2 -1
  182. package/src/lib/package-version.ts +8 -0
  183. package/src/lib/pinned-http.ts +142 -2
  184. package/src/lib/plain-data.ts +103 -0
  185. package/src/lib/process-control.ts +13 -5
  186. package/src/lib/provider-egress.ts +310 -0
  187. package/src/lib/provider-outbound.ts +109 -16
  188. package/src/lib/proxy-env.ts +82 -7
  189. package/src/lib/request-execution-budget.ts +72 -0
  190. package/src/lib/request-failure-attribution.ts +183 -0
  191. package/src/lib/request-failure-model.ts +236 -0
  192. package/src/lib/request-resend-gate.ts +138 -0
  193. package/src/lib/socks5-fetch.ts +136 -26
  194. package/src/lib/spend-ledger-owner.ts +364 -0
  195. package/src/lib/spend-reservation-ledger.ts +218 -27
  196. package/src/lib/standalone.ts +16 -0
  197. package/src/lib/upstream-retry.ts +167 -16
  198. package/src/lib/windows-system-proxy.ts +16 -11
  199. package/src/lib/winsw.ts +2 -2
  200. package/src/oauth/callback-server.ts +4 -3
  201. package/src/oauth/generic-account-failover.ts +1 -0
  202. package/src/oauth/health.ts +12 -1
  203. package/src/oauth/index.ts +27 -108
  204. package/src/oauth/login-cli.ts +80 -29
  205. package/src/oauth/login-flow-state.ts +127 -0
  206. package/src/providers/api-key-resolve.ts +133 -0
  207. package/src/providers/api-key-selection.ts +5 -1
  208. package/src/providers/derive.ts +34 -17
  209. package/src/providers/devin-cli-authmode-migration.ts +14 -10
  210. package/src/providers/key-failover.ts +97 -19
  211. package/src/providers/key-store.ts +34 -110
  212. package/src/providers/model-rename-fields.ts +147 -0
  213. package/src/providers/model-rename-migration.ts +179 -38
  214. package/src/providers/model-rename-startup.ts +7 -5
  215. package/src/providers/openai-virtual-models.ts +42 -2
  216. package/src/providers/quota/antigravity.ts +22 -2
  217. package/src/providers/quota/vendor-probes-key.ts +38 -23
  218. package/src/providers/reasoning-metadata.ts +43 -18
  219. package/src/providers/registry/entries-core.ts +47 -20
  220. package/src/providers/registry/entries-extended.ts +63 -4
  221. package/src/providers/registry/model-ids.ts +168 -0
  222. package/src/providers/registry/model-seeds.ts +56 -10
  223. package/src/providers/registry/types.ts +2 -0
  224. package/src/providers/resolved-model-policy-merge.ts +167 -0
  225. package/src/providers/resolved-model-policy.ts +406 -0
  226. package/src/providers/stale-vision-classification-migration.ts +137 -0
  227. package/src/providers/xai-transport.ts +12 -1
  228. package/src/reasoning-effort.ts +8 -0
  229. package/src/responses/apply-patch-envelope.ts +0 -12
  230. package/src/responses/freeform-wrapper-scan.ts +279 -0
  231. package/src/responses/function-call-compat.ts +38 -1
  232. package/src/responses/inline-document.ts +65 -0
  233. package/src/responses/input-media.ts +42 -8
  234. package/src/responses/legacy-dotted-tool-name-repair.ts +134 -0
  235. package/src/responses/muse-tool-name-alias.ts +19 -0
  236. package/src/responses/parser-content.ts +8 -2
  237. package/src/responses/parser-tools.ts +3 -0
  238. package/src/responses/parser.ts +3 -1
  239. package/src/responses/progressive-freeform-input.ts +130 -0
  240. package/src/responses/reasoning-envelope.ts +30 -0
  241. package/src/responses/schema.ts +3 -0
  242. package/src/responses/state.ts +5 -12
  243. package/src/responses/tool-name-aliases.ts +15 -1
  244. package/src/router.ts +108 -117
  245. package/src/routing/compatibility/behavior.ts +9 -0
  246. package/src/routing/compatibility/subject.ts +16 -1
  247. package/src/server/adapter-resolve.ts +9 -0
  248. package/src/server/admission-model-scope.ts +219 -0
  249. package/src/server/audio-live.ts +9 -3
  250. package/src/server/audio-upstream.ts +18 -0
  251. package/src/server/auth-cors.ts +29 -0
  252. package/src/server/chat-completions.ts +60 -4
  253. package/src/server/chat-native.ts +19 -4
  254. package/src/server/claude-messages.ts +61 -20
  255. package/src/server/effort-row.ts +11 -3
  256. package/src/server/grok-responses-control-frame.ts +160 -1
  257. package/src/server/grok-responses-snapshot-repair.ts +113 -11
  258. package/src/server/gui-freshness.ts +103 -0
  259. package/src/server/gui-static.ts +7 -9
  260. package/src/server/images.ts +59 -6
  261. package/src/server/index/claude-intercept-lifecycle.ts +49 -0
  262. package/src/server/index/serve-options.ts +142 -39
  263. package/src/server/index/spend-ledger-lifecycle.ts +92 -0
  264. package/src/server/index/startup-warnings.ts +24 -0
  265. package/src/server/index/websocket-handler.ts +6 -1
  266. package/src/server/index.ts +34 -38
  267. package/src/server/lifecycle.ts +4 -4
  268. package/src/server/live-call-bindings.ts +6 -0
  269. package/src/server/live.ts +88 -3
  270. package/src/server/management/agent-settings-routes.ts +121 -36
  271. package/src/server/management/aside-profile-routes.ts +266 -7
  272. package/src/server/management/companion-routes.ts +77 -0
  273. package/src/server/management/config-routes.ts +18 -2
  274. package/src/server/management/context.ts +3 -0
  275. package/src/server/management/integration-routes.ts +287 -5
  276. package/src/server/management/logs-usage-routes.ts +19 -0
  277. package/src/server/management/metrics-routes.ts +20 -0
  278. package/src/server/management/model-rows.ts +224 -12
  279. package/src/server/management/native-integration-routes.ts +103 -6
  280. package/src/server/management/oauth-account-routes.ts +45 -7
  281. package/src/server/management/route-registry.ts +20 -0
  282. package/src/server/management/shared.ts +28 -4
  283. package/src/server/management/system-restart.ts +7 -2
  284. package/src/server/management/system-routes.ts +2 -0
  285. package/src/server/management/usage-aggregate-cache.ts +4 -0
  286. package/src/server/management/usage-timeline-routes.ts +44 -0
  287. package/src/server/management-api.ts +10 -9
  288. package/src/server/management-auth.ts +15 -1
  289. package/src/server/proxy-liveness.ts +75 -0
  290. package/src/server/readiness.ts +29 -10
  291. package/src/server/relay-eager.ts +24 -2
  292. package/src/server/relay.ts +138 -12
  293. package/src/server/request-log-failure-attribution.ts +99 -0
  294. package/src/server/request-log.ts +169 -2
  295. package/src/server/request-metrics.ts +298 -0
  296. package/src/server/responses/adapter-continuation.ts +3 -3
  297. package/src/server/responses/adapter-dispatch.ts +11 -6
  298. package/src/server/responses/codex-ws-wire.ts +34 -8
  299. package/src/server/responses/combo-stream-preflight.ts +168 -6
  300. package/src/server/responses/compact.ts +43 -10
  301. package/src/server/responses/compaction-routing.ts +111 -0
  302. package/src/server/responses/core-codex-account.ts +8 -3
  303. package/src/server/responses/core-combo.ts +7 -7
  304. package/src/server/responses/core-normalize.ts +6 -12
  305. package/src/server/responses/core-opaque-recovery.ts +91 -0
  306. package/src/server/responses/core-options.ts +4 -0
  307. package/src/server/responses/encrypted-payload.ts +20 -2
  308. package/src/server/responses/fetch-helpers.ts +124 -8
  309. package/src/server/responses/input-admission.ts +10 -0
  310. package/src/server/responses/passthrough-delivery.ts +45 -15
  311. package/src/server/responses/passthrough-dispatch.ts +215 -44
  312. package/src/server/responses/passthrough-error.ts +27 -8
  313. package/src/server/responses/policy-fallback.ts +5 -13
  314. package/src/server/responses/request-prepare.ts +109 -18
  315. package/src/server/responses/request-send-budget.ts +17 -1
  316. package/src/server/responses/request-sidecar-auth.ts +1 -1
  317. package/src/server/responses/request-transport.ts +26 -6
  318. package/src/server/responses/reset-replay.ts +108 -0
  319. package/src/server/responses/run-turn-execution.ts +25 -3
  320. package/src/server/responses/sidecar-execution.ts +17 -2
  321. package/src/server/responses/ws-upstream.ts +14 -27
  322. package/src/server/responses-custom-tool-repair.ts +27 -54
  323. package/src/server/responses-request-tool-scope.ts +214 -0
  324. package/src/server/responses-undeclared-tool-guard.ts +35 -2
  325. package/src/server/search.ts +25 -1
  326. package/src/server/sse-payload-rewrite.ts +1 -1
  327. package/src/server/usage-ledger-retention.ts +73 -0
  328. package/src/service/cli.ts +48 -2
  329. package/src/service/health.ts +3 -2
  330. package/src/service/install-state-contract.d.mts +27 -0
  331. package/src/service/install-state-contract.mjs +34 -0
  332. package/src/service/launchd.ts +1 -1
  333. package/src/service/orchestration.ts +2 -4
  334. package/src/service/ownership-compatibility.ts +164 -0
  335. package/src/service/ownership-mutation-lease.d.mts +32 -0
  336. package/src/service/ownership-mutation-lease.mjs +211 -0
  337. package/src/service/repair.ts +45 -1
  338. package/src/service/state-lock.ts +269 -0
  339. package/src/service/state-record.d.mts +36 -0
  340. package/src/service/state-record.mjs +138 -0
  341. package/src/service/state.ts +582 -68
  342. package/src/service/windows-taskxml.ts +11 -10
  343. package/src/service.ts +7 -3
  344. package/src/tray/windows-tray.ps1 +156 -4
  345. package/src/types/config.ts +48 -3
  346. package/src/types/provider.ts +91 -0
  347. package/src/types/request.ts +30 -2
  348. package/src/types/tools.ts +33 -0
  349. package/src/types.ts +4 -0
  350. package/src/update/index.ts +207 -63
  351. package/src/update/job.ts +9 -5
  352. package/src/update/ownership-transaction.ts +47 -0
  353. package/src/update/restart-ownership.ts +54 -0
  354. package/src/update/runtime-ownership.d.mts +40 -0
  355. package/src/update/runtime-ownership.mjs +122 -0
  356. package/src/usage/attempt-delivery.ts +198 -0
  357. package/src/usage/cache-diagnostic.ts +305 -0
  358. package/src/usage/failure-fingerprint.ts +118 -0
  359. package/src/usage/failure-projection-cache.ts +174 -0
  360. package/src/usage/failure-projection.ts +174 -0
  361. package/src/usage/ledger-retention.ts +165 -0
  362. package/src/usage/log.ts +126 -79
  363. package/src/usage/request-outcome.ts +150 -0
  364. package/src/usage/retention-contract.ts +28 -0
  365. package/src/usage/summary.ts +2 -2
  366. package/src/usage/telemetry-contract.ts +237 -0
  367. package/src/usage/timeline.ts +236 -0
  368. package/src/vision/eligibility.ts +88 -9
  369. package/src/vision/plan.ts +34 -10
  370. package/src/web-search/alpha-search.ts +21 -1
  371. package/src/web-search/executor.ts +41 -2
  372. package/src/web-search/loop.ts +6 -1
  373. package/gui/dist/assets/index-C5IebErG.js +0 -136
  374. package/gui/dist/assets/index-OESInAjC.css +0 -1
@@ -18,6 +18,7 @@
18
18
  * entitlement gap (muse-spark max needs an active Muse Code subscription) costs one rejected
19
19
  * request instead of failing every turn that selects that rung.
20
20
  */
21
+ import { createHash } from "node:crypto";
21
22
  import { existsSync, readFileSync } from "node:fs";
22
23
  import { join } from "node:path";
23
24
  // Leaf modules on purpose: this file is imported from reasoning-effort.ts, which combos/types.ts
@@ -26,6 +27,7 @@ import { join } from "node:path";
26
27
  import { atomicWriteFile } from "../config/atomic-write";
27
28
  import { getConfigDir } from "../config/paths";
28
29
  import type { OcxProviderConfig } from "../types";
30
+ import { resolveProviderApiKey } from "./api-key-resolve";
29
31
 
30
32
  const FILENAME = "reasoning-metadata-cache.json";
31
33
  const SUPPORT_FILENAME = "reasoning-support-cache.json";
@@ -87,7 +89,7 @@ interface MetadataSnapshot {
87
89
  }
88
90
 
89
91
  interface SupportSnapshot {
90
- version: 1;
92
+ version: 2;
91
93
  rows: Record<string, { effort: string; at: number; evidence?: string }>;
92
94
  }
93
95
 
@@ -159,11 +161,31 @@ function modelLadderValue(
159
161
  return undefined;
160
162
  }
161
163
 
162
- /** Opaque row key: providerKey|modelId|effort. None of the three may contain a pipe. */
163
- const KEY_SEP = "|";
164
+ /**
165
+ * Bind learned capability to the credential that supplied the evidence. A digest keeps the
166
+ * credential itself out of the persisted cache while remaining stable across restarts and key
167
+ * selection. Providers without key-auth identity may use metadata, but cannot teach the cache.
168
+ *
169
+ * The hash input is the resolved wire credential, not the configured expression: the catalog
170
+ * path carries the raw config string in apiKey (a keychain:/env reference stays unresolved
171
+ * there), while the request path carries the resolved secret in apiKey and the configured
172
+ * expression in _apiKeyAttempt.reference. The request path hashes apiKey exactly as routed --
173
+ * the reference is only provenance, and re-resolving it at record time could read a credential
174
+ * rotated since the request was served. The catalog path resolves the configured expression,
175
+ * so both sides still bind learned refusals to the same wire credential, and a rotation behind
176
+ * a stable reference starts clean instead of inheriting the previous credential's refusals.
177
+ */
178
+ function credentialIdentity(provider: OcxProviderConfig): string | undefined {
179
+ const resolved = provider._apiKeyAttempt?.reference !== undefined
180
+ ? provider.apiKey
181
+ : resolveProviderApiKey(provider.apiKey);
182
+ if (typeof resolved !== "string" || resolved.length === 0) return undefined;
183
+ return createHash("sha256").update(resolved).digest("hex");
184
+ }
164
185
 
165
- function supportKey(providerKey: string, modelId: string, effort: string): string {
166
- return providerKey + KEY_SEP + modelId + KEY_SEP + effort;
186
+ /** JSON encoding avoids delimiter ambiguity in provider, model, and effort identifiers. */
187
+ function supportKey(providerKey: string, credential: string, modelId: string, effort: string): string {
188
+ return JSON.stringify([providerKey, credential, modelId, effort]);
167
189
  }
168
190
 
169
191
  function loadSnapshot(): MetadataSnapshot | null {
@@ -220,7 +242,9 @@ function loadSupport(): Map<string, number> {
220
242
  }
221
243
  const rows = new Map<string, number>();
222
244
  const parsed = readJsonFile<SupportSnapshot>(SUPPORT_FILENAME);
223
- if (parsed && parsed.version === 1 && parsed.rows && typeof parsed.rows === "object") {
245
+ // Version 1 rows had no credential identity and are deliberately invalidated: accepting them
246
+ // would preserve destination-wide refusals written by a lower-entitlement account.
247
+ if (parsed && parsed.version === 2 && parsed.rows && typeof parsed.rows === "object") {
224
248
  for (const [key, row] of Object.entries(parsed.rows)) {
225
249
  if (!row || typeof row.at !== "number") continue;
226
250
  if (nowMs - row.at > SUPPORT_TTL_MS) continue;
@@ -273,8 +297,9 @@ export function metadataDeclaresType(provider: OcxProviderConfig, modelId: strin
273
297
 
274
298
  export function isReasoningEffortLearnedUnsupported(provider: OcxProviderConfig, modelId: string, effort: string): boolean {
275
299
  const key = metadataProviderKey(provider);
276
- if (!key) return false;
277
- return loadSupport().has(supportKey(key, modelId, effort));
300
+ const credential = credentialIdentity(provider);
301
+ if (!key || !credential) return false;
302
+ return loadSupport().has(supportKey(key, credential, modelId, effort));
278
303
  }
279
304
 
280
305
  /**
@@ -291,10 +316,11 @@ export function dropLearnedUnsupportedReasoningEfforts(
291
316
  ): string[] {
292
317
  if (efforts.length === 0) return [...efforts];
293
318
  const key = metadataProviderKey(provider);
294
- if (!key) return [...efforts];
319
+ const credential = credentialIdentity(provider);
320
+ if (!key || !credential) return [...efforts];
295
321
  const support = loadSupport();
296
322
  if (support.size === 0) return [...efforts];
297
- const kept = efforts.filter(effort => !support.has(supportKey(key, modelId, effort)));
323
+ const kept = efforts.filter(effort => !support.has(supportKey(key, credential, modelId, effort)));
298
324
  return kept.length === 0 ? [...efforts] : kept;
299
325
  }
300
326
 
@@ -334,8 +360,9 @@ export function recordUnsupportedReasoningEffort(
334
360
  evidence?: string,
335
361
  ): boolean {
336
362
  const key = metadataProviderKey(provider);
337
- if (!key || !effort) return false;
338
- const rowKey = supportKey(key, modelId, effort);
363
+ const credential = credentialIdentity(provider);
364
+ if (!key || !credential || !effort) return false;
365
+ const rowKey = supportKey(key, credential, modelId, effort);
339
366
  const rows = loadSupport();
340
367
  if (rows.has(rowKey)) return false;
341
368
  rows.set(rowKey, Date.now());
@@ -346,15 +373,14 @@ export function recordUnsupportedReasoningEffort(
346
373
  try {
347
374
  const out: SupportSnapshot["rows"] = {};
348
375
  for (const [rowKey, at] of rows) {
349
- const parts = rowKey.split(KEY_SEP);
350
376
  const evidenceText = supportEvidence.get(rowKey);
351
377
  out[rowKey] = {
352
- effort: parts[2] ?? "",
378
+ effort: JSON.parse(rowKey)[3] ?? "",
353
379
  at,
354
380
  ...(evidenceText ? { evidence: evidenceText } : {}),
355
381
  };
356
382
  }
357
- atomicWriteFile(join(getConfigDir(), SUPPORT_FILENAME), JSON.stringify({ version: 1, rows: out }) + "\n");
383
+ atomicWriteFile(join(getConfigDir(), SUPPORT_FILENAME), JSON.stringify({ version: 2, rows: out }) + "\n");
358
384
  } catch {
359
385
  // Best-effort persistence only.
360
386
  }
@@ -371,11 +397,10 @@ export function flushReasoningSupportCache(): void {
371
397
  const rows = loadSupport();
372
398
  const out: SupportSnapshot["rows"] = {};
373
399
  for (const [rowKey, at] of rows) {
374
- const parts = rowKey.split(KEY_SEP);
375
400
  const evidenceText = supportEvidence.get(rowKey);
376
- out[rowKey] = { effort: parts[2] ?? "", at, ...(evidenceText ? { evidence: evidenceText } : {}) };
401
+ out[rowKey] = { effort: JSON.parse(rowKey)[3] ?? "", at, ...(evidenceText ? { evidence: evidenceText } : {}) };
377
402
  }
378
- atomicWriteFile(join(getConfigDir(), SUPPORT_FILENAME), JSON.stringify({ version: 1, rows: out }) + "\n");
403
+ atomicWriteFile(join(getConfigDir(), SUPPORT_FILENAME), JSON.stringify({ version: 2, rows: out }) + "\n");
379
404
  } catch {
380
405
  // Best-effort persistence only.
381
406
  }
@@ -30,6 +30,8 @@ import {
30
30
  OPENAI_API_GPT56_REASONING_EFFORTS,
31
31
  META_MUSE_REASONING_EFFORTS,
32
32
  META_MUSE_REASONING_EFFORT_MAP,
33
+ META_MUSE_CODE_REASONING_EFFORTS,
34
+ META_MUSE_CODE_REASONING_EFFORT_MAP,
33
35
  META_MUSE_CONTEXT_WINDOW,
34
36
  META_MUSE_MODELS,
35
37
  OPENAI_DAYBREAK_MODELS,
@@ -53,7 +55,7 @@ import {
53
55
  deepseekThinkingEffortsFor,
54
56
  deepseekReasoningMapFor,
55
57
  KIMI_K3_STANDARD_CONTEXT_WINDOW,
56
- KIMI_CODING_MODELS,
58
+ KIMI_CODING_LIVE_MODELS,
57
59
  KIMI_THINKING_MODELS,
58
60
  KIMI_CODING_NO_REASONING_MODELS,
59
61
  KIMI_CODING_K3_REASONING_EFFORTS,
@@ -442,8 +444,14 @@ export const PROVIDER_REGISTRY_CORE: readonly ProviderRegistryEntry[] = [
442
444
  oauthId: "kimi",
443
445
  jawcodeBundle: "moonshot",
444
446
  note: "Log in with your Kimi account",
445
- models: KIMI_CODING_MODELS,
446
- defaultModel: "kimi-k2.7-code",
447
+ // 260921: the retired k2.x ids stay out of the picker — live /coding/v1/models lists
448
+ // only kimi-for-coding[-highspeed], k3, k3-256k. Saved rows still naming kimi-k2.7-code
449
+ // are repaired by MODEL_RENAMES in model-rename-migration.ts.
450
+ models: KIMI_CODING_LIVE_MODELS,
451
+ // 260921: kimi-k2.7-code was retired from the subscription endpoint (live /models lists
452
+ // only kimi-for-coding[-highspeed], k3, k3-256k). The kimi-for-coding alias is the
453
+ // stable ID and currently routes to K2.8 Preview.
454
+ defaultModel: "kimi-for-coding",
447
455
  modelContextWindows: KIMI_CODING_MODEL_CONTEXT_WINDOWS,
448
456
  modelInputModalities: KIMI_CODING_MODEL_INPUT_MODALITIES,
449
457
  // K3 accepts low/high/max; Codex aliases are normalized by the model-scoped wire map.
@@ -599,12 +607,15 @@ export const PROVIDER_REGISTRY_CORE: readonly ProviderRegistryEntry[] = [
599
607
  label: "Meta Muse Code (CLI credential)",
600
608
  adapter: "openai-responses",
601
609
  baseUrl: "https://api.meta.ai/v1",
602
- // Meta own client sends this on every Muse Code call. We never have, so a future
603
- // server-side requirement would break every Muse request with no local signal.
610
+ // Meta's own client sends these on every Muse Code call. The compatibility marker
611
+ // is transparent while selecting the credential surface that accepts `max`.
604
612
  // Declared here rather than in a transport hook so it also covers model discovery
605
613
  // (src/oauth/index.ts:1176) and still yields to a user-set header
606
614
  // (mergeRegistryStaticHeaders, src/providers/registry.ts:3494).
607
- staticHeaders: { "x-api-version": "1.0.0" },
615
+ staticHeaders: {
616
+ "User-Agent": "muse-build/1.3.0 (opencodex compatibility)",
617
+ "x-api-version": "1.0.0",
618
+ },
608
619
  authKind: "oauth",
609
620
  oauthId: "meta-muse",
610
621
  dashboardUrl: "https://dev.meta.ai",
@@ -615,8 +626,8 @@ export const PROVIDER_REGISTRY_CORE: readonly ProviderRegistryEntry[] = [
615
626
  liveModels: false,
616
627
  modelContextWindows: Object.fromEntries(META_MUSE_MODELS.map(id => [id, META_MUSE_CONTEXT_WINDOW])),
617
628
  modelInputModalities: Object.fromEntries(META_MUSE_MODELS.map(id => [id, ["text", "image"] as ["text", "image"]])),
618
- modelReasoningEfforts: Object.fromEntries(META_MUSE_MODELS.map(id => [id, META_MUSE_REASONING_EFFORTS])),
619
- modelReasoningEffortMap: Object.fromEntries(META_MUSE_MODELS.map(id => [id, META_MUSE_REASONING_EFFORT_MAP])),
629
+ modelReasoningEfforts: Object.fromEntries(META_MUSE_MODELS.map(id => [id, META_MUSE_CODE_REASONING_EFFORTS])),
630
+ modelReasoningEffortMap: Object.fromEntries(META_MUSE_MODELS.map(id => [id, META_MUSE_CODE_REASONING_EFFORT_MAP])),
620
631
  note: "Signs in to Meta with a browser device code on any platform, then mints the Muse Code subscription key. That grant is reimplemented from the one the Muse Code CLI performs and has NOT been exercised against Meta from OpenCodex, so treat the first login as unverified. If the Muse Code CLI is already signed in on macOS, the existing key is imported instead of starting a new grant. A pasted key from https://dev.meta.ai still works as a fallback when a device login cannot complete, and faces the same format check and live validation. A device login authenticates as Meta own Muse Code client, which is a stronger claim than reusing a key the CLI already minted. Meta scopes that credential to the Muse Code CLI, so this is an UNSUPPORTED use: Meta does not authorize subscription coverage outside its own CLI, how these calls settle is not observable from the API, and you should treat every call as billable against your account. The key, imported or pasted, is copied into OpenCodex's auth store. For an account signed in with the device login, OpenCodex refreshes Meta's subscription windows on demand from the same key endpoint the login uses, at most once every five minutes. For an imported or pasted key there is no endpoint to query them on demand, so OpenCodex reads them from streaming responses and shows the last observed value with its age; refreshing one then requires another streaming turn, and translated (non-passthrough) turns report none. Rate limits apply per team, not per key. For a supported path use the meta-model provider with your own key (export it as META_MODEL_API_KEY).",
621
632
  },
622
633
  {
@@ -652,6 +663,11 @@ export const PROVIDER_REGISTRY_CORE: readonly ProviderRegistryEntry[] = [
652
663
  // Zen Go can close a Chat stream after a fully assembled function call without sending
653
664
  // finish_reason or [DONE] (#2260). The adapter still rejects incomplete argument JSON.
654
665
  openaiChatEofTolerance: true,
666
+ // Muse Spark on OpenCode Go can sit silent during prolonged reasoning and close without a protocol terminal.
667
+ modelResponsesTerminalRepair: {
668
+ "muse-spark-1.2-contributor": { graceMs: 5_000 },
669
+ "muse-spark-1.3-contributor": { graceMs: 5_000 },
670
+ },
655
671
  // Go rejects reasoning.encrypted_content with previous_response_id (#3838).
656
672
  // Use explicit replay history and the existing stateless Responses policy.
657
673
  statelessResponses: true,
@@ -696,17 +712,25 @@ export const PROVIDER_REGISTRY_CORE: readonly ProviderRegistryEntry[] = [
696
712
  "glm-5.3-flash": ["text", "image"],
697
713
  // Experimental DeepSeek vision preview — expected to merge into deepseek-v4-flash later.
698
714
  [DEEPSEEK_VISION_PREVIEW_MODEL]: ["text", "image"],
699
- // This route is text-only upstream — it is already listed in this preset's
700
- // noVisionModels, which routes images through the proxy's vision sidecar and
701
- // makes the catalog advertise image input on its behalf. The positive
702
- // text-only declaration is what reaches an EXISTING install: derive.ts fills
703
- // noVisionModels all-or-nothing, so a config persisted before this id joined
704
- // the list keeps a stale list, the sidecar predicate never matches, the row
705
- // carries no modality at all, and any combo containing it collapses to
706
- // ["text"] (#4505). modelInputModalities IS per-key filled, so this
707
- // declaration lands on old configs. It states the route's real upstream
708
- // capability and keeps the sidecar explicitly distinct from native vision.
709
- "deepseek-v4.1-flash": ["text"],
715
+ // This route became natively multimodal; it is NOT a sidecar consumer.
716
+ //
717
+ // History: the id was declared text-only here and listed in this preset's
718
+ // noVisionModels, which routed its images through the vision sidecar (#4505).
719
+ // That classification came from jawcode metadata and went stale. Probed
720
+ // 2026-09-19 against https://opencode.ai/zen/go/v1/chat/completions with the
721
+ // headers this proxy sends: the route accepts an image_url part and the model
722
+ // reads it correctly (a four-band colour chart was described in the right
723
+ // order). Its sibling deepseek-v4-flash on the same gateway still answers
724
+ // HTTP 400 "Model only supports text input", which is what keeps the two
725
+ // distinct here rather than collapsing them.
726
+ //
727
+ // The declaration is what reaches an EXISTING install: derive.ts fills
728
+ // noVisionModels all-or-nothing, so a config persisted while the stale list
729
+ // was current keeps it forever, and modelInputModalities is filled per-key
730
+ // BENEATH the saved value. Both halves are repaired by
731
+ // stale-vision-classification-migration.ts; correcting the registry alone
732
+ // would fix new installs and leave existing ones stripping images.
733
+ "deepseek-v4.1-flash": ["text", "image"],
710
734
  // Muse Spark Contributor is natively multimodal on Zen Go: it accepts input_image
711
735
  // parts over /responses (probed 2026-08-26). Without this declaration the catalog
712
736
  // advertises it text-only and the Codex app blocks image attachments client-side with
@@ -760,7 +784,10 @@ export const PROVIDER_REGISTRY_CORE: readonly ProviderRegistryEntry[] = [
760
784
  // Kimi K2.7 Code accepts text+image+video: do NOT list it here.
761
785
  noVisionModels: [
762
786
  "glm-5.3", "glm-5.2", "glm-5", "glm-5.1",
763
- "deepseek-v4.1-flash", "deepseek-v4-flash",
787
+ // deepseek-v4.1-flash is deliberately absent: probed natively multimodal on this
788
+ // gateway 2026-09-19 (see the modelInputModalities note above). Its sibling
789
+ // deepseek-v4-flash stays listed — that route rejects image_url upstream.
790
+ "deepseek-v4-flash",
764
791
  "mimo-v2-pro", "mimo-v2.5-pro",
765
792
  "minimax-m2.5", "minimax-m2.7",
766
793
  "qwen3.7-max",
@@ -72,10 +72,10 @@ import {
72
72
  VOLCENGINE_PLAN_TEXT_ONLY_MODELS,
73
73
  ALIBABA_INTL_TOKEN_PLAN_INPUT_MODALITIES,
74
74
  KIMI_API_MODELS,
75
- KIMI_CODING_MODELS,
76
75
  KIMI_THINKING_MODELS,
77
76
  KIMI_CODING_NO_REASONING_MODELS,
78
77
  KIMI_API_NO_REASONING_MODELS,
78
+ KIMI_CODING_LIVE_MODELS,
79
79
  KIMI_CODING_REASONING_EFFORTS,
80
80
  KIMI_CODING_DEFAULT_REASONING_EFFORTS,
81
81
  KIMI_CODING_REASONING_EFFORT_MAPS,
@@ -102,6 +102,11 @@ import {
102
102
  OPPER_MODEL_CONTEXT_WINDOWS,
103
103
  OPPER_MODEL_MAX_OUTPUT_TOKENS,
104
104
  OPPER_MODEL_INPUT_MODALITIES,
105
+ STEPFUN_MODELS,
106
+ STEPFUN_MODEL_CONTEXT_WINDOWS,
107
+ STEPFUN_MODEL_INPUT_MODALITIES,
108
+ STEPFUN_NO_VISION_MODELS,
109
+ STEPFUN_REASONING_EFFORTS,
105
110
  } from "./model-seeds";
106
111
 
107
112
  export const PROVIDER_REGISTRY_EXTENDED: readonly ProviderRegistryEntry[] = [
@@ -746,9 +751,22 @@ export const PROVIDER_REGISTRY_EXTENDED: readonly ProviderRegistryEntry[] = [
746
751
  id: "volcengine-coding-plan",
747
752
  label: "Volcengine Ark Coding Plan",
748
753
  baseUrl: "https://ark.cn-beijing.volces.com/api/coding/v3",
749
- adapter: "openai-chat",
754
+ responsesPath: "/responses",
755
+ adapter: "openai-responses",
750
756
  authKind: "key",
757
+ supportsServiceTier: false,
751
758
  preserveCustomDestination: true,
759
+ // A row already saved on Chat keeps Chat. This is a `preserveCustomDestination` key entry,
760
+ // so `providerMatchesRegistryTransport` refuses the adapter mismatch and the request path
761
+ // returns the stored row untouched; the alias below still hands it this entry's metadata.
762
+ // Deliberately no startup config migration: the Z.AI one (`zai-responses-migration.ts`) is
763
+ // safe only because it rewrites rows the router already canonicalizes, and it gates on
764
+ // `providerMatchesRegistryTransport` to guarantee that. A Chat row here is NOT canonicalized,
765
+ // so migrating it would change a wire the operator is actually using, and a marker added
766
+ // now cannot tell the old default apart from a deliberate pre-upgrade Chat choice.
767
+ destinationAliases: [{ baseUrl: "https://ark.cn-beijing.volces.com/api/coding/v3", adapter: "openai-chat" }],
768
+ // Validated Ark Coding Plan continuations reject replayed reasoning items.
769
+ dropResponsesReasoningItems: true,
752
770
  dashboardUrl: "https://console.volcengine.com/ark/region:ark+cn-beijing/overview",
753
771
  defaultModel: "ark-code-latest",
754
772
  models: VOLCENGINE_CODING_PLAN_MODELS,
@@ -798,6 +816,21 @@ export const PROVIDER_REGISTRY_EXTENDED: readonly ProviderRegistryEntry[] = [
798
816
  defaultModel: "qwen3.8-max",
799
817
  models: ALIBABA_TOKEN_PLAN_MODELS,
800
818
  liveModels: false,
819
+ // Alibaba documents an OpenAI-compatible Responses API on this same /compatible-mode/v1 base
820
+ // and ships an official Codex integration guide on wire_api = "responses" (#5097). The
821
+ // gateway serves the same models over both wires, and qwen3.8-flash, qwen3.7-plus and
822
+ // glm-5.3 carry live end-to-end evidence there (custom tools, reasoning replay, streaming,
823
+ // multi-turn continuation).
824
+ //
825
+ // That is deliberately NOT expressed as a modelWireDefaults pin. Pinning would move every
826
+ // existing Codex user of those models onto a different upstream with no config change, and
827
+ // one delta is unresolved: preserveReasoningContentModels below is read by the CHAT adapter,
828
+ // while the Responses serializer reads preserveResponsesReasoningContent, which this entry
829
+ // does not set. On the Responses wire those models would replay with blanked reasoning
830
+ // content -- less state than they carry today. Z.AI and DeepSeek set both flags together for
831
+ // exactly this reason. Until that flag is justified against this gateway, Responses stays a
832
+ // documented per-model modelAdapters opt-in;
833
+ // tests/providers/alibaba-token-plan-responses-optin.test.ts holds both halves.
801
834
  note: "Token Plan Personal Edition · China (Beijing)",
802
835
  modelInputModalities: ALIBABA_TOKEN_PLAN_INPUT_MODALITIES,
803
836
  modelContextWindows: ALIBABA_TOKEN_PLAN_CONTEXT_WINDOWS,
@@ -980,13 +1013,17 @@ export const PROVIDER_REGISTRY_EXTENDED: readonly ProviderRegistryEntry[] = [
980
1013
  },
981
1014
  {
982
1015
  id: "kimi-code", label: "Kimi (coding)", baseUrl: "https://api.kimi.com/coding/v1", adapter: "openai-chat", authKind: "key",
983
- dashboardUrl: "https://platform.moonshot.cn/console/api-keys", defaultModel: "kimi-k2.7-code",
1016
+ // 260921: kimi-k2.7-code was retired from the coding endpoint; the kimi-for-coding alias
1017
+ // is the stable ID and currently routes to K2.8 Preview (same as the OAuth preset).
1018
+ dashboardUrl: "https://platform.moonshot.cn/console/api-keys", defaultModel: "kimi-for-coding",
984
1019
  modelSuffixBracketStrip: true,
985
1020
  // API-key form of the same Kimi Code Plan transport; keep cache affinity identical to OAuth.
986
1021
  promptCacheKey: true,
987
1022
  // Keep Responses tool-result adjacency aligned with the OAuth preset (#4726).
988
1023
  requiresAdjacentResponsesToolResults: true,
989
- models: KIMI_CODING_MODELS,
1024
+ // 260921: same live-id picker as the OAuth preset — the retired k2.x ids are repaired
1025
+ // in saved configs by MODEL_RENAMES, not offered on fresh installs.
1026
+ models: KIMI_CODING_LIVE_MODELS,
990
1027
  modelContextWindows: KIMI_CODING_MODEL_CONTEXT_WINDOWS,
991
1028
  modelInputModalities: KIMI_CODING_MODEL_INPUT_MODALITIES,
992
1029
  noReasoningModels: KIMI_CODING_NO_REASONING_MODELS,
@@ -1027,6 +1064,11 @@ export const PROVIDER_REGISTRY_EXTENDED: readonly ProviderRegistryEntry[] = [
1027
1064
  // Same DeepSeek routes as the Go preset above, behind the same vendor, so they carry
1028
1065
  // the same json_schema rejection (#1338 / #1415).
1029
1066
  noJsonSchemaModels: [...DEEPSEEK_GATEWAY_THINKING_MODELS, ...OPENCODE_FREE_DEEPSEEK_MODELS],
1067
+ // Muse Spark on Zen can sit silent during prolonged reasoning and close without a protocol terminal.
1068
+ modelResponsesTerminalRepair: {
1069
+ "muse-spark-1.2-contributor-free": { graceMs: 5_000 },
1070
+ "muse-spark-1.3-contributor-free": { graceMs: 5_000 },
1071
+ },
1030
1072
  },
1031
1073
  { 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" },
1032
1074
  {
@@ -1328,4 +1370,21 @@ export const PROVIDER_REGISTRY_EXTENDED: readonly ProviderRegistryEntry[] = [
1328
1370
  noVisionModels: CODEBUDDY_CN_NO_VISION_MODELS,
1329
1371
  note: "Official CodeBuddy Code CLI (Tencent Cloud), China/internal environment. Uses the documented CODEBUDDY_API_KEY + headless CLI surface; never reads desktop sessions or private console endpoints. Region-isolated from codebuddy (Global); credentials are never exchanged across regions. v1 disables CLI tools (--tools \"\"): text/reasoning only for now. Requires `npm i -g @tencent-ai/codebuddy-code`. AUP/routing authorization flagged for maintainer security review.",
1330
1372
  },
1373
+ {
1374
+ id: "stepfun",
1375
+ label: "StepFun",
1376
+ baseUrl: "https://api.stepfun.com/v1",
1377
+ adapter: "openai-chat",
1378
+ authKind: "key",
1379
+ dashboardUrl: "https://platform.stepfun.com",
1380
+ defaultModel: "step-5-preview",
1381
+ models: STEPFUN_MODELS,
1382
+ liveModels: true,
1383
+ preserveCustomDestination: true,
1384
+ modelContextWindows: STEPFUN_MODEL_CONTEXT_WINDOWS,
1385
+ modelInputModalities: STEPFUN_MODEL_INPUT_MODALITIES,
1386
+ noVisionModels: STEPFUN_NO_VISION_MODELS,
1387
+ reasoningEfforts: STEPFUN_REASONING_EFFORTS,
1388
+ note: "StepFun (阶跃星辰) official OpenAI-compatible API.",
1389
+ },
1331
1390
  ];
@@ -0,0 +1,168 @@
1
+ import type { ProviderRegistryEntry } from "./types";
2
+
3
+ /**
4
+ * What a registry field's KEYS mean for selector decoding.
5
+ *
6
+ * An encoded selector is decoded against the native model ids a provider is known to accept.
7
+ * That set used to be a hand-written list of eight registry maps, so an id declared only in an
8
+ * unlisted map could not be decoded, and every new model-keyed field had to be remembered.
9
+ *
10
+ * Every field of ProviderRegistryEntry is classified below, including the ones that carry no
11
+ * model identity at all. That is deliberate: the "satisfies" check makes a new registry field a
12
+ * TYPE error until somebody decides what its keys mean, rather than letting it default to
13
+ * invisible. The two checks are different and both matter - the satisfies clause proves the
14
+ * classification is exhaustive when this compiles, and the parity test proves the fields the
15
+ * shipped registry actually carries are the ones classified here.
16
+ *
17
+ * These are decode hints and nothing more. A hint does not publish a catalog row, grant
18
+ * availability or entitlement, and it does not confer transport authority: the caller still
19
+ * checks registry transport identity before asking for them, and an ambiguous selector is still
20
+ * rejected rather than guessed.
21
+ */
22
+ type RegistryFieldModelIdRole =
23
+ | { readonly kind: "none" }
24
+ | { readonly kind: "record-keys" }
25
+ | { readonly kind: "nested-record-keys"; readonly key: "modelSupportsServiceTier" };
26
+
27
+ const NONE = { kind: "none" } as const;
28
+ const RECORD_KEYS = { kind: "record-keys" } as const;
29
+ /**
30
+ * Key-auth declares its own service-tier support under a nested map.
31
+ *
32
+ * Reading its keys is identity evidence only. It does not activate the key-auth service tier,
33
+ * which stays the business of the service-tier resolver.
34
+ */
35
+ const KEY_AUTH_SERVICE_TIER = { kind: "nested-record-keys", key: "modelSupportsServiceTier" } as const;
36
+
37
+ /**
38
+ * Not a configuration surface. Exported so the parity suite can compare this classification
39
+ * against the fields the shipped registry entries actually carry at runtime.
40
+ */
41
+ export const REGISTRY_FIELD_MODEL_ID_ROLES = {
42
+ id: NONE,
43
+ label: NONE,
44
+ adapter: NONE,
45
+ baseUrl: NONE,
46
+ apiKeyTransport: NONE,
47
+ alias: NONE,
48
+ authKind: NONE,
49
+ codexAccountMode: NONE,
50
+ allowKeyAuthOverride: NONE,
51
+ allowPrivateNetworkByDefault: NONE,
52
+ keyOptional: NONE,
53
+ apiKeyValidation: NONE,
54
+ freeTier: NONE,
55
+ allowBaseUrlOverride: NONE,
56
+ preserveCustomDestination: NONE,
57
+ baseUrlChoices: NONE,
58
+ staticHeaders: NONE,
59
+ modelSuffixBracketStrip: NONE,
60
+ featured: NONE,
61
+ sponsor: NONE,
62
+ dashboardPreset: NONE,
63
+ note: NONE,
64
+ dashboardUrl: NONE,
65
+ defaultModel: NONE,
66
+ models: NONE,
67
+ liveModels: NONE,
68
+ modelWireDefaults: RECORD_KEYS,
69
+ fastWire: NONE,
70
+ modelResponsesUpstreamStreaming: RECORD_KEYS,
71
+ modelResponsesTerminalRepair: RECORD_KEYS,
72
+ responsesItemIdRepair: NONE,
73
+ responsesPath: NONE,
74
+ chatCompletionsPath: NONE,
75
+ destinationAliases: NONE,
76
+ statelessResponses: NONE,
77
+ requiresAdjacentResponsesToolResults: NONE,
78
+ requiresPairedResponsesToolResults: NONE,
79
+ annotateEmptyToolOutputs: NONE,
80
+ supportsServiceTier: NONE,
81
+ supportsOpenAiWebSearchToolFields: NONE,
82
+ supportsResponsesCustomTools: NONE,
83
+ modelSupportsServiceTier: RECORD_KEYS,
84
+ keyAuthServiceTier: KEY_AUTH_SERVICE_TIER,
85
+ fastTierDescription: NONE,
86
+ modelServiceTierCapabilityBaseUrlGuard: NONE,
87
+ preserveResponsesReasoningContent: NONE,
88
+ dropResponsesReasoningItems: NONE,
89
+ modelSupportsReasoningSummaries: RECORD_KEYS,
90
+ modelSupportsVerbosity: RECORD_KEYS,
91
+ supportsVerbosity: NONE,
92
+ modelDiscovery: NONE,
93
+ contextWindow: NONE,
94
+ modelContextWindows: RECORD_KEYS,
95
+ modelDisplayNames: RECORD_KEYS,
96
+ modelInputModalities: RECORD_KEYS,
97
+ defaultMaxOutputTokens: NONE,
98
+ modelMaxOutputTokens: RECORD_KEYS,
99
+ reasoningEfforts: NONE,
100
+ modelReasoningEfforts: RECORD_KEYS,
101
+ modelDefaultReasoningEfforts: RECORD_KEYS,
102
+ reasoningEffortMap: NONE,
103
+ modelReasoningEffortMap: RECORD_KEYS,
104
+ directReasoningEffortModels: NONE,
105
+ reasoningWireFormat: NONE,
106
+ noVisionModels: NONE,
107
+ noReasoningModels: NONE,
108
+ noTemperatureModels: NONE,
109
+ noTopPModels: NONE,
110
+ noPenaltyModels: NONE,
111
+ noJsonSchemaModels: NONE,
112
+ parallelToolCalls: NONE,
113
+ promptCacheKey: NONE,
114
+ chatServiceTier: NONE,
115
+ openaiChatEofTolerance: NONE,
116
+ autoToolChoiceOnlyModels: NONE,
117
+ preserveReasoningContentModels: NONE,
118
+ requiresReasoningPlaceholderModels: NONE,
119
+ showThinkingSummary: NONE,
120
+ reasoningSplitModels: NONE,
121
+ reasoningDetailsModels: NONE,
122
+ thinkingToggleModels: NONE,
123
+ thinkingBudgetModels: NONE,
124
+ escapeBuiltinToolNames: NONE,
125
+ oauthId: NONE,
126
+ virtualModels: RECORD_KEYS,
127
+ modelMaxInputTokens: RECORD_KEYS,
128
+ jawcodeBundle: NONE,
129
+ extraMetadataAliases: NONE,
130
+ metadataModelIdNormalize: NONE,
131
+ googleMode: NONE,
132
+ project: NONE,
133
+ location: NONE,
134
+ } satisfies Record<keyof ProviderRegistryEntry, RegistryFieldModelIdRole>;
135
+
136
+ /**
137
+ * The native model ids this registry entry names, in registry-field order, first seen wins.
138
+ *
139
+ * Only KEYS are collected. virtualModels is a map whose keys are the selected identities and
140
+ * whose values name a transport target, so its wireModelId values are deliberately not read:
141
+ * a wire target is not something a client may select by name.
142
+ */
143
+ export function registryModelIdKeys(entry: ProviderRegistryEntry): readonly string[] {
144
+ const ids: string[] = [];
145
+ const seen = new Set<string>();
146
+ const add = (key: string): void => {
147
+ if (key.length === 0 || seen.has(key)) return;
148
+ seen.add(key);
149
+ ids.push(key);
150
+ };
151
+ for (const field of Object.keys(REGISTRY_FIELD_MODEL_ID_ROLES) as (keyof ProviderRegistryEntry)[]) {
152
+ const role = REGISTRY_FIELD_MODEL_ID_ROLES[field];
153
+ if (role.kind === "none") continue;
154
+ if (role.kind === "record-keys") {
155
+ const map = entry[field];
156
+ if (typeof map !== "object" || map === null) continue;
157
+ for (const key of Object.keys(map)) add(key);
158
+ continue;
159
+ }
160
+ // The nested role belongs to keyAuthServiceTier alone, so its owner is read by name.
161
+ // ProviderRegistryEntry is an interface and carries no implicit index signature, so a
162
+ // by-name read is what keeps this loop typed instead of asserted through unknown.
163
+ const nested = entry.keyAuthServiceTier?.[role.key];
164
+ if (typeof nested !== "object" || nested === null) continue;
165
+ for (const key of Object.keys(nested)) add(key);
166
+ }
167
+ return Object.freeze(ids);
168
+ }